Native macOS sandboxing for Agents

sandbox-exec policies for coding agents — deny-first, composable, thoroughly tested.

Pure Bash Zero dependencies Open source

Live dangerously, but do it safely.

rm -rf / goes nowhere
No full read access — only what the agent needs
Write access via explicit allow-list only
Sane, dev-friendly defaults
Fully customizable for your setup
Every agent ships the same escape hatch
$ claude --dangerously-skip-permissions
$ codex --dangerously-bypass-approvals-and-sandbox
$ amp --dangerously-allow-all

Successfully contained agents clunkers

All major agents work perfectly inside Safehouse. They just can't touch anything outside it.

Claude CodeClaude Code
CodexCodex
OpenCodeOpenCode
AmpAmp
CursorCursor
AiderAider
Gemini CLIGemini CLI
ClineCline
Augment CodeAugment Code
DroidDroid
PiPi
+yours

Why Safehouse?

Coding agents run as your user. They inherit every permission you have — read your SSH keys, browse your credentials, write anywhere on disk. The built-in permission prompts help, but skipping them (which most people do) means the agent has the same access as sudo -u $USER. Safehouse uses macOS sandbox-exec to enforce kernel-level restrictions that the agent process cannot bypass, even if it tries.

No read access to your entire home. Only what the agent needs.

Agents inherit your full user permissions. Safehouse flips this to deny-first — nothing is readable unless explicitly granted.

Without Safehouse

  • ~/ full access
  • ~/.ssh/id_ed25519 full access
  • ~/.aws/credentials full access
  • ~/other-repos/ full access
  • ~/Documents/ full access

With Safehouse

  • ~/my-project/ read/write
  • ~/shared-lib/ read-only
  • ~/.ssh/id_ed25519 denied
  • ~/.aws/credentials denied
  • ~/other-repos/ denied

What's allowed vs. denied

Allowed

  • System path reads (/usr, /bin, /System)
  • Full process exec/fork (agents spawn deep trees)
  • Scoped toolchain caches (~/.cargo, ~/.npm, etc.)
  • Scoped agent config dirs (~/.claude, ~/.cursor, etc.)
  • Keychain and Security framework (always-on)
  • Shell startup files (~/.zshenv, ~/.zprofile)
  • Network (fully open by default)
  • SSH config and known_hosts (not private keys)
  • Temp directories (/tmp, /var/folders)

Denied

  • SSH private keys (~/.ssh/id_*)
  • Browser profiles (cookies, session data)
  • Raw device access (/dev/disk*, /dev/bpf*)
  • Setuid/setgid binaries (sudo, passwd)
  • Everything not explicitly allowed (deny-first)
  • AWS credentials (optional, via overrides)
  • Directories outside your project and toolchains

Necessary evils

Access that looks scary but is genuinely required. Narrowest scope possible.

Getting started

Clone the repo, add an alias, and wrap your agent command. That's it — no build step, no dependencies.

# 1. Clone the repo
git clone https://github.com/eugene1g/agent-safehouse.git ~/.local/share/agent-safehouse

# 2. Add an alias (add this to ~/.zshrc or ~/.bashrc)
alias safehouse="$HOME/.local/share/agent-safehouse/bin/safehouse"

# 3. Run any agent inside Safehouse
cd ~/projects/my-app
safehouse -- claude --dangerously-skip-permissions

Safehouse automatically grants read/write access to the current working directory and read access to your installed toolchains. Everything else — home directory, SSH keys, cloud credentials, other repos — is denied by the kernel.

Use aliases to be safe by default

Add these aliases to your shell config and every agent runs inside Safehouse automatically — you don't have to remember. To run an agent without the sandbox, you deliberately type command claude to bypass the alias and invoke the real binary. This inverts the default: safety is automatic, and running unprotected requires a conscious choice.

# ~/.zshrc or ~/.bashrc
# Point this to your clone location
export SAFEHOUSE="$HOME/.local/share/agent-safehouse/bin/safehouse"

# Sandboxed — the default. Just type the command name.
alias claude='$SAFEHOUSE -- claude --dangerously-skip-permissions'
alias codex='$SAFEHOUSE -- codex --dangerously-bypass-approvals-and-sandbox'
alias amp='$SAFEHOUSE -- amp --dangerously-allow-all'
alias gemini='NO_BROWSER=true $SAFEHOUSE -- gemini --yolo'
alias aider='$SAFEHOUSE -- aider --yes-always'
alias cursor='$SAFEHOUSE -- cursor-agent --force'

# Unsandboxed — use `command` to bypass the alias and run the real binary.
# command claude              — plain interactive session
# command claude <flags>      — pass any flags directly

# Extras — Docker, read-only reference paths, etc.
alias claude-docker='$SAFEHOUSE --enable=docker -- claude --dangerously-skip-permissions'

Usage

# Run Claude in the current repo (CWD always gets read/write)
cd ~/projects/my-app
safehouse -- claude --dangerously-skip-permissions

# Grant extra writable directories
safehouse --add-dirs=/tmp/scratch:/data/shared -- claude

# Grant read-only access to reference code
safehouse --add-dirs-ro=/repos/shared-lib -- aider

# Enable Docker socket access (off by default)
safehouse --enable=docker -- docker ps

# Inspect the generated policy without running anything
safehouse --dry-run -- claude

Just grab the policy file

All profiles flattened into a single .sb file. Run directly with sandbox-exec.

Copy, paste, and go

Save the file locally, then replace the two template placeholders.

Open policy file on GitHub

# After saving the file locally, replace the template paths
sed -i '' 's|/private/tmp/agent-safehouse-static-template/home|'$HOME'|g' agent-safehouse-policy.sb
sed -i '' 's|/private/tmp/agent-safehouse-static-template/workspace|'$PWD'|g' agent-safehouse-policy.sb

# Run any agent under the policy
sandbox-exec -f agent-safehouse-policy.sb claude --dangerously-skip-permissions