It reads your rules
ai-jail trusts your own ~/.ai-jail file. It treats a .ai-jail file inside the project as untrusted: that file can tighten the sandbox but never open it. Flags on the command line win over both.
ai-jail builds a small, closed copy of your system, puts your project in it, and starts the agent inside. The walls are features of the operating system itself, so the agent runs at full speed and cannot switch them off. Per launch, the network is closed apart from the agent's own API host, open to a few named hosts, or fully open.
For the engineer who wants to know what is enforced. The first paragraph and the first picture are enough for everyone else.
It takes a moment, every time, and nothing keeps running in the background afterwards.
↔ Swipe sideways to see the full diagram
ai-jail trusts your own ~/.ai-jail file. It treats a .ai-jail file inside the project as untrusted: that file can tighten the sandbox but never open it. Flags on the command line win over both.
ai-jail sets up each layer below before the agent exists, and maps the toolchains you have (mise, cargo, npm and other caches) read-only or into a jail-owned cache. If a config file is unreadable or invalid, ai-jail stops instead of starting with weaker rules.
The agent, and every script, package and MCP server it starts, inherits the same walls. When the agent exits, ai-jail exits too.
Every launch picks one of three network modes. The default is the strictest: no route out except what the filtered proxy lets through, which is a known agent's own API host on both platforms and, on Linux with toolchains on (the default), a short list of package registries. The other two are flags you pass on purpose.
↔ Swipe sideways to see the full diagram
The default. The sandbox gets a private network namespace with no route out, except through the filtered proxy. For a known agent the proxy lets its own API host through, on Linux and macOS, so a bare ai-jail claude works. On Linux, with toolchains on (the default), it also lets a fixed list of package registries through, so npm install and cargo build work; on macOS the registries stay closed. --no-network makes it fully offline.
--allow-host HOST, repeatable. The namespace stays closed; a small bridge inside forwards traffic to a CONNECT proxy outside, which dials only the listed hosts and their subdomains. The proxy resolves each name once, outside, and dials the address it checked. ai-jail sets http_proxy inside, and a program that ignores it reaches nothing.
--network. The wall is gone on the network side, and the agent can send out whatever it can read.
| Linux | macOS | |
|---|---|---|
| The fence in filtered mode | The network namespace itself, plus the bridge inside it | One sandbox-exec rule that allows outbound connections to the proxy's port only |
| DNS inside the sandbox | Fails. The proxy resolves names outside. | The system resolver is not fenced, so a low-bandwidth DNS channel remains |
ai-jail does not trust any single wall to hold alone. On Linux the agent sits behind five of them, each enforced by the kernel, plus two filters on what goes in and what comes out.
↔ Swipe sideways to see the full diagram
ai-sandbox), no inter-process channels to host programs, and a network namespace with no route out unless you open one with --allow-host or --network. If ai-jail dies, everything inside dies with it./usr are read-only, and the home directory and /tmp are fresh and held in memory. The toolchains you have (mise, cargo, npm caches) are mapped into that fresh home, read-only or as a jail-owned cache.~/.ssh, ~/.aws, ~/.gnupg, ~/.docker and browser profiles are on a built-in list that is never mounted. The harness's own state directory, for example Claude's ~/.claude, is the exception: ai-jail mounts it read-write by default so the agent starts logged in, and --no-agent-state leaves it out. Anything written outside the project vanishes when the session ends.ptrace, mount, bpf, kernel modules, io_uring, the kernel keyring, raw sockets, and TIOCSTI, a call that can type keystrokes into your terminal. It is a blocklist on purpose, so compilers and runtimes keep working. The one process started before Landlock and seccomp apply is the small bridge that carries filtered network traffic out, so it alone stays unrestricted.PATH, HOME, TERM, locale, proxy and certificate settings.ANTHROPIC_API_KEY for Claude, which is copied by default along with its login; --no-agent-state stops it. You pass another one through by name with --env when a task needs it.macOS has no namespaces, Landlock or seccomp. ai-jail turns the same policy into a profile for sandbox-exec, Apple's built-in sandbox. The ai-jail documentation says plainly that this is not equivalent to the Linux isolation.
↔ Swipe sideways to see the full diagram
| Linux | macOS | |
|---|---|---|
| Backend | bubblewrap, then Landlock, seccomp and resource limits | sandbox-exec with a profile generated for each launch. Apple has deprecated it, and nothing better exists without kernel extensions. |
| Home directory | A fresh in-memory home. Your real one is not mounted. | The profile starts with no global reads and then allows only the paths the agent needs, so your real home stays closed. |
/tmp | Private, discarded on exit | The host's /tmp, because there is no mount namespace. TMPDIR points at a private folder made for the launch, and that is the only temp path granted. |
| Copy-on-write maps | --overlay-map lets the agent write to a copy | Treated as a read-only map |
| Network | Closed by default: no route out except a known agent's own API host and the package registries allowed when toolchains are on. --allow-host or --network opens more, --forward-port relays one port of the host's loopback, --no-network closes all. Under --lockdown the open network is always blocked, even with --network. | The same default and the same flags, including the known agent's API host, but without --forward-port, the registries or the toolchain cache maps, which are Linux only. Lockdown blocks the open network here too. |
| Other programs | Invisible: own process list, no host IPC | Mach, IOKit and host IPC are denied unless you pass --macos-host-ipc |
ai-jail writes as little security code of its own as it can. It drives bubblewrap, which is small, packaged by every major distribution and exercised daily by Flatpak, and adds kernel features on top.
| Option | Why not |
|---|---|
| Firejail | It can do the job, but it is a setuid root binary, and several CVEs come from that design. bubblewrap works without root. |
| Rewriting bubblewrap in Rust | More than 500 lines of security-sensitive code to maintain and patch alone, to avoid a dependency of about 50 KB that receives upstream fixes. |
| nsjail | Capable, but built for servers and driven by protobuf config files, and less widely packaged. |
| systemd-nspawn | Requires systemd, so it fails inside containers and on systems without it. |
The whole tool is one synchronous Rust program, with no async runtime, daemon or container image.
Dependencies counted in the ai-jail repository at version 2.9.0; tests at version 2.9.0.
ai-jail is a single binary that needs no daemon and no root. You add one word in front of the command you already run.
brew tap akitaonrails/tap && brew install ai-jailai-jail claude