Bridge API
Endpoint reference, request gating, environment variables, audit events
Cockpit Bridge API 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` |
| Bearer token (`Authorization: Bearer …` or `?token=`), constant-time compare | 401 `unauthorized` |
| CSRF header `x-cockpit-csrf` = token, for mutating verbs with an Origin | 403 `csrf_required` |
| Real-executor assertion (mock refused unless explicitly allowed) | 502 `mock_executor_refused` |
`GET /healthz` is the one unauthenticated route (shell liveness probe). Unhandled upstream failures return 502 `bridge_upstream_error`.
Endpoints
Health
| Method & path | Purpose |
|---|---|
| `GET /healthz` | Unauthenticated liveness (`{status:'ok'}`) |
| `GET /api/health` | Bridge health + config echo (`executor_url`, `mock_executor_allowed`) |
| `GET /api/executor/capabilities` | Deep-probe the executor (`host_runtime_enabled`, raw status; `unreachable` on failure) |
Events & telemetry
| Method & path | Purpose |
|---|---|
| `GET /api/events` | Server-Sent Events stream — `cockpit.refresh` on connect, heartbeat every 5s. (The Bridge serves no WebSocket; pty streams go browser→executor.) |
| `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 `aiwg mc` disk state with the live executor task session |
Inventory & instances
| Method & path | Purpose |
|---|---|
| `GET /api/inventory` | Normalized instance inventory (runtime/transport/daemon 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. `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/reconnect` | Stale-agent recovery — full semantics in Recovery |
| `DELETE /api/instances/:id` | Destroy, with Docker-CLI reconciliation and already-gone synthesis (Recovery) |
| `POST /api/tasks/:instanceId/:taskId/cancel` | Cancel an A2A task |
Sessions
| Method & path | Purpose |
|---|---|
| `GET /api/sessions?instance=` | List sessions; each row carries a normalized `attach_url` for the direct browser→executor pty WebSocket |
| `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=` | `aiwg discover` (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 (`aiwg index`) |
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 with the per-launch token injected (`window.__COCKPIT_TOKEN__`) and a `SameSite=Strict` CSRF cookie; other paths serve hashed, immutable-cached assets from the web build, path-sandboxed. A legacy fallback page renders when no web build is present.
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 |
| `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.reconnect.requested` · `instance.destroy.requested` · `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.