claude-code with --dangerously-skip-permissions, minus the danger
- Rewrite mount architecture: direct ~/.claude bind replaces old ~/.claudebox symlink - Per-project ~/.claude/projects/ isolation via SHA-256[:16] of canonical git root - Worktree-aware canonical root resolution (git rev-parse --git-common-dir) - --gc flag to remove stale instance dirs - /bin/sh symlink fix for git hooks - Auth credentials moved to ~/.claudebox/.credentials.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .planning | ||
| CLAUDE.md | ||
| claudebox.sh | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
| test-gc.sh | ||
claudebox
Run Claude Code inside a bubblewrap sandbox with an allowlisted environment, explicit filesystem mounts, and a minimal PATH.
SSH keys, GPG/age secrets, cloud tokens, and Tailscale state stay completely invisible to the AI agent. If a secret is accessible inside the sandbox, it's a bug.
Quick start
nix run git+https://git.toph.so/toph/claudebox
Or add to your flake:
{
inputs.claudebox.url = "git+https://git.toph.so/toph/claudebox";
}
Then add inputs.claudebox.packages.${system}.default to your environment.systemPackages or home-manager packages.
What it does
- Starts Claude Code inside a bwrap namespace with
--clearenv - Only allowlisted env vars enter the sandbox (HOME, PATH, TERM, EDITOR, LANG, ANTHROPIC_API_KEY if set)
- Mounts CWD read-write, Nix store read-only, everything else is tmpfs
- Provides
nix shelland comma (, <tool>) so Claude can install tools on demand - Injects a SANDBOX.md so Claude knows it's sandboxed and how to get tools
- Pre-configures git identity and safe.directory from host
Flags
| Flag | Description |
|---|---|
--yes, -y |
Skip the env audit and launch immediately |
--dry-run |
Print the bwrap command without executing |
--check |
Verify prerequisites and exit |
--shell |
Drop into a bash shell instead of Claude Code |
--gc |
Remove stale per-project instance dirs and exit |
-- |
Pass remaining args to Claude Code |
Extra env vars
Pass additional host variables into the sandbox:
CLAUDEBOX_EXTRA_ENV=MY_VAR,OTHER_VAR claudebox
How it works
~/.claudebox/ # persistent config dir (host)
├── SANDBOX.md # managed by claudebox, overwritten each launch
├── history.jsonl # conversation history
├── .credentials.json # Claude Code credentials (if present)
└── projects/
└── <16-char-hex>/ # per-project instance dir (keyed by canonical git root)
└── project-root # records the canonical path for this instance
Inside the sandbox:
~/.claude → bind-mounted from host (plugins, skills, hooks, MCP all visible)
~/.claude/projects → bind-mounted from ~/.claudebox/projects/<hash>/ (per-project isolation)
~/.claude/history.jsonl → bind-mounted from ~/.claudebox/history.jsonl
~/.claude/SANDBOX.md → bind-mounted from ~/.claudebox/SANDBOX.md
Each project gets an isolated ~/.claude/projects/ directory inside the sandbox, so conversation history and project state are separated per repo. Git worktrees share the same instance dir as their main worktree.
Requirements
- NixOS or Nix with flakes enabled
- User namespaces (enabled by default on NixOS)
License
MIT