agentjail-shield wraps your coding agent in the operating system’s kernel
sandbox before exec’ing it. Every subprocess the agent spawns inherits the
restrictions, so tricks like printf x > ~/.ssh/id_rsa, eval $(base64 -d),
or python -c "open(...).write(...)" all return EPERM at the kernel level,
regardless of any hook bypass.
This is the OS-native enforcement layer in agentjail’s isolation model, stronger than hooks alone.
Quick start
Run your agent through the sandbox with agentjail claude (or agentjail run -- codex / agentjail run -- cursor) - it shields by default:
agentjail claude
The shield is a role of the single agentjail binary. You can invoke it
directly on any command through the agentjail-shield symlink:
agentjail-shield -- sh -c "cat ~/.ssh/id_rsa"
# → Operation not permitted
To run hook-only, without the OS sandbox, add --no-sandbox
(agentjail claude --no-sandbox).
How it works
macOS: Apple Seatbelt
agentjail-shield generates an Apple Seatbelt (sbpl) profile from your policy
config and execs the agent under sandbox-exec. The profile is deny-list based (allow-by-default):
- Denies writes to sensitive paths:
~/.ssh,~/.aws,~/.gnupg,~/.config,~/.agentjail,~/.docker,~/.kube,~/.cargo,~/Library/Keychains,~/Downloads,~/Desktop,/etc,/var - Denies writes matching sensitive filenames:
.env*,*.pem,*.key,id_rsa,credentials,.netrc,~/.npmrc,~/.pypirc,~/.git-credentials - Denies reads of credential paths:
~/.ssh,~/.aws,~/.gnupg,~/.docker,~/.kube,~/Library/Keychains, private key files - Allows reads of system trust stores (
/private/etc/ssl,/System/Library/Keychains) so TLS works - Restricts network egress (see Network enforcement)
No sudo, no entitlement, no Developer ID required. sandbox-exec ships on
every macOS since 10.5.
Linux: Landlock LSM
On Linux, agentjail-shield uses Landlock (available since Linux 5.13). Landlock is allowlist-based, the opposite of the macOS deny-list:
| Allowed (read-write) | Allowed (read-only) | Denied |
|---|---|---|
/tmp, current working directory | /usr, /bin, /lib, /lib64, /sbin, /etc, /dev, /proc, /sys, /opt, /run, $HOME (excluding sensitive subdirs) | Everything else |
Sensitive subdirectories (~/.ssh, ~/.aws, ~/.gnupg, ~/.agentjail,
~/.config) are never added to the allowlist, so they are denied by default.
When an explicit grant covers a regular file directly under ~/.config, the
shield applies file-scoped Landlock rights. It does not grant the directory or
expose sensitive config subdirectories.
Landlock restrictions are irreversible: once applied, neither the process nor its descendants can lift them. No special privileges required.
Other platforms
On unsupported platforms (Windows, FreeBSD, etc.), agentjail-shield prints a
warning and execs the agent without any sandbox (fail-open). The hook layer
(agentjail-hook) still runs on every tool call.
Network capture and enforcement
The shield captures the agent’s network traffic and checks it against
network.allowed_hosts. This works on both Linux and macOS.
Capture gateway (default)
The LLM provider call (POST /v1/messages) is captured by default through a
base-URL capture gateway - on macOS this needs no system extension, because
the shield points the agent’s own ANTHROPIC_BASE_URL at a per-session loopback
proxy that records the request/response and forwards to the real provider over
TLS.
Transparent tunnel (opt in with --tunnel)
To capture and enforce all of the agent’s traffic - not just the provider call
- opt into the transparent tunnel with
--tunnel. It routes everything through agentjail’s gVisor + MITM engine (network namespaces on Linux, a system extension on macOS) and enforcesnetwork.allowed_hosts, with HTTP/2 and gRPC support.
This remains opt-in for a direct agentjail run -- <agent> launch. If you chose
agentjail install --with-path-shim, the installed claude, codex, and
Cursor agent wrappers use the transparent tunnel by default.
Use --require-tunnel or AGENTJAIL_REQUIRE_TUNNEL=1 when a fallback would
invalidate a test or release claim. AgentJail then refuses to launch if the
transparent tunnel cannot start.
On macOS, the full transparent tunnel requires the separately built and approved Network Extension application. The AgentJail CLI tarball does not package, install, activate, or approve that extension. The default base-URL provider capture path above does not need it.
Run agentjail doctor to see the effective value of every network knob and
where it came from (CLI flag > env var > policy.yaml > default, per ADR 0110).
--netproxy (deprecated)
The older egress firewall - agentjail-netproxy, a localhost CONNECT proxy the
agent’s HTTPS_PROXY points at - still works but is deprecated in favor of the
tunnel.
Relationship to the hook layer
The sandbox does not replace the hook (agentjail-hook + agentjail-daemon).
They serve complementary roles:
| Capability | Hook | Sandbox |
|---|---|---|
| MCP server allowlisting | Yes | No |
Command-intent rules (git push --force) | Yes | No |
| Tell the agent why something was blocked | Yes | No |
| UX decisions (allow / deny / ask) | Yes | No (deny only) |
| Catch shell/eval/Python file writes | No | Yes (kernel-level) |
| Catch subprocess bypass | No | Yes (inherited by descendants) |
| Network per-host enforcement | No | Yes (via the tunnel, both platforms) |
Use both for defense in depth. The hook catches the 90% case with good UX; the sandbox is the safety net that catches the rest.
Configuration
The shield reads the same ~/.agentjail/policy.yaml as the hook and daemon.
The relevant sections:
file.extra_deny
Additional write-denied paths, appended to the built-in sensitive path list:
file:
extra_deny:
- /Users/me/secret-project
- /opt/production-data
file.extra_allow
Additional write-allowed paths (Linux Landlock only; no effect on macOS):
file:
extra_allow:
- /data/scratch
network.allowed_hosts
Hosts the agent can reach, enforced by the transparent tunnel (both platforms):
network:
allowed_hosts:
- api.github.com
- registry.npmjs.org
- "*.example.com"
Wildcards follow cert-style matching: *.example.com matches
foo.example.com but not example.com itself. Built-in defaults include
GitHub, npm, PyPI, crates.io, Go proxy, and Deno.
CLI reference
agentjail-shield [flags] -- <agent-cmd> [args...]
The -- separator is required.
| Flag | Default | Description |
|---|---|---|
--policy=PATH | ~/.agentjail/policy.yaml | Path to the policy config file |
--profile-print | false | Print the generated sandbox profile to stderr and exit |
--tunnel | false | Capture and enforce all traffic through the transparent tunnel |
--require-tunnel | false | Refuse fallback if the transparent tunnel cannot start |
--no-provider-gateway | false | Disable the base-URL LLM capture gateway |
--no-sandbox | false | Run hook-only, without the OS sandbox |
See agentjail doctor for the effective network configuration and precedence
(ADR 0110); --netproxy is deprecated in favor of --tunnel.
Practical guides
- Claude Code sandbox: set up and verify the hook, kernel boundary, network controls, and MCP policy together.
- AI agent security: place sandboxing inside a broader model for credentials, tool calls, and audit logs.
Debugging
# Mirror structured startup diagnostics to stderr
agentjail run --verbose -- codex
# Print the generated profile without running the agent
agentjail-shield --profile-print -- claude
# Test a specific operation
agentjail-shield -- sh -c "echo test > ~/.ssh/test_file"
# Expected: "Operation not permitted" on macOS
Routine launches print one to three lines. Full structured diagnostics are kept
under ~/.agentjail/logs/; AgentJail retains the newest 10 session files. The
directory is mode 0700, and each file is mode 0600.
Fail behavior
| Scenario | Behavior |
|---|---|
sandbox-exec missing (macOS) | Fail-open with loud warning; hook still active |
| Landlock unsupported (Linux < 5.13) | Fail-open with loud warning |
| Landlock setup error (other) | Fail-closed unless AGENTJAIL_SHIELD_ALLOW_UNSANDBOXED=1 |
policy.yaml missing | Falls back to built-in defaults |
agentjail-netproxy not found | Falls back to port-based filtering |
| Unsupported platform | Fail-open with warning |
Next steps
- Isolation tiers: where the sandbox fits in agentjail’s layered enforcement model.
- Configuration: the full
policy.yamlreference. - How it works: the hook layer that complements the sandbox.