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.
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.
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 |
--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.
Debugging
# 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
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.