Skip to content

v1.0.0

Choose a tag to compare

@LuD1161 LuD1161 released this 23 Jul 05:08

agentjail v1.0.0 — network visibility: see what your agent sends its model on Linux and macOS, transparent HTTP/2 + gRPC tunnel, IPv6, and a network-visibility UI

agentjail 1.0 — see what your coding agent sends its model, on Linux and macOS.

The most revealing thing an agent does is talk to its model: the prompt it assembled, the tools it was handed, the slice of your codebase it pulled in, and what came back. On Linux, agentjail has captured that traffic through its transparent tunnel + MITM for releases. v1.0.0 brings the same full visibility to macOS — the harder platform, with no system extension required — and levels up the tunnel on both with HTTP/2, gRPC, and IPv6. All verified against a real agent on the build you actually install.

Highlights

  • Full network capture on macOS, no system extension. Linux already captured your agent's traffic through the tunnel; on macOS a transparent MITM can't decrypt POST /v1/messages because Bun's inference client ignores every CA. So v1.0.0 points the agent's own ANTHROPIC_BASE_URL at a local, egress-guarded capture gateway that records the full request/response and forwards to the real API over TLS — no privileged extension needed (AGE-259, ADR 0109).
  • The transparent tunnel, now with HTTP/2, gRPC, and IPv6. The same gVisor + MITM engine on Linux and macOS now negotiates ALPN and serves h2 with full parity to HTTP/1.1, carries gRPC status and trailers, keeps streaming RPCs from stalling, and captures IPv6 egress instead of leaking it (ADR 0102/0108, AGE-262).
  • Proven on the installed build. A live Claude Code session decrypted and captured end to end behind a golden-VM gate, closing three defects only the shipped binary + a real agent could hit (ADR 0103).
  • A network-visibility UI. Per-session request timelines, column filters, and a body viewer over an on-disk capture store.
  • One canonical network config (ADR 0110): CLI flag beats env beats config beats default, and agentjail doctor prints each knob's effective value and where it came from.

Upgrading: nothing changes in how you run agentjail — curl installs, brew upgrade, and self-update pick up 1.0 automatically, and existing config and policy keep working. Read the full story on the blog.


Added

  • Base-URL capture gateway for LLM providers (AGE-259, ADR 0109): current Claude Code runs on Bun and its inference client ignores every CA trust store, so a transparent MITM cannot decrypt POST /v1/messages (and in fact breaks it). Instead the shield points the agent's own ANTHROPIC_BASE_URL at a per-session loopback proxy — nonce-gated, egress-guarded, a user-set base URL is preserved — that records the full request/response (bodies encrypted at rest) and forwards to the real provider over TLS. Runs in both plain and --tunnel modes, independent of the system extension; fail-closed for a detected provider with an explicit --no-provider-gateway / network.capture_gateway: false opt-out. The darwin non-tunnel path became spawn-and-wait (from syscall.Exec) so the in-process gateway survives, preserving exit-code and signal parity.
  • IPv6 datapath for the macOS tunnel (AGE-262, opt-in): IPv6 egress was grabbed by the extension but had no v6 datapath, so it reset (SSL_ERROR_SYSCALL) and evaded capture while IPv4 worked. The gateway now provisions a v6 address (fd79::, outside the DNS-VIP pool) end to end; enable with --tunnel-ipv6 / network.tunnel_ipv6 (default off until the installed app is attested dual-stack).
  • Network flag precedence + doctor sourcing (ADR 0110): one canonical order for every network knob — CLI flag > env var > policy.yaml > default — and agentjail doctor now prints each knob's effective value AND where it came from (cli/env/config/default). --netproxy is documented as deprecated.
  • HTTP/2 in the tunnel MITM (ADR 0102): advertises h2, http/1.1, serves h2 via http2.Server.ServeConn with an http2.Transport upstream, full parity with the HTTP/1.1 path (body capture, policy/deny, recording). gRPC unary works with grpc-status through trailers; hop-by-hop headers are stripped on both legs. Proven end-to-end over the real TUN.
  • Streaming/bidi h2 request bodies are forwarded without pre-draining, so a long-lived stream never deadlocks; host/path/method policy still applies (body-content scan is bounded-body only — ADR 0102).
  • Encrypted body capture with keychain/file KEK tiers so doctor never overstates protection (ADR 0095/0097).
  • agentjail ui --trusted-host: reach the local UI behind a same-host reverse proxy without disabling the anti-rebinding guard (ADR 0099).
  • agentjail-daemon --retention-interval: retention + WAL checkpoint re-run periodically, not just at startup (ADR 0101).
  • Network session "active" state reflects the owning shield PID's liveness, matching agentjail sessions list --active (ADR 0100).

Fixed

  • Streaming responses stalled on the macOS tunnel (ADR 0108): the gVisor serverNetstack pump dropped outbound packets when its queue was full, silently losing TCP segments on a lossless carrier and hanging long SSE streams. It now applies backpressure (blocks, never drops).
  • Decrypted bodies were not persisted on the darwin tunnel (AGE-259): the session id was shield-<ts>-<hex>, which mitm.NewBodyStore rejected (non-alnum), silently disabling body storage. The id is now alphanumeric, so request/response bodies actually land.
  • Dev builds (make dev-deploy/dev-install) reported version dev — the -ldflags targeted a nonexistent main.version instead of internal/buildinfo.Version (AGE-247).
  • Retention enforced its window only once at daemon startup, so a long-lived daemon's DB grew unbounded (AGE-225).
  • h2 request trailers were dropped on streamed bodies (.Clone() froze an all-nil map before net/http2 filled it).
  • Tunnel silently fell back to netproxy on every installed deployment (ADR 0103): the TUN-holder / nsenter re-exec used os.Executable(), which resolves the installed agentjail-shield symlink to agentjail and misdispatched to the CLI, so the TUN helper never ran. It worked only from the standalone dev binary, hiding the bug. Now dispatched by argv[0].
  • Tunnel holder died mid-session (ADR 0103): Pdeathsig fires on the cloning thread's exit, which Go could retire, SIGKILLing the holder so a follow-on nsenter failed. Replaced with socket-based liveness (the holder exits when the shield closes the handoff socket).
  • Agent could not resolve DNS inside the tunnel on systemd-resolved hosts (ADR 0103): the 127.0.0.53 stub is unreachable in the netns, so the agent saw ENOTIMP. The holder now bind-mounts a resolver pointing at the in-tunnel gateway.

Docs

  • Document the unprivileged-userns requirement: Ubuntu 23.10+ gates it behind AppArmor; the tunnel fails open to netproxy when it is unavailable, and agentjail doctor prints the one-time command to enable the full tunnel.