Trust & Security

Token custody, posture badges, audit log, library boundary

Cockpit Trust & Security

Cockpit's security model has two independent layers: local shell↔Bridge auth (who may drive Cockpit) and transport posture visibility (how much to trust each instance's link to the executor). Keeping them separate is deliberate — a green local session tells you nothing about a degraded instance transport, and vice versa.

Local auth: token, CSRF, origin

Every Bridge launch mints a fresh random bearer token. All `/api` requests must pass, in order:

1. Origin check — browser requests must originate from the Bridge's own loopback origin. 2. Bearer token — `Authorization: Bearer …` (or `?token=` for EventSource), compared constant-time. 3. CSRF — mutating verbs with an Origin must echo the token in an `x-cockpit-csrf` header (the served app receives it via a `SameSite=Strict` cookie). 4. Real-executor assertion — requests are refused when the upstream looks like the test mock, unless a test harness explicitly allows it.

The UI receives the token by injection into the served page (`window.__COCKPIT_TOKEN__`); shells receive it through the runtime handshake below.

Token custody

`aiwg cockpit` writes `~/.aiwg/cockpit/runtime/bridge.json` (file mode 600, directory 700) containing the port and either the token or — preferred — a keychain reference. Backends: macOS Keychain (`security`), Windows Credential Manager (`cmdkey`), libsecret (`secret-tool`), and opt-in KWallet.

Strictness knobs:

  • `AIWG_COCKPIT_KEYCHAIN_STRICT=1` — never leave a plaintext token in the

runtime file when a keychain ref exists, and refuse to launch if the keychain fails; shell-core likewise refuses runtime files that carry only a plaintext token.

  • `AIWG_COCKPIT_REQUIRE_KEYCHAIN=1` — fail launch on keychain store failure.
  • `AIWG_COCKPIT_KEYCHAIN_DISABLED=1` — skip the keychain (plaintext runtime

file only; for constrained environments).

Posture badges (what the UI is telling you)

Runtime isolation

Each instance reports a runtime kind; Cockpit renders the executor-reported tier rather than assuming:

KindIsolation badge
`host``least` — full host access; the host-runtime daemon's own status is shown separately
`container` / `docker``shared-kernel`
`vm` / `qemu` / `kvm``strong` — hardware boundary
anything else`opaque` / `unknown` — degraded display, not a broken UI

Transport trust

A separate badge for the instance↔executor link: `secure` (e.g. mTLS local CA) · `local` (e.g. UDS, vsock, loopback) · `compatibility` (legacy shared-secret, TOFU) · `degraded` · `unknown`, plus the concrete mode string and a staleness flag. Legacy paths deliberately render as compatibility/degraded — never default-green. Agentic-sandbox owns transport provisioning and peer identity; Cockpit owns visibility and audit presentation, and stores no tokens, keys, CSRs, or bearer material in UI state, logs, or activity payloads.

Host daemon

For host-runtime targets the daemon status renders independently of the Bridge: `available`, `stopped`, `permission_denied`, `degraded`, `detected`, `unavailable`, `unknown`. When operator action is needed, Cockpit shows the documented command — it never silently installs or starts the daemon.

Health

A running runtime whose agent registration has vanished renders `agent unreachable` (and stays listed) rather than disappearing — the entry point to Recovery.

Sessions: observe-first authority

Attach starts as observer with stdin disabled; taking control is an explicit, visible role upgrade. Approval decisions in the Approvals tab are operator authorization, posted through the gated Bridge. See Sessions.

Audit log

Operator decisions are recorded locally at `~/.aiwg/cockpit/audit/events.jsonl` (file 600, dir 700): instance lifecycle, launches, reconnects, destroys, task cancels, session creates, approval responses, index rebuilds, and action injections (recorded as operator intents by the UI). Entries are redacted before write — secret-looking keys and values (tokens, API keys, bearer/`sk-`/GitHub-token shapes) never land on disk. The full event list is in Bridge API → Audit events.

Library boundary

The Library holds operator-owned copies under `~/.aiwg/cockpit/library`. Catalog access is read-only; AIWG install files are never written or overwritten by any library operation, and library paths are sandboxed against traversal.

Standing security proofs

Two proof-of-concept gates run in CI with every Cockpit check (`npm run poc`): a Bridge kill-isolation proof (killing the Bridge leaves executor sessions alive — control plane and data plane genuinely separate) and a security-checks suite (auth, origin, injection surfaces). The mock executor these use is test-only (Development).