← All posts
By The agentjail team

Announcing agentjail v1.0.0 🎉

Read the transcript your coding agent sends its model. agentjail v1.0.0 captures the full request and response on Linux and macOS - no system extension needed.

A capture panel showing a POST /v1/messages call to api.anthropic.com recorded with request and response bodies, labelled 'no system extension' - the agent's own ANTHROPIC_BASE_URL pointed at a local proxy.

Every few seconds, your coding agent opens a private line to its model and sends a package you have never read.

It is not small. On each turn it ships a system prompt, the full schema of every tool it can call, and a slice of your world - open files, working directory, git status, whatever it decided was “context.” Then it streams back a plan and a pile of tool calls. You approve the tool calls. You never see the package.

That package is also the perfect exfil channel. A prompt-injected agent doesn’t need to run curl; it can fold your .env into the next message to the model and you would have no idea, because that request was the one thing your sandbox couldn’t see.

agentjail 1.0 reads it - on Linux and macOS. On Linux, agentjail has captured that traffic through its transparent tunnel for releases. 1.0 brings the same full visibility to macOS, the harder platform: a plain agentjail claude now records the full POST /v1/messages - request and response, bodies and all - with no system extension required.

This is our biggest release yet - the 1.0 that pulls seven weeks of guardrails together and closes the last blind spot: the model call on macOS.

So we tapped the line

Here is what a captured request looks like (redacted, but this is the real shape):

POST https://api.anthropic.com/v1/messages
{
  "model": "claude-...",
  "system": "You are Claude Code, Anthropic's official CLI for Claude...",   // a few thousand tokens, every call
  "tools": [
    { "name": "Bash",  "input_schema": { /* ... */ } },
    { "name": "Read",  "input_schema": { /* ... */ } },
    { "name": "Edit",  "input_schema": { /* ... */ } },
    { "name": "Write", "input_schema": { /* ... */ } }
    // ...a dozen more, shipped in full on every turn
  ],
  "messages": [
    { "role": "user",      "content": "fix the flaky test in checkout" },
    { "role": "user",      "content": "<cwd, git status, and the contents of the files it decided to pull in>" },
    { "role": "assistant", "content": "<plan + tool_use blocks>" }
  ]
}

Three things jump out the first time you actually look:

  • How much rides along. The system prompt and the entire tool catalog are re-sent on every single turn, alongside whatever files the agent pulled into context. That is your code, leaving your machine, on a channel you had no window into.
  • The response is a to-do list for your shell. The tool_use blocks coming back are the exact commands the agent is about to run. Now you can read them before it does, next to the prompt that produced them.
  • This is where a leak would hide. If something is going to smuggle a secret out, the model call is the quietest place to do it. It is now the loudest.

Open agentjail ui, hit the Network tab, and every turn is there: the request, the response, decrypted, on disk, per session.

Why this took a whole release: Bun doesn’t trust you

The obvious way to see HTTPS traffic is a man-in-the-middle proxy with a trusted local CA. We have shipped exactly that on Linux for months. On macOS Claude Code, it simply does not work - and the reason is a good one to know if you run agents.

Current Claude Code runs on Bun, and Bun’s fetch client ignores every CA trust store - the system keychain, NODE_EXTRA_CA_CERTS, all of it. Point it through a MITM and it doesn’t get decrypted; it gets a TLS error and the request dies. We tried the polite doors (install into the System keychain, env-var overrides, a login-item trust profile). Every one of them failed for that specific inference client, and worse, a transparent MITM broke the call it was trying to observe.

So we stopped fighting the TLS client and used a lever it hands us for free: its own base URL. The shield points the agent’s ANTHROPIC_BASE_URL at a per-session loopback proxy - nonce-gated, egress-guarded, and if you set your own base URL it is preserved. The proxy speaks real TLS to the real provider, records both directions, and encrypts the bodies at rest. No CA to trust, no system extension to approve, and it runs in both plain and --tunnel modes. It is fail-closed: if it can’t stand up the gateway for a detected provider, it does not quietly let the call through (--no-provider-gateway opts out).

That is the door that was actually open. It is also why 1.0 captures the request that matters out of the box, where a “correct” MITM cannot.

Want to see everything? The optional full tunnel

The capture gateway already catches the request that matters. To watch everything else the agent touches, opt into --tunnel: it routes all of the agent’s traffic through the same gVisor + MITM engine on both Linux and macOS. Two things landed in 1.0:

  • HTTP/2 and gRPC (both platforms, Linux first). The tunnel MITM now advertises h2, http/1.1 and serves h2 with full parity to the HTTP/1.1 path - body capture, policy, deny, recording - with gRPC status and trailers preserved. Long-lived streams stopped deadlocking: request bodies forward without pre-draining, and a gVisor pump that used to silently drop packets on a full queue now applies backpressure.
  • IPv6 (macOS). v6 egress used to be grabbed by the tunnel with nowhere to go, so it reset and slipped past capture while IPv4 worked. It now has a real datapath end to end - opt in with --tunnel-ipv6 while the installed app is being attested dual-stack.

Proven on the build you install

A tunnel that works from a dev binary but not the installed one does not work. 1.0 kills three defects only the shipped, symlinked binary plus a real agent could surface: the TUN re-exec resolved the wrong executable and misdispatched to the CLI, the tunnel holder died mid-session on a retired thread, and DNS was unreachable inside the netns on systemd-resolved hosts. A golden-VM gate now stands up a fresh machine, installs through the real installer, runs a live Claude Code session, and asserts the traffic is decrypted and captured end to end. The guarantee holds on the binary you actually run - not the one we build in CI.

The road to 1.0

1.0 is a milestone, not a rewrite - the point where seven weeks of layers add up:

  • Hook + policy daemon: every tool call evaluated against Rego in ~8 ms median, and the agent can’t disable its own hooks, daemon, or policy files.
  • Default-deny egress firewall: the agent reaches only allowlisted hosts; every other connection is blocked at the proxy, even if a hook is bypassed.
  • The shield sandbox: one allowlist compiled into Seatbelt (the macOS sandbox Chrome and Safari run untrusted web code in) and Landlock (the mainline-kernel LSM Microsoft and Google ship at scale). Files and exec denied by default - no bespoke VM to trust.
  • MCP inventory + per-tool policy, per-project cascade, PID-based sessions, a live UI and replay TUI: all of it made legible.
  • One session id, one true outcome: the Monitor and Network tabs now key off the same session identifier end to end, and every action reports the combined policy+sandbox result and which layer enforced it - a command the policy allowed but the sandbox blocked shows up as blocked, not a misleading green allow.

Network visibility is the capstone: the sandbox already governs what the agent can read, write, and run - now you can see what it says to the outside world.

What’s next

This is the start, not the summit - visibility comes first because you can’t block what you can’t see. What’s coming, honestly marked as planned:

  • Block, don’t just watch. Turn what you can now read into what you can stop: a policy you write in a few lines, evaluated on the content of a call - not just its host - so a protocol-aware proxy can deny a network request by what it’s actually carrying. Big enough for its own post - soon.
  • A native macOS app. Menu-bar install, policy management, and live session monitoring.
  • Cost tracking + session budgets. Token usage per agent, session, and project, so a runaway agent doesn’t eat your quota.
  • Deeper isolation. Eventually kernel-level enforcement of any process, not just hooked agents (eBPF LSM on Linux).

These are planned, not shipped, and we won’t pretend otherwise. macOS and Linux today; Windows deferred (WSL works in the meantime).

Upgrading

Nothing changes in how you run agentjail. curl installs, brew upgrade, and the built-in self-update pick up 1.0 automatically; existing config and policy keep working. The next time you wonder what your agent just told its model - run it under the shield and open the Network tab.

Thanks to everyone who broke the tunnel in creative ways and pushed us to get the installed-build path right. On to enforcing what you can now read.