Agent Safehouse is a macOS sandbox wrapper for coding agents (Claude, Cursor, Aider, Gemini, etc.) built on sandbox-exec.
Policy model is strict by default: start from (deny default), then add explicit allow rules via layered .sb profiles.
Core sandbox wrapper is pure Bash + Sandbox Profile Language (.sb) and has no build step. Repo also includes a VitePress docs site and Cloudflare deploy tooling (pnpm + Wrangler).
- Use
rgfor text search. - Use
fdfor file listing/find. - Make least-privilege policy edits; avoid broad
subpathgrants unless required. - Do not hand-edit
dist/*; editbin/andprofiles/, then regenerate dist artifacts. - If you change any
.sbfile or policy assembly/runtime logic (bin/safehouse.sh,bin/lib/*.sh), run./scripts/generate-dist.sh. - Run tests with
./tests/run.shon macOS withsandbox-exec, outside any existing sandbox. Use./tests/run.sh e2efor the tmux-driven startup/prompt-roundtrip suite for agent TUIs. - If tests cannot run because the current session is already sandboxed, state that explicitly and continue with static validation.
# Generate policy only (prints temp policy path)
./bin/safehouse.sh [--add-dirs-ro=...] [--add-dirs=...] [--enable=...] [--append-profile=...]
# Run command in sandbox
./bin/safehouse.sh [policy opts] -- <command> [args...]
./bin/safehouse.sh --stdout
# Explain effective workdir/grants/profile selection (debugging)
./bin/safehouse.sh --explain --stdout
# Trust and load <workdir>/.safehouse config (disabled by default)
./bin/safehouse.sh --trust-workdir-config --stdout
# Validate behavior
./tests/run.sh
./tests/run.sh e2e
# Regenerate committed dist artifacts
./scripts/generate-dist.sh./scripts/generate-dist.sh updates these deterministic outputs:
dist/safehouse.sh
dist/ is for consumers: it provides ready-to-run packaged artifacts, especially dist/safehouse.sh, a single executable containing the assembled policy + runtime shell logic.
Authoring source of truth is bin/ and profiles/; make functional changes there, then regenerate dist/.
Agents should usually avoid loading dist/ files into context unless validating generated distribution output or debugging packaging/compression regressions.
Policy concatenation order in bin/safehouse.sh/bin/lib/*.sh:
profiles/00-base.sbprofiles/10-system-runtime.sbprofiles/20-network.sbprofiles/30-toolchains/*.sbprofiles/40-shared/*.sbprofiles/50-integrations-core/*.sb(container-runtime-default-deny,git,scm-clis,ssh-agent-default-deny,worktreesalways on)profiles/55-integrations-optional/*.sb(--enable, withelectronimplyingmacos-gui; keychain injected selectively)profiles/60-agents/*.sbprofiles/65-apps/*.sb- CLI path grants:
--add-dirs-rothen--add-dirs - Workdir grant (omitted if
--workdirexplicitly empty) --append-profileoverlays last
Later rules win. Check ordering first when behavior is unexpected.
- Profiles are authored as modular sections by stage prefix (
00,10,20,30,40,50,55,60,65). - Assembly is deterministic: stage order is fixed, and files within each stage directory are concatenated in lexicographic order (
find ... | sort). - Declare dependency metadata with
$$require=path/to/profile.sb[,path/to/other.sb]$$only when you need implicit optional integration injection. $$require=...$$is machine-read by policy assembly;;; Requires: ...comments are human-facing documentation only.- Keep each
.sbfile standalone for its concern: include complete rules for that capability in the file, avoid splitting one feature across many files without an explicit dependency. - New profile checklist: add standard header (
Category/description/Source), add tests intests/policy/integrations/or the relevant topic folder, and run./scripts/generate-dist.sh.
00-base.sbincludesHOME_DIRplaceholder__SAFEHOUSE_REPLACE_ME_WITH_ABSOLUTE_HOME_DIR__; replaced at assembly time inbin/lib/policy/render.sh.- Ancestor directory
literalread grants (up to/) are intentionally generated byemit_path_ancestor_literals()for directory traversal compatibility. .sbmatcher semantics:literal(exact),subpath(recursive),prefix(starts-with),regex.
- Tests live under
tests/policy/,tests/surface/, andtests/e2e/, with shared helpers intests/test_helper.bash. ./tests/run.shrunspolicy + surfaceby default,./tests/run.sh e2eruns the tmux-driven startup/prompt-roundtrip suite for agent TUIs, and./tests/run.sh allruns every suite.- If adding policy behavior, add/update Bats coverage for the public contract or security boundary that changed.
- Keep
.sbheader comments and any#safehouse-test-id:*#markers used by ordering/structure assertions. - CI runs macOS tests and auto-regenerates/commits dist artifacts on relevant changes.
--append-profileis the final override layer; deny rules there take precedence.- Keep
/System/Volumes/Data/...aliases only when a concrete integration requires them. - Verify behavior with tests (when runnable) and regenerated dist artifacts before finalizing changes.