Architecture
Shells, Bridge, executor — control vs data plane, ports, components
Cockpit Architecture
Three tiers: shells (browser, VS Code webview, Tauri desktop) in front of a Bridge (local token-gated control plane) in front of the agentic-sandbox executor (the runtime substrate).
operator / CLI: aiwg cockpit
│ spawns the Bridge; writes OS-keychain token +
│ ~/.aiwg/cockpit/runtime/bridge.json (0600)
▼
┌──────────────────────────────────────────────────────────────┐
│ Bridge (127.0.0.1:8140, token-gated /api) │
│ · control plane: inventory, lifecycle, running, missions, │
│ approvals, cost, sessions (create + attach_url), │
│ contributions, artifact index │
│ · read-only catalog: aiwg discover / show (display only) │
│ · user asset library: clone/import/delete (never writes │
│ AIWG install files) │
│ · audit log: ~/.aiwg/cockpit/audit/events.jsonl (redacted) │
│ · serves the built React app (token-injected) │
└──────────────────────────────────────────────────────────────┘
│ proxies / sources ▲ loads /?token=…
▼ │
agentic-sandbox executor ┌──────┴──────┬───────────────┐
(8120 gRPC / 8121 WS / browser VS Code webview Tauri window
8122 HTTP) (apps/cockpit/{web,vscode,desktop})
· A2A v2 + pty-ws/v1
Control plane vs data plane
- Control plane — lifecycle, approvals, session create/list, index,
library, audit — goes through the gated Bridge (`/api/*`, Bearer token + CSRF; see Trust & Security).
- Data plane — the live pty byte stream — connects the browser **directly
to the executor** over WebSocket, using the `attach_url` the Bridge issues with each session row. The Bridge serves no WebSocket endpoint of its own; its live-update channel to the UI is Server-Sent Events (`/api/events`, a `cockpit.refresh` event plus heartbeat).
The attach URL preserves whatever the executor advertises (`attach_url` / `pty_ws_url`, with `{host}` substitution and `http→ws` scheme mapping) and falls back to `{ws-base}/agents/{instance-id}/sessions/{session-id}/attach`. The path segment is the instance id, not the resolved agent name.
Component map
All components live under `apps/cockpit/` — a private npm workspace root (`@aiwg/cockpit`, CalVer in lockstep with base AIWG).
| Component | Role |
|---|---|
| `bridge/` | The control-plane server and static host. Single-file Node HTTP server (`createBridge()` factory + CLI entry), env-configured, no framework dependencies |
| `web/` | React 19 + Vite + TypeScript SPA — the 11 operator surfaces (Surfaces); xterm.js terminals |
| `shell-core/` | The cross-shell handshake contract: runtime-file reading, token resolution, OS-keychain backends |
| `vscode/` | VS Code extension (webview shell over the same Bridge) |
| `desktop/` | Tauri v2 native shell over the same Bridge |
| `mock-executor/` | Wire-faithful agentic-sandbox stand-in — automated-test-only, refused for human launches (Development) |
| `contrib/` | Declarative UI contributions (actions, screens, workflows) + JSON schema; actions inject commands into a session |
| `runtime-docs/` | Specification of the `~/.aiwg/cockpit/runtime/` handshake files |
| `poc/` | Standing risk-gate proofs (bridge kill-isolation, security checks) run in CI via `npm run poc` |
| `scripts/` | Dev launchers (`cockpit-dev.sh`, `cockpit-up.sh`) |
Executor discovery and admin surfaces
The Bridge resolves the executor from `AIWG_COCKPIT_EXECUTOR_URL` (alias `EXECUTOR_URL`), defaulting to `http://127.0.0.1:8122`. Liveness is probed at `/healthz/http` → `/healthz` → `/health`; deep capabilities (e.g. `host_runtime_enabled`) at `/healthz/deep` first.
Upstream calls use candidate lists so the Bridge tolerates executor-version skew: inventory tries `/admin/instances` then `/api/v2/admin/instances`; start/stop try legacy-then-v2; destroy and reconnect prefer the v2 admin surface. Running work and approvals are not admin projections at all — they are derived from per-instance A2A task lists (`/agents/{id}/tasks`), which is why they work against any conformant executor. Cost currently rides the legacy `/admin/cost` route.
Field normalization covers snake_case and camelCase payloads; unknown fields degrade to opaque posture rather than a broken screen.
Ports
The agentic-sandbox canonical dev runner binds 8120 (gRPC), 8121 (pty-ws), 8122 (HTTP). The Bridge therefore defaults to 8140, off that range, and refuses to start on a reserved executor port rather than silently squatting on one. Override with `PORT` or `AIWG_COCKPIT_BRIDGE_PORT`.
Relationship to `aiwg serve`
They are different servers with different jobs:
| `aiwg serve` (:7337) | Cockpit Bridge (:8140) | |
|---|---|---|
| Role | Serve API + sandbox registry substrate | Merged operator console |
| UI | `apps/web` retained as a compatibility bundle | `apps/cockpit/web`, served by the Bridge itself |
| Consumers | Programmatic + legacy dashboard | Operators (browser / VS Code / desktop) |
New operator-console work happens in Cockpit. The Bridge shells out to the repo-local AIWG CLI for read-only catalog data (`aiwg discover` / `aiwg show`) and enriches inventory from the executor's agent registry when admin surfaces are absent. See the serve guide for the substrate.
See also
- Bridge API — the full endpoint and configuration reference
- Development — bring-up scripts, mock boundary, e2e