Skip to main content
Airlock’s audit trail starts as two local logs answering different questions, and airlock publish ships both to the same self-hosted store — airlockd from the deploy bundle — where they become one per-user record. There is no third-party service in this path: events land in the Postgres you operate.

No schema setup

The backend applies its own embedded migrations when it starts. There is no table to create and no SQL to run — docker compose up then airlock publish is the whole setup.

Identity: login and API tokens

Publishing is authenticated and identity-bound:
  1. Your admin creates your account in the console (Airlock’s own user table — no external IdP required).
  2. airlock login exchanges the backend URL, your email, and your password for an ingest-scoped API token, cached in ~/.airlock/auth.json (mode 0600). The backend stores only the token’s hash.
  3. Every publish carries the token, and the server attributes each event to the token’s user. A batch claiming any other identity is rejected. A token cannot write someone else’s history, and an ingest token cannot read anyone’s timeline.
The api also records a server-assigned received_at beside each event’s client-reported timestamp, so backdating is visible rather than silent.

What gets shipped

airlock publish collects two sources into the same event stream: Sandbox transcripts — each user or assistant message in ~/.airlock/agent/claude/projects/<project>/<session>.jsonl becomes one event: content is a readable flattening of the message. What the dashboard displays and what detection scans:
  • Text blocks are included as-is.
  • Tool calls become [tool_use <name> <first 200 chars of input>].
  • Tool results become [tool_result <first 300 chars>].
  • Thinking blocks are omitted.
  • The whole field is capped at 8,000 characters.
Proxy egress logs — each line in ~/.airlock/logs/<repo>/audit-*.jsonl becomes one access event: A third kind — decision events from agents running outside any sandbox — arrives over the push API rather than through publish, and lands in the same store and timeline. Together: the conversation in which a document was read, and the network attempts the same session made.

Publish

1

Preview without sending

Prints per-source event and session counts and sample rows. Touches no network. Also available as -n.
2

Publish

Requires airlock login first — without it, publish stops with not logged in.
3

Or stream continuously

Polls both sources every 3 seconds and republishes any file whose size changed, so the dashboard follows a session while it is still running. Also available as -w. Ctrl-C to stop.

Re-running is safe

Ingest is insert-only and idempotent: every event carries a per-source dedupe key, and an event already received is skipped, never rewritten. Publishing the same session twice produces no duplicates, and a re-publish cannot alter history. If a batch fails under --watch, that file’s checkpoint is left stale deliberately, so the next tick retries it rather than skipping the events.

Credentials

Your identity lives in ~/.airlock/auth.json (mode 0600), written by airlock login: the backend URL and an ingest-scoped API token. It stays on your host: publish uses it directly, it is never injected into a sandbox, and the sandbox’s allowlist does not include the backend — the agent being audited cannot reach its own audit trail. Connector credentials stay separately in ~/.airlock/secrets.toml; see configuration.