Closed by default. You hold the switches.
With no options, the agent gets only your project, without network, login or keys. You open what a task needs one switch at a time, and you can close the jail further.
Three places a setting can come from
Settings come from a file inside the project, a file in your home directory and the flags you type. They are listed from least to most authority.

Project .ai-jail
ai-jail treats it as untrusted: it can tighten the jail and never open it. ai-jail never follows it through a symlink, and the agent sees an empty file in its place.
Global ~/.ai-jail
It is yours, so ai-jail trusts it. It holds a base table for every run, plus [commands.<name>] tables chosen by the first word of the command.
Command-line flags
Flags have the highest authority. Most switches come in pairs, such as --network and --no-network, so a flag can also close what a file opened.
In a command table, single values replace the base and lists such as maps and masks are added to it.
# base: applies to every commandhide_dotdirs = [".my_secrets"]mask = [".env", ".env.*"] # only when the command starts with "claude"[commands.claude]network = trueagent_state = trueenv_pass = ["ANTHROPIC_BASE_URL"]mask = ["*.pem"]The switches
Every row is off until you ask, and each capability flag has a --no- twin that closes it again. A red badge marks a switch that weakens the jail. Security has the full list and the reasons.
Network and services
| Flag | Config key | What it does | Risk |
|---|---|---|---|
--network | network = true | Unrestricted network access. Anything the agent can read, it can send out. | Weakens the jail |
--ssh | ssh = true | Your ~/.ssh, read only, and your SSH agent socket, so git push works. | Opens one thing |
--docker | no_docker = false | The Docker socket. This is effectively root on your machine, because the daemon can start containers that mount the host. | Weakens the jail |
--tailscale | tailscale = true | The Tailscale socket. | Opens one thing |
--systemd-user | systemd_user = true | The systemd user bus. The agent can ask your user manager to run services on the host. Linux only. | Weakens the jail |
Agent and environment
| Flag | Config key | What it does | Risk |
|---|---|---|---|
--agent-state | agent_state = true | The agent's own login and settings, such as ~/.claude or ~/.codex. Anything in the jail can then use those credentials. | Opens one thing |
--claude-dir <PATH> | claude_dir = "..." | Uses the given directory as Claude's state and sets CLAUDE_CONFIG_DIR. | Opens one thing |
--env <NAME[=VALUE]> | none | Passes one variable from your shell, or sets one with NAME=VALUE. Repeatable, never saved to disk. | Opens one thing |
--inherit-env | inherit_env = true | Passes your whole shell environment, every secret in it included. Avoid it. | Weakens the jail |
| none | env_pass = ["NAME"] | The same as --env, from the global file only. A project file that sets it is ignored. | Opens one thing |
Desktop and hardware
| Flag | Config key | What it does | Risk |
|---|---|---|---|
--gpu | no_gpu = false | GPU devices, and with them the driver's attack surface. Linux only. | Opens one thing |
--display | no_display = false | The Wayland socket and nothing else from your session. Linux only. | Opens one thing |
--x11 | x11 = true | The X11 socket. X11 lets a program log keystrokes and take screenshots. | Weakens the jail |
--audio | audio = true | PipeWire and PulseAudio sockets and /dev/snd. Anything in the jail can record and play audio. Linux only. | Opens one thing |
--host-shm | host_shm = true | The host's /dev/shm, which opens shared memory with processes outside the jail. | Opens one thing |
--pictures | pictures = true | Your ~/Pictures, read only. | Opens one thing |
--browser[=hard|soft] | browser_profile = "hard" | A separate browser profile. hard (the default) keeps nothing between runs, soft keeps a profile under ~/.local/share/ai-jail/browsers. | Opens one thing |
Files
| Flag | Config key | What it does | Risk |
|---|---|---|---|
--map <PATH|SOURCE:DEST> | ro_maps = [...] | Mounts one more path read only. Write SOURCE:DEST to mount it somewhere else. Repeatable. | Opens one thing |
--rw-map <PATH|SOURCE:DEST> | rw_maps = [...] | Mounts one more path read and write. Repeatable. | Opens one thing |
--overlay-map <PATH> | overlay_maps = [...] | Mounts a path copy-on-write: the agent's writes go to a side layer and the original stays untouched. Linux only; on macOS it becomes a read-only mount. | Opens one thing |
--hide-dotdir <NAME> | hide_dotdirs = [...] | Never mounts the named dot-directory, for example .my_secrets. Repeatable. |
Layers you can turn off, and should not
| Flag | Config key | What it does | Risk |
|---|---|---|---|
--no-landlock | no_landlock = true | Turns off Landlock, the kernel's own copy of the file rules. Refused under --lockdown. | Weakens the jail |
--no-seccomp | no_seccomp = true | Turns off the filter that blocks dangerous system calls. | Weakens the jail |
--no-rlimits | no_rlimits = true | Turns off the limits that stop runaway processes. | Weakens the jail |
--no-private-home | private_home = false | Gives up the fresh home directory. This is broad access to your real home. Prefer --map for the one path you need. | Weakens the jail |
How it works explains what each layer does.
Utility
| Flag | What it does |
|---|---|
--dry-run | Prints the sandbox command and runs nothing. It never writes a config file. |
--init | Writes or updates the project .ai-jail and exits. |
--clean | Ignores the project .ai-jail for this run. |
--bootstrap | Generates permission configs for the AI tools themselves. |
status | Shows the current .ai-jail configuration. |
-v, --verbose | Shows every mount as the jail is built. |
Run ai-jail --help for the rest: the status bar, worktrees, mise and terminal options.
Hide secrets inside the project
Your .env file lives in the project, and the project is readable and writable. With the network on, the agent can send out whatever it can read there. Masking takes those files away.

--mask <PATH|GLOB>
Replaces every match with an empty placeholder. The agent sees that the file exists and gets no content, so tools that only check for it keep working.
--deny-path <PATH|GLOB>
Makes every match a permission error.
--mask-except and --deny-path-except
Carve one path back out of a rule. They weaken protection, so a project file cannot set them.
# quote globs so ai-jail gets the pattern, not your shell's expansionai-jail --mask .env --mask '.env.*' --mask '*.pem' --deny-path secrets/ claude ai-jail --mask '.env.*' --mask-except .env.example claude# a project file may tighten, so this works in the repository toomask = [".env", ".env.*", "*.pem"]deny_paths = ["secrets/"]Lockdown: let an agent read code it must not change
--lockdown is for reviews and audits. The agent can read the project and answer questions about it, and it cannot change a file in it.

What changes
- The project is mounted read only
- Only
/tmpis writable on Linux. On macOS nothing is - No dot-directories from your home, no extra maps, no overlays
- The environment is cleared and
PATHis fixed to system directories - Landlock is mandatory: if it cannot be applied, the launch fails
- Tighter limits on processes, open files and file size
- No display, audio, GPU, Docker, SSH agent or mise, whatever else you passed
ai-jail --lockdown claudeRecipes
Copy one. To see what it builds, run it with --dry-run first.
A cloud agent, day to day
A hosted model needs the network to reach its API and its own login to authenticate. Put both in the global file and the command stays short.
# once, in ~/.ai-jail[commands.claude]network = trueagent_state = true # then, in any projectai-jail claudeGit push over SSH
--ssh mounts ~/.ssh read only and forwards your SSH agent socket. Pushing also needs the network.
ai-jail --network --ssh --agent-state claudemise toolchains
The fresh home directory has no mise installs in it, so ai-jail skips mise. Map the two mise directories in, read only.
[commands.claude]ro_maps = ["~/.config/mise", "~/.local/share/mise"]A browser for the agent
--browser alone gives a browser that cannot load a page or open a window. On Linux it also needs --network and --display, and an X11 browser needs --x11 in place of --display.
# Waylandai-jail --browser=soft --network --display chromium# X11ai-jail --browser=soft --network --x11 chromiumSeparate Claude profiles
Keep work and personal logins apart by pointing each run at its own Claude directory.
ai-jail --network --claude-dir ~/.claude-work claudeTry a risky refactor on a copy
An overlay keeps the agent's writes in a side layer under .ai-jail-overlays in the project. Compare it with the original afterward and keep what you like. Linux only.
ai-jail --network --agent-state --overlay-map ~/Projects/my-app/src claudeOld configs keep working
Backward compatibility is a project rule: the project never removes a flag or a config key. Unknown keys are ignored and missing ones take their defaults, so a file written for an old version still loads.
Older switches keep their inverted names, where true turns the thing off: no_gpu, no_docker, no_display, no_mise, no_landlock, no_seccomp, no_rlimits. That is why --gpu is saved as no_gpu = false. Newer switches use plain names such as network = true.
Put your agent behind bars
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