Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
A

ai-jail

> 编程语言
Open source

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

1.0K stars0 likes1 views
WebsiteGitHub

About

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

ai-jail

ai-jail runs AI coding agents in an OS sandbox: bubblewrap plus Landlock, seccomp, and limits on Linux; sandbox-exec on macOS. It is a useful layer, not a replacement for a disposable VM when running hostile code.

Install

bash
# Homebrew
brew tap akitaonrails/tap && brew install ai-jail

# Arch Linux
yay -S ai-jail-bin       # prebuilt Linux x86_64 binary
yay -S ai-jail           # build from source

# crates.io
cargo install --locked ai-jail

# Nix (flake) — sets BWRAP_BIN automatically
nix run github:akitaonrails/ai-jail -- claude
nix profile install github:akitaonrails/ai-jail

# GitHub Releases (signed archives, checksums alongside)
# ai-jail-linux-x86_64.tar.gz / ai-jail-macos-aarch64.tar.gz

Build from source with Rust 1.97.1:

bash
cargo build --release --locked
install -Dm755 target/release/ai-jail ~/.local/bin/ai-jail

Linux requires bwrap (bubblewrap): pacman -S bubblewrap, apt install bubblewrap, or dnf install bubblewrap. BWRAP_BIN is accepted only when it canonically resolves to a root-owned executable that is not group- or 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 — the standard multi-user store layout, mode 1775. A group-writable store without the sticky bit is refused, because a group member could then replace the binary. A single-user store owned by the invoking user does not qualify either. macOS uses Apple's deprecated /usr/bin/sandbox-exec interface. Windows is not supported; use WSL2 and the Linux backend inside it.

Quick start

bash
cd ~/Projects/my-app
ai-jail claude                 # no agent credentials mounted
ai-jail --agent-state claude   # mount Claude's credential state
ai-jail --dry-run claude

The project directory is writable by default; host capabilities are not. The first ordinary run may create .ai-jail; --dry-run never writes it. Existing unreadable or invalid project/global configuration fails closed rather than launching with a weakened policy. Bootstrap output is always mode 0600.

Secure defaults

Private home is on by default: the agent gets a fresh tmpfs $HOME, not your host home. Agent credential state (Claude's ~/.claude and ~/.claude.json, for example) is not mounted unless you ask for it:

bash
ai-jail --agent-state claude

or in trusted global config:

toml
# ~/.ai-jail
[commands.claude]
agent_state = true

Mounting agent state exposes that agent's login/session material to everything running in the sandbox, so it stays opt-in. Use --no-private-home only when deliberately granting broad host-home access; --map and --rw-map remain explicit, narrow alternatives.

The following capabilities default off: network, GPU, display, linked Git worktree metadata, X11, host shared memory, terminal passthrough, update check, and macOS host IPC. Docker, SSH, Pictures, Tailscale, and the systemd user bus are also off by default.

Flag pair Effect and security consequence
--network / --no-network Enables/disables unrestricted network. --network permits full network exfiltration of any readable data.
--gpu / --no-gpu Enables/disables GPU device access.
--display / --no-display Enables/disables display access. Only the validated Wayland socket is mounted; ai-jail never mounts all of XDG_RUNTIME_DIR. X11 is separate (--x11).
--x11 / --no-x11 Enables/disables X11 separately. X11 access permits keylogging and screenshots.
--audio / --no-audio Enables/disables host audio (Linux only). Binds the validated PipeWire/PulseAudio sockets in XDG_RUNTIME_DIR plus /dev/snd; anything in the sandbox can record and play audio while enabled.
--host-shm / --no-host-shm Enables/disables host /dev/shm; enabling it opens host cross-process IPC.
--terminal-passthrough / --no-terminal-passthrough Enables/disables raw terminal forwarding. Output is filtered through a VT parser by default; raw forwarding exposes terminal clipboard, query, and parser surface.
--agent-state / --no-agent-state Enables/disables mounting the invoked command's credential state (default off). Enables the agent to authenticate — and lets anything in the sandbox use those credentials.
--inherit-env / --no-inherit-env Default is a minimal environment allowlist. --inherit-env passes the full parent environment, secrets included.
--update-check / --no-update-check Enables the status bar's outbound GitHub version check, run in a background thread while the interactive status bar is active (default off; all other launches make no network requests).
--macos-host-ipc / --no-macos-host-ipc Enables/disables macOS Mach, IOKit, and host IPC exposure.
--worktree / --no-worktree Enables/disables validated linked-worktree metadata. When enabled, the per-worktree git dir and the shared common dir are writable so the agent can commit; --lockdown keeps both read-only.
--private-home / --no-private-home Enables/disables the default private home. Disabling it is broad host-home access.

--allow-tcp-port remains accepted for backward compatibility, but launch fails closed because UDP cannot be securely constrained through this option. Use --network only when unrestricted network access is explicitly desired.

--docker mounts an actual Unix Docker socket and is effectively host-root: the daemon can create host-mounted containers. DOCKER_HOST must identify an actual Unix socket; TCP/SSH endpoints are not mounted. ~/.docker is not broadly mounted. --systemd-user exposes only explicit user-bus sockets, but can still ask the host user manager to run services.

Environment policy

By default the sandbox receives only a minimal allowlist of terminal, locale, and toolchain variables — not your shell environment. Extend it explicitly:

bash
ai-jail --env CI --env API_BASE=https://internal.example claude
  • --env NAME forwards one variable from the parent environment.
  • --env NAME=VALUE sets a literal value.
  • Both forms are repeatable; a later --env for the same name wins.
  • --inherit-env passes the entire parent environment instead. This exports every secret currently in your shell into the sandbox; avoid it.

The same thing is available from trusted config as env_pass, so you do not have to repeat --env on every launch:

toml
# ~/.ai-jail
env_pass = ["CI", "API_BASE=https://internal.example"]

[commands.claude]
env_pass = ["ANTHROPIC_BASE_URL"]

env_pass is a trusted-layer field: it is read from the global config and its [commands.] tables, and ignored in a project .ai-jail, since a repository must not be able to pull variables out of your shell. It is also never written back to disk, because NAME=VALUE entries can carry secrets.

Project secrets

The project directory is writable by default, so secrets inside it are readable by the agent unless you mask or deny them:

toml
# .ai-jail (project config — untrusted, but tightening like this is honored)
mask = [".env", ".env.*", "*.pem"]
deny_paths = ["secrets/"]
  • --mask PATH|GLOB replaces matching project paths with empty placeholders: the agent sees the path exists but gets no content.
  • --deny-path PATH|GLOB makes matching paths inaccessible entirely.
  • --mask-except / --deny-path-except carve out exceptions.

Masks and denies apply to paths that exist when the sandbox is built. A literal path that is missing at launch, or a glob that matches nothing, is skipped with a warning — and a file created later, inside the session, is not covered. Create the file before launching (an empty .env is enough) when you need the rule enforced. Quote glob patterns so ai-jail receives the pattern instead of your shell expanding it first.

Ephemeral home and temp

The private home is a fresh tmpfs per launch. Nothing persists between runs except state you explicitly mount (agent state, --rw-map, command tables). On Linux /tmp inside the sandbox is sandbox-local and discarded on exit; writes to dotfiles and caches vanish with the sandbox. macOS has no mount namespace, so /tmp is the host's: TMPDIR instead points at a private per-launch session directory (mode 0700), and that is the only temp path the profile grants. The one exception is ai-jail claude on macOS, which is also granted write access to /private/tmp/claude- because Claude Code creates that directory unconditionally at startup and ignores TMPDIR; unlike the session directory, it persists between runs. Use a map or --agent-state for anything durable.

Browsers

--browser[=hard|soft] reuses an isolated browser profile, but browsers still need --network and --display passed explicitly on Linux (on macOS the display is system-level, so only --network applies there); --browser alone produces a browser that cannot load pages — and on Linux cannot open a window. X11-based browsers need --x11 instead of --display. Audio (e.g. video playback) additionally needs --audio, which binds the validated PipeWire/PulseAudio sockets — ai-jail --browser=soft --network --display --audio chromium.

Configuration

Two config files plus CLI flags, in increasing authority:

  1. ./.ai-jail (project) — untrusted, monotonic policy: it may tighten the sandbox but can never enable capabilities, outside maps, ports, claude_dir, or exceptions. It is masked from the sandbox by default, so the agent sees an empty file rather than your policy. Add .ai-jail to .gitignore and leave it uncommitted if you would rather the agent not notice it at all: git status inside the sandbox is then completely clean. A committed .ai-jail always shows as modified there, because masking replaces its contents — git has to report something either way. To let specific checkouts ship their own capability opt-ins, list their parent directory under trust_project_config in the global config (see below).
  2. ~/.ai-jail (global, trusted) — a base table plus optional [commands.] tables keyed by the first word of the command.
  3. CLI flags — highest authority.

A [commands.] table merges over the global base

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Rust

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言