Model or dataset
akitaonrails/ai-jail avatar
akitaonrails/ai-jail

ai-jail: Running AI Coding Agents Inside bubblewrap and sandbox-exec

Multi-OS sandbox to run AI agents with better constraints (it is not 100% secure, but enough)

1,213 stars110 forksRustGPL-3.0

At a glance

What is it?
ai-jail wraps AI coding agents in OS-level sandboxes on Linux and macOS, with private home and credential mounting off by default. It is a containment layer for agent runs, not a VM, and the README says so directly.
Who is it for?
Adopt ai-jail if you already run a coding agent on Linux or macOS and want the default posture to be deny-by-default: private tmpfs home, no network, no agent credentials unless you pass --agent-state. Do not adopt it as a substitute for a disposable VM when the code the agent will execute is genuinely hostile; the README's own framing is that this is a useful layer, not a replacement.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem ai-jail addresses: agents that read your home directory and write anywhere

An AI coding agent is a process that reads files, runs shell commands, and writes to disk on your behalf. By default it inherits your full home directory, your environment variables, your SSH keys, your cloud credentials, and your network. If the agent is confused, or if a prompt injection reaches it through a file it reads, that access is the blast radius. ai-jail exists to shrink that blast radius without requiring a full virtual machine per run.

The target user is a developer who already runs a terminal agent such as Claude in a project directory and wants the run confined to that directory. The tool is written in Rust, licensed GPL-3.0, and ships binaries for Linux x86_64 and macOS aarch64. Its own description is deliberately modest: it is not 100 percent secure, but enough. That framing matters, because the README repeats it: ai-jail is a useful layer, not a replacement for a disposable VM when running hostile code.

How the sandbox is built: bubblewrap, Landlock, seccomp and limits on Linux, sandbox-exec on macOS

On Linux, ai-jail composes several kernel and userspace mechanisms. bubblewrap provides the namespace and mount isolation. Landlock adds a filesystem access-control layer. seccomp restricts the system calls the sandboxed process can make. Resource limits cap what the process can consume. On macOS, the tool uses Apple's deprecated /usr/bin/sandbox-exec interface instead. Windows is not supported at all; the README points Windows users at WSL2 and the Linux backend inside it.

The data flow is mount-based rather than policy-based. The project directory is writable by default; host capabilities are not. Private home is on by default, so the agent gets a fresh tmpfs $HOME rather than your real home directory. Agent credential state, such as Claude's ~/.claude and ~/.claude.json, is not mounted unless you explicitly ask. The first ordinary run may create a .ai-jail directory; --dry-run never writes it. Bootstrap output is always mode 0600.

One design decision worth calling out: existing unreadable or invalid project or global configuration fails closed rather than launching with a weakened policy. That is the right default for a security tool, but it means a malformed config file stops your agent run instead of silently degrading. Expect to debug TOML, not to be warned and continue.

Installing ai-jail and the BWRAP_BIN ownership check that will trip you up

Installation is conventional. Homebrew users run brew tap akitaonrails/tap && brew install ai-jail. Arch users have yay -S ai-jail-bin for the prebuilt Linux x86_64 binary or yay -S ai-jail to build from source. Cargo users run cargo install --locked ai-jail. Nix users can run nix run github:akitaonrails/ai-jail -- claude or nix profile install github:akitaonrails/ai-jail; the flake sets BWRAP_BIN automatically. GitHub Releases carry signed archives with checksums alongside, named ai-jail-linux-x86_64.tar.gz and ai-jail-macos-aarch64.tar.gz.

Building from source requires Rust 1.97.1 and the documented sequence is cargo build --release --locked followed by install -Dm755 target/release/ai-jail ~/.local/bin/ai-jail. On Linux you must install bubblewrap separately: pacman -S bubblewrap, apt install bubblewrap, or dnf install bubblewrap.

The part that catches people is BWRAP_BIN. It is accepted only when it canonically resolves to a root-owned executable that is neither group- nor world-writable, or to an executable with no write bits under a /nix/store whose own owner is root (or an unmapped owner inside a user namespace), is not world-writable, and carries the sticky bit if it is group-writable. That is the standard multi-user store layout, mode 1775. A group-writable store without the sticky bit is refused, because a group member could replace the binary. A single-user store owned by the invoking user does not qualify either. If you point BWRAP_BIN at a binary you built yourself outside those layouts, ai-jail will refuse it, and that is intentional.

The default-deny flag set, and what each capability actually exposes

The interesting part of ai-jail is not the sandbox construction, which relies on existing kernel primitives, but the default posture. Network, GPU, display, linked Git worktree metadata, X11, host shared memory, terminal passthrough, update check, and macOS host IPC all default off. Docker, SSH, Pictures, Tailscale, and the systemd user bus are also off by default. You opt into capability, not out of it.

Several flags deserve attention because their consequences are not obvious from the name. --network permits full network exfiltration of any readable data, so enabling it to let an agent fetch a dependency also opens an outbound channel for everything the sandbox can read. --x11 permits keylogging and screenshots. --host-shm opens host cross-process IPC. --terminal-passthrough disables the default VT parser filtering, exposing terminal clipboard, query and parser surface. --docker mounts a real Unix Docker socket and is effectively host-root, because the daemon can create host-mounted containers. --inherit-env passes the full parent environment, secrets included, replacing the default minimal allowlist. --worktree makes the per-worktree git dir and the shared common dir writable so the agent can commit; --lockdown keeps both read-only. --no-private-home is broad host-home access, and the README recommends --map and --rw-map as explicit, narrow alternatives instead.

--allow-tcp-port is still accepted for backward compatibility, but launch fails closed because UDP cannot be securely constrained through that option. That is a deliberate break: the flag parses and then refuses to run. Use --network only when you genuinely want unrestricted access. --update-check is the one flag that produces outbound traffic on its own; it runs a GitHub version check in a background thread while the interactive status bar is active, and all other launches make no network requests.

Agent credentials are opt-in, and mounting them undoes part of the isolation

A sandboxed agent that cannot authenticate is not useful for long. ai-jail handles this with --agent-state, which mounts the invoked command's credential state. For Claude that means ~/.claude and ~/.claude.json. It can also be set in trusted global config:

[commands.claude] agent_state = true

The README is explicit that mounting agent state exposes that agent's login and session material to everything running in the sandbox. So the flag solves an authentication problem while widening the trust boundary inside the sandbox. If the agent reads a malicious file and is manipulated into exfiltrating credentials, --agent-state is what makes that possible. The default is off for that reason, and the trade-off is real rather than theoretical.

Where ai-jail is the wrong tool

The README states the boundary plainly: this is a useful layer, not a replacement for a disposable VM when running hostile code. If your threat model includes code you did not write and cannot review, a shared-kernel sandbox built on namespaces, Landlock and seccomp is not the same as a separate kernel. A kernel bug or a misconfigured mount escapes the sandbox. ai-jail reduces the chance of accidental damage and casual exfiltration; it does not make the agent safe to point at untrusted repositories.

There are narrower failure modes too. macOS support rests on /usr/bin/sandbox-exec, which Apple has deprecated, so its long-term availability is outside the project's control. Windows users get nothing native and must go through WSL2. The BWRAP_BIN ownership rules mean self-built or oddly-permissioned bubblewrap installations are rejected outright, which will frustrate anyone running a custom toolchain. And the fail-closed config behaviour means a bad ~/.ai-jail file blocks the run rather than degrading gracefully. If your workflow depends on --allow-tcp-port, it no longer works at all.

What a VM-based alternative does differently

The natural alternative is running the agent inside a disposable virtual machine or a container with its own kernel, rather than sharing yours. The difference is not configuration, it is the isolation boundary. ai-jail relies on the host kernel to enforce the boundary: bubblewrap namespaces, Landlock rules, seccomp filters, and mount construction all run inside the kernel you are already using. A VM gives the agent its own kernel, so a kernel-level escape in the guest does not reach the host. That is a stronger guarantee and a heavier cost: boot time, image management, and a more awkward path for the agent to see your project directory.

ai-jail's advantage is that it is a single binary invocation in your existing shell. You run ai-jail claude in ~/Projects/my-app and the project directory is writable while host capabilities are not. There is no image to build and no VM to boot. The trade is that you are trusting the same kernel mechanisms the rest of your system trusts. For an agent working on your own code with your own prompts, that is a reasonable trade. For an agent consuming untrusted input, it is not.

Maintenance cost and the GPL-3.0 licence

ai-jail is actively released: v1.20.0, v1.20.1 and v1.20.2 all landed within roughly ten days in August and September 2026. That cadence means the flag surface and defaults can shift between minor versions, so pinning a version in CI is worth considering if you script the tool. Building from source requires Rust 1.97.1 and the --locked flag is documented, which suggests the project expects reproducible builds from a committed lockfile. On Linux you also carry a bubblewrap dependency that you must keep patched independently, since ai-jail validates the binary but does not ship it.

The project is GPL-3.0. If you invoke ai-jail as a separate program from your own tooling, that is ordinary use of a GPL program. If you link its code into your own product or redistribute a modified binary, the licence obligations attach. This is a general description, not legal advice; check with counsel if you plan to embed or redistribute it.

Editorial conclusion

Adopt ai-jail if you already run a coding agent on Linux or macOS and want the default posture to be deny-by-default: private tmpfs home, no network, no agent credentials unless you pass --agent-state. Do not adopt it as a substitute for a disposable VM when the code the agent will execute is genuinely hostile; the README's own framing is that this is a useful layer, not a replacement. Before trusting it, verify three things on your machine: that bwrap resolves to a root-owned, non-world-writable binary, that --dry-run shows the mount set you expect, and that your project does not need --worktree, --docker or --network to function.

Official sources

  1. akitaonrails/ai-jail on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Community notes

Community notes