Single-user audit gate for AI agents that talk HTTP. Whether you run Claude Code, Codex, Aider, OpenCode, an MCP client, or a plain curl in a script, agent-gate sits on your laptop, captures outbound HTTPS through a local proxy, flags + persists each captured flow, and lets you review it all in a local web dashboard. Airtight capture is supported on macOS and Linux today; Windows supports permissive proxy capture while the airtight runtime is in Plan 4. No backend, no telemetry, no cross-machine collation — everything stays on your disk.
The Operations dashboard — session catalog with flag counts, captured-event metrics, and a live risk feed.
agent-gate support has three separate layers:
- Airtight capture means
agent-gate run -- <cmd>puts the agent in an OS-level network jail, so subprocesses and tools that ignoreHTTPS_PROXYstill cannot bypass the local proxy. - Permissive capture means the proxy, dashboard, policy engine, and store work, but the target must honor
HTTP_PROXY/HTTPS_PROXY. - Rich parsing means the dashboard extracts AI-specific fields such as model, token counts, tool calls, tool results, streamed message content, or protocol-specific inventory counts. Today that rich path applies to Anthropic Messages traffic on
api.anthropic.com, OpenAI-compatible Chat Completions and Responses HTTP calls, and selected Codex/ChatGPT backend setup endpoints onchatgpt.com; everything else still gets audited as generic HTTP.
- Airtight launcher — per-OS network jail forces every byte of egress through the local proxy. Subprocesses inherit it; tools that ignore
HTTPS_PROXYget kernel-level deny. - TLS-MITM proxy with passthrough escape hatch — decrypts and parses every request. Cert-pinned hosts get raw TCP tunneling so MITM-rejecters still work.
- Three-list policy model —
allowlist.txt,denylist.txt,passthrough.txt. Mutated only byinit, the dashboard, or your editor — never by the runtime. - Nine built-in policy rules — host not allowlisted, secrets in body, env leakage, oversize, parse errors, more. Per-flag dismiss with reason.
- PII detection across the wire — SSN, cards, email, phone, JWT, UUID, IPv4, etc. The Explore page colors body text by kind.
- Parser registry — Anthropic Messages SSE reassembled into single events; OpenAI Chat Completions and Responses HTTP surface model, tokens, tool calls. Generic HTTP fallback for everything else.
- One-command bootstrap —
agent-gate initwrites config, mints a CA, detects your agents, seeds their hosts, installs the CA into your trust store. doctorvalidate-and-repair — one line per check across config, CA, ports, lockfile, agents, trust stores.--auto-repairfor filesystem fixes.- JSONL + SQLite store — JSONL is the source of truth; SQLite is an index.
agent-gate reindexrebuilds the index whenever. - Single binary, pure Go — no CGO. Cross-compiles to darwin/linux/windows × amd64/arm64.
brew tap WZ/tap
brew install agent-gate
agent-gate init # one-time bootstrap: config + CA + agent detection + cert install
agent-gate run -- claude # launch your agent through the gateThen open http://127.0.0.1:7878 to review what your agent is doing.
On macOS and supported Linux hosts this runs in airtight mode by default; Windows currently falls back to permissive capture. To validate the install at any time: agent-gate doctor.
For binary download, build-from-source, headless/CI install, and every flag the binary takes, see the runbook.
| Platform | Supported today | Current behavior | TODO |
|---|---|---|---|
| macOS | Yes |
|
|
| Linux | Yes, when unprivileged user namespaces are allowed |
|
|
| Windows | Partial |
|
|
| Agent / client | Supported today | What is rich today | TODO |
|---|---|---|---|
Claude Code (claude) |
Yes, primary path |
|
|
Codex (codex) |
Capture yes (via HTTP fallback) |
|
|
Aider (aider) |
Capture yes |
|
|
OpenCode (opencode) |
Capture yes |
|
|
| OpenClaw / Hermes Agent | Manual capture; not first-class today |
|
|
| curl, scripts, MCP clients, custom agents | Capture yes |
|
|
Three subsystems share one binary. On macOS and supported Linux hosts, the launcher spawns the target inside a per-OS network jail. The proxy decrypts each HTTPS request, runs it through a parser → policy → store pipeline. The dashboard reads from the store. Everything is loopback. Nothing leaves your disk.
The audit-log is non-negotiable: if the storage consumer falls behind, the proxy slows down (and upstream may time out). Drop-on-full would be a correctness bug, not a knob — every captured flow lands on disk, period.
The default landing page (/) is the session catalog — agent sessions grouped by host, with event counts, latest activity, and a flag rollup. Filter by host or time window. Click into any session for the timeline.
Right column is the risk feed: every active flag code, severity, and hit count. One glance tells you if anything new fired.
Session detail — 22 hits to downloads.claude.ai, all flagged host_not_allowlisted because Claude Code auto-updates from a host nobody trusted yet. Decide once, trust the host, move on.
Every captured event in one searchable, filterable table at /explore. Filter by PII kind (SSN, credit card, email, JWT, UUID, …), time window, or host. Substring-search request bodies, URLs, and hosts. Host chips show how many events per host so you can scope quickly without typing.
254 events across 5 hosts. Each row's PII chip (10 UUID, 3 EMAIL, etc.) tells you what's in the body before you click.
Click any event for the inspection page. Status, capture mode, the full request/response payload side-by-side. Credential-like values are masked by default — toggle to raw bytes when you actually need them (and the toggle itself logs a raw_peek event, so you can't peek silently).
Three host-policy buttons sit at the top of the page: Trust (allowlist), Block (denylist, returns 403), Passthrough (raw TCP for cert-pinned upstreams). Per-flag Dismiss writes to dismissals.json with a free-text reason.
A real host_not_allowlisted hit: the proxy returned a synthetic 403 to the agent and saved both sides for review. The body's UUIDs are highlighted by the PII coloring layer.
agent-gate run -- <cmd> spawns the target inside a per-OS network jail that physically forces all egress through the proxy. macOS and Linux work today; Windows falls back to permissive capture until Plan 4.
| Platform | Mechanism | Notes |
|---|---|---|
| macOS | sandbox-exec profile denies all network* ops except loopback to the proxy port |
|
| Linux | __netns-helper subprocess enters an unprivileged user + network namespace, binds the proxy port inside it, passes the listener FD back via SCM_RIGHTS |
|
| Windows | WFP provider/sublayer registered by agent-gate init; runtime path scaffolded for Plan 4 |
|
For the full flag reference, mode-picking guide, and run recipes, see the runbook.
agent-gate is a network audit gate, not a sandbox or RBAC system. It captures and audits HTTP egress; it does not isolate filesystem, IPC, or privileges.
Defends against:
- Tools that ignore
HTTPS_PROXY— kernel-level network deny in airtight mode - Subprocess descendants — the jail is inherited (sandbox profile / network namespace)
Does NOT defend against:
- Local IPC — UNIX sockets, named pipes, abstract sockets, shared memory are out of the proxy's view by design
- Root or admin agents — the user can lift the jail
- Filesystem reads — agent-gate sees the network, not the disk; an agent reading
.envand writing it elsewhere on disk is invisible - Steganographic exfiltration through allowed hosts
If your threat model needs filesystem isolation or RBAC, agent-gate alone is insufficient — pair it with OS-level sandboxing.
Each request hits the proxy and gets routed by three plain-text host lists. Resolution order is fixed:
1. denylist hit → 403 (always wins)
2. passthrough hit (and not denylisted) → raw TCP tunnel; no TLS interception
3. enforce mode + not allowlisted → 403
4. default → MITM, decrypt, capture, forward
The lists live in ~/.config/agent-gate/ and are mutated only by init, the dashboard, or your editor — never by the runtime:
| File | Add a host when… | How to add |
|---|---|---|
allowlist.txt |
…it's a known-good upstream you don't want flagged | Dashboard Trust → POST /api/trust, or agent-gate init --allow-host HOST |
denylist.txt |
…you want it blocked at the proxy with a 403, no upstream contact | Dashboard Block → POST /api/block |
passthrough.txt |
…it pins TLS or otherwise rejects MITM (e.g. mcp-proxy.anthropic.com) and you still want the connection metadata audited |
Dashboard Passthrough → POST /api/passthrough |
agent-gate help allowlist|denylist|passthrough prints the long-form explanation in your terminal.
Every captured event runs through nine rules. Severity drives the dashboard's risk feed and sort order — high floats to the top, info stays out of your way unless filtered.
High — likely worth a look:
| Code | Fires when |
|---|---|
host_not_allowlisted |
Request host is not in the allowlist |
secret_in_request |
Request body matches a credential pattern |
env_in_tool_result |
Tool result contains ≥3 KEY=VALUE lines |
Medium — usually benign but unusual:
| Code | Fires when |
|---|---|
oversized_request |
Request body > 5 MB |
unknown_mcp_endpoint |
Response is text/event-stream and host is unknown |
Low — noise tracking:
| Code | Fires when |
|---|---|
oversized_response |
Response body > 5 MB |
Info — context, not concern:
| Code | Fires when |
|---|---|
permissive_capture |
Session captured under env-only enforcement |
parse_error |
Parser annotated an error on the flow |
ws_pinned_upstream |
WebSocket upgrade succeeded (101) but the upstream client pins TLS — empty body capture is expected here (e.g. codex on chatgpt.com) |
Per-flag dismiss-with-reason on the dashboard writes to dismissals.json so the same flag doesn't keep nagging you on subsequent runs.
- Block at the network layer for non-allowlisted hosts unless
--enforce-allowlistis on. The default posture is audit, don't drop. Allowlist is an annotation, not a firewall, by default. - Decrypt cert-pinned upstreams. When the agent's MCP client pins
mcp-proxy.anthropic.com, agent-gate's MITM fails. We tunnel TCP raw via passthrough — body audit isn't possible, only connection metadata. - Detect filesystem exfiltration. agent-gate audits network. If the agent reads
.envand writes it somewhere on disk, we don't see it. - Run as a service / agent / daemon. Single-shot supervisor + dashboard per
agent-gate run. The lockfile enforces "one instance at a time." - Ship to a remote backend. No telemetry, no upload, no cross-machine collation. Everything stays local.
go build -o /tmp/agent-gate ./cmd/agent-gate # build
go test ./... # unit tests
go test -race ./... # race detector
go vet ./...
gofmt -l . # MUST be empty before commit
# cross-compile sanity
GOOS=linux go build ./...
GOOS=windows go build ./...
GOOS=darwin go build ./...CI matrix at .github/workflows/ci.yml runs Go 1.25 across ubuntu / macos / windows, plus a vet-race-fmt job on Linux.



