Bridge API
Endpoint reference, request gating, environment variables, audit events
Cockpit Bridge API Reference
Prompt-first procedure: Describe the outcome you want in your agent conversation. The agent should select and load the appropriate AIWG assets, explain material changes, request any needed approval, and report verification evidence. Exact commands and flags appear only in the CLI reference.
The Bridge is a single loopback HTTP server (default `127.0.0.1:8140`). This page documents its surface as implemented. All `/api/*` routes pass four gates before dispatch; see Trust & Security for the model.
| Gate | Failure |
|---|---|
| Loopback, same-port browser origin | 403 `forbidden_origin` |
| Explicit bearer or HttpOnly browser session; URL query credentials are rejected | 401 `unauthorized` |
| Session-bound CSRF header `x-cockpit-csrf` for mutating browser verbs | 403 `csrf_required` |
| Real-executor assertion (mock refused unless explicitly allowed) | 502 `mock_executor_refused` |
| Optional sandbox mTLS readiness gate (`AIWG_COCKPIT_REQUIRE_SANDBOX_MTLS=1`) | 503 `executor_trust_required` |
`GET /healthz` is the one unauthenticated route (shell liveness probe). Unhandled upstream failures return 502 `bridge_upstream_error`. Protected executor failures preserve 401 `executor_unauthenticated` and 403 `executor_forbidden` instead of degrading to an empty inventory.
Endpoints
Browser bootstrap
| Method & path | Purpose |
|---|---|
| `POST /bootstrap/nonce` | Native bearer-authenticated issuance of a 60-second, one-time nonce for `browser`, `tauri`, or `vscode` |
| `POST /bootstrap/session` | Exchanges the nonce from the URL fragment for an HttpOnly `SameSite=Strict` session and in-memory CSRF binding |
| `GET /bootstrap/session` | Restores the CSRF binding for an existing valid session; does not expose the session identifier |
Health
| Method & path | Purpose |
|---|---|
| `GET /healthz` | Unauthenticated liveness (`{status:'ok'}`) |
| `GET /api/health` | Bridge health + config echo (`executor_url`, auth posture, and A2A protocol/fallback policy) |
| `GET /api/executor/capabilities` | Deep-probe the executor (`host_runtime_enabled`, raw status; `unreachable` on failure) |
| `GET /api/bootstrap/readiness` | Client-safe sandbox CA/bootstrap readiness posture; no PEMs, keys, CSRs, bearer values, or raw credential paths |
Events & telemetry
| Method & path | Purpose |
|---|---|
| `GET /api/events` | Server-Sent Events stream — `cockpit.refresh` on connect, heartbeat every 5s. |
| `GET /api/events/snapshot` | Unified event model v1 — typed events aggregated from inventory, running, approvals, missions, sessions |
| `GET /api/running` | Running board, derived from per-instance A2A task lists (not an executor admin route) |
| `GET /api/missions` | Mission Control projection — merges durable the agent-owned mc operation disk state with the live executor task session |
Inventory & instances
| Method & path | Purpose |
|---|---|
| `GET /api/inventory` | Normalized instance inventory (runtime/transport/daemon and selected A2A protocol posture per instance); agent-registry fallback; degraded envelope instead of hard failure |
| `GET /api/loadouts` | Loadout catalog passthrough |
| `POST /api/instances` | Launch a runtime target via the executor's v2 admin API. VM requests may include `provider` and `runtime_options` (`kind`, `provider`, `required_capabilities`, `excluded_capabilities`, `launch_strategy`, and `constraints`). `runtime:'qemu'` requires a resolvable SSH public key (400 `ssh_public_key_required` / `ssh_public_key_not_found`) |
| `GET /api/operations/:id` | Poll an async provisioning operation |
| `POST /api/instances/:id/start` · `/stop` | Lifecycle |
| `POST /api/instances/:id/snapshot` · `/checkpoint` | Create a Cloud Hypervisor snapshot or libvirt checkpoint when the instance advertises the matching capability; returns/polls the sandbox operation |
| `POST /api/instances/:id/restore` · `/fork` · `/warm-pool` | Launch from an opaque `asset_ref` through sandbox `runtime_options`; optional `name` names the child runtime, and Cloud Hypervisor accepts `restore_mode` (`ondemand` or `copy`) |
| `POST /api/instances/:id/reconnect` | Stale-agent recovery — full semantics in Recovery |
| `DELETE /api/instances/:id` | Destroy, with executor-owned lifecycle first and local Docker reconciliation only when explicitly enabled (Recovery) |
| `POST /api/activity/coverage` | Read-only proxy to sandbox `GET /api/v2/activity/coverage`; exact scope is supplied in the JSON body and converted to sandbox scope headers |
| `POST /api/activity/timeline` | Read-only metadata timeline proxy; validates `activity.event/v1`, scope correlation, coverage, and restricted-field absence |
| `POST /api/activity/export` | Explicit signed-export action; returns an attachment and never places the export in normal dashboard state |
| `POST /api/tasks/:instanceId/:taskId/cancel` | Cancel an A2A task |
Fast-start actions are provider and capability gated. Cockpit renders Snapshot for Cloud Hypervisor, Checkpoint for libvirt, and Restore/Fork/Warm pool only when the executor reports the required instance capability. VFIO/GPU constraints can intentionally exclude fast-start capabilities and force cold launch with `fallback_mode:'fail'`.
Sandbox MCP
| Method & path | Purpose |
|---|---|
| `GET /api/mcp/discovery` | Client-safe sandbox MCP discovery: protocol/transport posture, tools, resources/templates, scopes, principals, and capability metadata |
| `POST /api/mcp` | Authenticated Bridge proxy to the sandbox MCP endpoint. Requires `AIWG_COCKPIT_MCP_TOKEN_FILE`; forwards `MCP-Protocol-Version`; redacts request/response metadata in `sandbox.mcp.proxy` audit events |
Sessions
| Method & path | Purpose |
|---|---|
| `GET /api/sessions?instance=` | List sessions; each row carries a Bridge-owned `attach_url`. The Bridge authenticates the upstream PTY upgrade without exposing the executor bearer to the browser. |
| `POST /api/instances/:id/sessions` | Create a session (`mode`, `backend`, `loadout` query params). Recovers an in-flight create by name on timeout; 409 `agent_not_registered` when no agent |
| `DELETE /api/instances/:id/sessions/:sessionId` | End a session |
| `GET /api/instances/:id/sessions/:sessionId/screen` | Screen snapshot (404 `session_screen_unavailable` when the backend has no screen) |
Approvals
| Method & path | Purpose | |
|---|---|---|
| `GET /api/approvals?status=` | HITL inbox derived from A2A `input-required` / `hitl-prompt/v1` tasks (default `pending`) | |
| `POST /api/approvals/:id?decision=approve\ | deny` | Decide; `:id` is `instanceId::taskId`; posted via the A2A respond surface |
Cost
| `GET /api/cost` | Spend passthrough (executor `/admin/cost`) |
|---|
Catalog, index, contributions
These shell out to the AIWG CLI (read-only):
| Method & path | Purpose | |||||
|---|---|---|---|---|---|---|
| `GET /api/capabilities?q=&limit=&type=` | the agent’s capability search (limit 1–50; type `all\ | skill\ | agent\ | command\ | rule\ | flow`) |
| `GET /api/show?path=` or `?type=&name=` | Artifact body; path access is corpus-sandboxed (400 `path_outside_corpus`), name resolution can 409 `ambiguous_artifact` | |||||
| `GET /api/contributions` | Validated declarative UI contribution manifests | |||||
| `GET /api/index/status` · `GET /api/index/query?...` · `POST /api/index/rebuild` | Artifact-index operations (the agent-owned index operation) |
Library
| Method & path | Purpose |
|---|---|
| `GET /api/library` | List operator-owned assets under `~/.aiwg/cockpit/library` |
| `POST /api/library/clone?type=&name=&path=` | Clone a catalog asset into the library (201; refuses existing destinations) |
| `DELETE /api/library/:name` | Remove (path-sandboxed; 404 `not_in_library`) |
The library invariant: catalog reads are read-only and AIWG install files are never written — the library holds copies the operator owns.
Audit
| Method & path | Purpose |
|---|---|
| `GET /api/audit?limit=` | Tail of the redacted audit log (1–200, default 50) |
| `POST /api/audit/intent` | Record an operator intent event (the web UI logs `action.inject.requested` through this) |
Static
`/` serves the built web app without credential injection and with `no-store`, `no-referrer`, and loopback-origin CSP headers. The app exchanges a one-time URL-fragment nonce for an HttpOnly session and removes the fragment before API traffic. Other paths serve hashed, immutable-cached assets from the web build, path-sandboxed. A legacy fallback page uses the same bootstrap contract.
Environment variables
The Bridge is configured entirely by environment (no CLI flags):
| Variable | Default | Effect |
|---|---|---|
| `AIWG_COCKPIT_EXECUTOR_URL` (alias `EXECUTOR_URL`) | `http://127.0.0.1:8122` | Upstream executor |
| `AIWG_COCKPIT_EXECUTOR_TOKEN_FILE` | — | Mode-600 file containing one executor bearer token. The Bridge reloads it per upstream request and keeps it out of browser state, URLs, logs, and audit records. |
| `AIWG_COCKPIT_MCP_TOKEN_FILE` | — | Mode-600 file containing the separate MCP principal bearer used only by `POST /api/mcp`; discovery remains read-only without it. |
| `AIWG_COCKPIT_REQUIRE_SANDBOX_MTLS` | off | `1`: all authenticated `/api/*` routes fail with 503 `executor_trust_required` until sandbox CA/bootstrap readiness is secure and complete. |
| `AIWG_COCKPIT_LOCAL_DOCKER_FALLBACK` | off | `1`: allow local-development `docker exec agent-reconnect` and `docker rm -f` fallback when executor-owned lifecycle does not handle a Docker/container row. |
| `AIWG_COCKPIT_LOCAL_LIBVIRT_FALLBACK` | platform policy | `1`: allow local `virsh qemu-agent-command` reconnect fallback on non-Linux hosts; Linux permits the fallback automatically. |
| `PORT` / `AIWG_COCKPIT_BRIDGE_PORT` | `8140` | Listen port; refuses the executor-reserved 8120–8122 |
| `AIWG_COCKPIT_AUTOSTART_EXECUTOR` | on | `0` disables best-effort executor autostart |
| `AIWG_COCKPIT_EXECUTOR_COMMAND` | — | Pin the autostart command (otherwise an installed `agentic-mgmt` is tried) |
| `AIWG_COCKPIT_ALLOW_MOCK_EXECUTOR` | off | `1` permits the automated-test mock (Development) |
| `AIWG_COCKPIT_AUDIT_DIR` | `~/.aiwg/cockpit/audit` | Audit log location |
| `AIWG_COCKPIT_CONTRIB` | — | Additional contribution-manifest directory, layered after first-party |
| `AIWG_COCKPIT_AGENT_CACHE_TTL_MS` | `5000` | Agent-list cache TTL |
| `AIWG_ROOT` | — | Extra corpus root for `/api/show` path resolution |
| `AIWG_COCKPIT_KEYCHAIN_STRICT` | off | `1`: never persist a plaintext token when a keychain ref exists; fail launch if the keychain fails |
| `AIWG_COCKPIT_REQUIRE_KEYCHAIN` | off | `1`: fail launch if the keychain store fails |
| `AIWG_COCKPIT_KEYCHAIN_DISABLED` | off | `1`: skip OS keychain entirely |
| `AIWG_COCKPIT_ENABLE_KWALLET` / `AIWG_COCKPIT_KWALLET` | off / `kdewallet` | Opt-in KWallet backend and wallet name |
Dev-launcher variables (`AIWG_COCKPIT_ENSURE_EXECUTOR`, `AIWG_COCKPIT_START_HOST_DAEMON`, e2e/UAT knobs) are covered in Development.
Audit events
Written as redacted JSONL to `~/.aiwg/cockpit/audit/events.jsonl` (file 600, dir 700). Secret-looking keys and values (tokens, API keys, bearer strings) are redacted before write. Events:
`instance.launch.requested` / `instance.launch.result` · `instance.lifecycle.requested` (start/stop) · `instance.fast_start.requested` · `instance.reconnect.requested` · `instance.destroy.requested` · `sandbox.mcp.proxy` · `task.cancel.requested` · `session.start.requested` · `approval.response.submitted` · `index.rebuild.requested` / `index.rebuild.completed` · operator intents via `/api/audit/intent` (the web UI records `action.inject.requested` for every action injection).
Executor compatibility
Upstream calls use candidate-list fallbacks to tolerate executor version skew (legacy `/admin/` and v2 `/api/v2/admin/` surfaces both probed; see Architecture). Snake_case and camelCase payloads are both normalized; unknown fields render as opaque posture rather than errors.
When `AIWG_COCKPIT_EXECUTOR_TOKEN_FILE` is set, every REST/A2A request uses the same centralized authenticated fetch path. PTY `attach_url` values are replaced with an opaque Bridge route. The browser proves possession of the per-launch Cockpit token through a private WebSocket subprotocol; the Bridge strips that subprotocol, adds the executor `Authorization` header to the upstream upgrade, and forwards only the public `pty-ws.v1` protocol. Group/world-accessible token files fail closed. Replacing the file rotates the upstream identity without a Bridge restart.
Governed activity requests
All three activity routes require `tenant_id`, `host_id`, `instance_id`, and `agent_id` as non-empty JSON strings. The Bridge maps only those values to the four `x-agentic-*` scope headers; it never derives scope from inventory or forwards browser-supplied sandbox headers. `filter` is allow-listed to the sandbox activity query contract. Missing/invalid scope is `400 activity_scope_required`; cross-scope events, malformed envelopes, and restricted-content fields fail closed. Sandbox 401/403 responses remain stable executor authentication/authorization errors. The executor bearer stays in the Bridge credential-file domain.
Coverage must be fetched and displayed before timeline rows. Signed export is a separate operator action. Only its key ID and Merkle root are displayed after a successful download; a missing sandbox signing key is `503 activity_export_unavailable`.