Give coding agents the environment they need—while limiting persistent writes to the selected repository.
Quick start · How it works · CLI · Security model · Contributing
agent-box runs an interactive shell or coding agent inside a Bubblewrap sandbox while preserving your real Linux toolchain and exact absolute paths.
Your host filesystem remains visible but read-only. One selected repository is mounted read/write at its original path, while writes elsewhere in your home directory land in a disposable copy-on-write overlay. Existing Git identity, language runtimes, package caches, virtual environments, and agent configuration remain immediately available.
This lets you safely try session-specific agent configuration. For example, you can edit Claude Code's default settings at ~/.claude/settings.json from inside agent-box; the agent sees the changed settings for that session, but the host file is unchanged and the edits are discarded when agent-box exits.
Important
agent-box is primarily a host write-isolation tool, not a confidentiality boundary. Sandboxed programs can read files and credentials your Unix account can read. Network access is shared by default. Read the security model before using it with untrusted code.
Containers are excellent isolation tools, but they usually introduce a new filesystem layout and a second development environment. That is awkward when a project already depends on path-sensitive state such as:
/home/user/projects/app/.venv/bin/python
Inside agent-box, that path stays exactly the same. The repository's virtual environments, node_modules, Rust targets, build trees, and other local state continue to work without rebuilding an image.
| Capability | Default behavior |
|---|---|
| Host filesystem | Read-only |
| Selected repository | Read/write at the same absolute path |
| Home directory | Existing files readable; new writes disposable |
/tmp and /run |
Private |
| Network | Shared; disable with --offline |
| Git safety snapshot | Enabled |
| Git push | Blocked; enable with --allow-git-push |
| Runtime footprint | Shell, Git, Bubblewrap, and Python's standard library only |
- Linux with unprivileged user namespaces
- Bash 4.4 or newer
- Bubblewrap 0.12.0 or newer
- Git 2.x
- Python 3.9 or newer
realpath
Bubblewrap releases before 0.12.0 are rejected because they are affected by GHSA-pxhw-h44j-8pfx. See the Bubblewrap installation guide if your distribution does not provide a new enough version.
git clone https://github.com/abraxarion/agent-box.git
cd agent-box
./scripts/check-system.sh./agent-box ~/projects/my-projectThe Bash prompt is marked with a box so it is easy to see when you are inside the sandbox. Start your preferred coding agent from that shell:
codex
# or: claude
# or: piTo launch Claude Code directly:
./agent-box --claude ~/projects/my-projectRunning directly from the checkout is fully supported:
./agent-box /path/to/repositoryTo install for the current user:
./scripts/install.shThis creates:
~/.local/bin/agent-box
~/.local/lib/agent-box/
Ensure ~/.local/bin is in PATH, then run agent-box from anywhere.
Use a custom prefix when needed:
PREFIX=/opt/agent-box ./scripts/install.shHOST SANDBOX
/ ───────────────► / read-only
$HOME ───────────────► $HOME disposable COW
$REPOSITORY ───────────────► same path read/write
host Git metadata snapshot stays outside host-side ZIP
/tmp private tmpfs
/run private tmpfs
/proc private procfs
/dev private device view
The order of the mounts is part of the security design:
- Mount the host root read-only.
- Layer a disposable writable overlay over
$HOME. - Replace
/tmpand/runwith private filesystems. - Bind the selected repository read/write after the private mounts.
- Install Git push guards unless explicitly disabled.
- Enter isolated PID, IPC, UTS, user, and cgroup namespaces.
- Share the network only when
--offlineis not set.
The later repository bind punches through the HOME overlay and any private runtime mount at only the selected path. Repository writes therefore persist even when the project lives inside your home directory or below /tmp.
Before opening the sandbox, agent-box creates an atomic ZIP containing Git metadata. For a repository at:
/home/user/projects/app/
the snapshot is written beside it:
/home/user/projects/app.git-save-20260908-142530.zip
The archive includes Git objects, refs, index data, logs, configuration, hooks, and linked-worktree metadata when applicable. It is created with mode 0600 because those files can contain sensitive remote URLs or local configuration. It does not include arbitrary untracked files or unstaged working-tree content.
Use --no-git-save only when you deliberately do not want this recovery point.
Local Git work remains available in a standard repository:
git status
git diff
git add .
git commit
git branch
git merge
git rebasegit push, git send-pack, and ordinary Git aliases that resolve to either command are rejected unless the sandbox starts with --allow-git-push.
This guard prevents ordinary or accidental Git remote writes. It is not a firewall and does not stop a deliberately hostile process from invoking another Git executable or network client. Use --offline when the sandbox must have no normal external network access.
agent-box [OPTIONS] [REPO] [-- COMMAND_ARGS...]
REPO defaults to the current directory. Arguments after -- are passed unchanged to the selected shell or to Claude Code.
| Option | Default | Effect |
|---|---|---|
REPO |
Current directory | Project directory exposed read/write at its canonical absolute path. Broad paths that contain protected mounts are rejected. |
--claude |
Interactive shell | Start Claude Code directly. |
--git-save-disabled |
Snapshot enabled | Skip the pre-launch Git metadata ZIP. |
--no-git-save |
Snapshot enabled | Alias for --git-save-disabled. |
--allow-git-push |
Push blocked | Disable the Git push guard. |
--offline |
Network shared | Keep Bubblewrap's isolated network namespace. |
--disk-tmp |
Private tmpfs | Mount a private, host-backed session directory at /tmp. |
--dry-run |
Launch | Run preflight and print the Bubblewrap command without executing it. |
--version |
— | Print the version. |
-h, --help |
— | Print help. |
The default shell is $SHELL, falling back to /bin/bash.
Open the current directory:
agent-boxOpen a selected repository:
agent-box ~/projects/my-projectRun a command through the selected shell:
agent-box ~/projects/my-project -- -lc 'codex'Forward arguments to Claude Code:
agent-box --claude ~/projects/my-project -- --model sonnetPreview the generated Bubblewrap command without creating a snapshot:
agent-box --dry-run --no-git-save ~/projects/my-projectUse disk-backed temporary storage for large builds:
agent-box --disk-tmp ~/projects/my-projectAllow Git pushes:
agent-box --allow-git-push ~/projects/my-projectDisable normal external networking:
agent-box --offline ~/projects/my-project| Location or state | Persists? | Reason |
|---|---|---|
| Files changed inside the selected repository | Yes | Real host read/write bind |
Standard-repository .git changes |
Yes | Metadata is inside the repository bind |
| Linked-worktree external Git metadata | Not necessarily | External Git directories remain read-only |
Writes elsewhere in $HOME |
No | Disposable overlay |
~/.claude/settings.json edits |
No | Disposable HOME overlay; available only during the sandbox session |
Default /tmp and /run |
No | Private tmpfs |
--disk-tmp contents |
Normally no | Session directory is removed at exit |
| Pre-launch Git snapshot | Yes | Created on the host before sandbox startup |
- Linux only. The launcher depends on Bubblewrap and Linux namespaces.
- Not secret isolation. Host-readable secrets remain readable.
- Network is all or nothing. There is no domain or port allowlist.
- Linked worktrees are partially supported. Snapshots include their metadata, but Git operations that must update external worktree metadata may fail read-only.
- Cleanup cannot survive everything. A
--disk-tmpdirectory can remain afterSIGKILL, a kernel crash, or power loss. - Git blocking is defense in depth. It targets ordinary Git push paths, not arbitrary network protocols.
See SECURITY.md for the complete threat model.
Follow the Bubblewrap installation guide, then run:
./scripts/check-system.shClaude Code is required only with --claude. The default interactive shell works without it, and any available coding agent can be started from inside that shell.
Restart with --disk-tmp. The host-backed temporary directory remains private to the session and is normally removed when the launcher exits.
The linked worktree's external Git metadata is outside the selected read/write bind. Use a standard checkout for full Git mutation support.
Run the test suite:
./tests/run.shRun static checks:
bash -n agent-box lib/*.sh libexec/git-policy libexec/git-send-pack-block scripts/*.sh tests/*.sh
shellcheck -x agent-box lib/*.sh libexec/git-policy libexec/git-send-pack-block scripts/*.sh tests/*.sh
PYTHONPYCACHEPREFIX=/tmp/agent-box-pycache python3 -m py_compile libexec/git-snapshot.pyMost tests use temporary repositories and fake Bubblewrap executables, so they do not require a live sandbox.
Project layout:
agent-box launcher and orchestration
lib/ CLI, common helpers, Bubblewrap arguments
libexec/ Git snapshot and push-policy executables
scripts/ installation and system checks
tests/ shell-based regression suite
docs/ extended operator documentation
assets/ project artwork
.github/ CI, issue forms, and pull-request template
AGENTS.md maintainer architecture contract
SECURITY.md threat model and reporting policy
See CONTRIBUTING.md before proposing a change. Security-sensitive changes should also follow the invariants in AGENTS.md.
Current agent-box version is 0.1.2
agent-box is available under the MIT License.