Skip to main content
Airlock has two planes. The capture plane is every surface that turns access into identity-attached events: the agent runtime described on this page, plus the push-ingestion API and SaaS audit connectors covered in capture surfaces. The review plane is a self-hosted backend (Postgres plus airlockd, deployed from the bundle) that runs detection and turns it all into a per-person record: see the review plane below. Per session, the agent runtime runs two containers: a sandbox where the agent works, and an egress proxy that is the sandbox’s only way out.

Scoped isolation

The sandbox is attached to a single Docker network, airlock-net, created with --internal. An internal network has no route to the internet and none to the host, so the only peer the sandbox can reach is the proxy. The proxy container is the only one attached to both airlock-net and the default bridge network, which is what gives it egress.

The sandbox container

Started fresh for every run with --rm, so nothing persists in it:
  • Your repo is mounted read-write at /<reponame>, which is also the working directory and the container hostname. The name is lowercased, and any character outside a-z 0-9 _ . - becomes -.
  • HTTP_PROXY, HTTPS_PROXY (and lowercase variants) point at the proxy on port 8888. NO_PROXY covers localhost,127.0.0.1.
  • AIRLOCK_REPO holds the in-container repo path. AIRLOCK_USER_ID and AIRLOCK_USER_EMAIL (from airlock login) identify who is running this session.
  • Allowlisted host env vars and any MCP credentials are injected.
  • A TTY is attached only when your terminal has one, so piping into airlock works.

The agent’s home is not yours

So the agent isn’t re-authenticating and re-picking a theme on every launch, two paths persist across runs: This is an Airlock-owned home, deliberately not your ~/.claude. The sandbox keeps its own credentials, isolated from the ones on your host.

The egress proxy

One proxy container per repo, named airlock-proxy-<repo>, restarted on each airlock run. This is also what makes allowlist changes take effect immediately: edit .airlock.toml, start a sandbox, and the new policy is live. Identity is threaded in when the container starts: AIRLOCK_USER_ID and AIRLOCK_REPO ride in as environment, and every audit line carries them. On a shared machine, restart the sandbox after switching accounts. Attribution follows whoever was signed in when the proxy started. For each request, the proxy takes the hostname, checks it against policy (exact match or a subdomain of a listed domain), and writes an audit entry before acting on the verdict.
  • Allowed CONNECT — dials upstream and splices bytes. TLS stays end-to-end between the agent and the provider.
  • Allowed plain HTTP — forwarded upstream.
  • Refused403 naming the host and the policy that refused it.
Policy is one of two modes, per repo. Allowlist (the default) refuses anything unlisted and refuses to start on an empty list rather than silently allowing nothing. Blocklist permits everything except the hosts you name. See choosing a mode. The mode decides what is refused; it does not decide what is recorded. Both modes log every request identically, so the audit trail and everything built on it are unchanged whichever you run.

The audit log

Every attempt, allowed or denied, is appended as one JSON object per line to:
user_id comes from the identity threaded into the proxy’s environment at start; for an unattributed run (no airlock login) it is simply omitted. Denials are written immediately; allowed tunnels log on close, with byte counts in both directions and duration. The same events stream to the proxy container’s stdout as ALLOW / DENY lines, so docker logs -f airlock-proxy-<repo> gives you a live view. To see what a repo’s agents have been reaching:

The review plane

The egress log answers where did it connect. It cannot answer what did it read — by design, since TLS stays end-to-end and the proxy never sees plaintext. That answer comes from the agent’s own transcripts, which survive the container because the agent’s home is persisted.
Publishing runs on the host, outside the sandbox. That placement is the security property: the backend is not on the egress allowlist, so the agent being audited cannot reach its own audit trail, and the API token that authenticates publishing never enters a container. The store is the deploy bundle’s Postgres — in your VPC or on your laptop, operated by you. airlockd attributes every ingested event to its token’s user (a token cannot write anyone else’s history), records a server-side received_at beside the client timestamp so backdating is visible, and raises decision flags at ingest. The console is served by the same daemon, login-gated with role-scoped reads. And where an agent workflow crosses an irreversible boundary, the same machinery backs action gates: verification before the action.
Published content — including returned tool results, so internal document text — lands in that Postgres and stays inside your deployment. The backend makes no outbound connections.