Agent Safehouse: deny-first macOS sandboxing for coding agents built on sandbox-exec
Sandbox your local AI agents so they can read/write only what they need
At a glance
- What is it?
- Agent Safehouse wraps LLM coding agents in a composable sandbox-exec policy on macOS, starting from deny-all and adding only the file access an agent needs. It is a hardening layer for developers running Claude Code, Codex, Amp or similar tools, not a defence against a determined attacker.
- Who is it for?
- Adopt Agent Safehouse if you run coding agents on macOS with broad permissions and want a default-deny file policy you can extend per machine. Do not adopt it if you need a hard security boundary against a determined attacker, or if your team works on Linux, where the project itself points to bubblewrap, firejail and Landlock-based tools instead.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem: agents that can read your whole home directory
Coding agents are typically launched with the permissions of the developer who starts them. On macOS that means the process can read SSH keys, cloud credentials, browser profiles and every unrelated repository in the home directory. Agent Safehouse exists to narrow that surface. Its stated design goal is practical least privilege: start from deny-all, allow only what the agent needs to do useful work, and keep the developer workflow productive. The README is explicit about the intended audience and the intended claim. It is aimed at people running LLM coding agents locally on macOS, and it describes itself as a hardening layer rather than a perfect security boundary against a determined attacker. That sentence should shape how you evaluate everything else. A tool that reduces the blast radius of an agent that wanders outside its project is useful. A tool that claims to contain a motivated attacker with code execution is a different product, and this one does not make that claim.
How the policy is assembled: deny-first, composable, with appended profiles loading last
The mechanism is macOS sandbox-exec driven by policy profiles written in the Scheme-like sandbox profile language. Profiles are composable, and the ordering matters. Appended profiles load last, which means their rules can narrow earlier defaults, including the built-in home exceptions. That is the escape hatch for anyone who wants the default behaviour tightened rather than loosened. The default posture is narrower than the phrase "home access" suggests. HOME_DIR is used to render precise home-relative rules in the assembled policy, and by itself it does not grant recursive read access to the home directory. What you get by default is metadata-only traversal on /, the path to $HOME, and $HOME itself, so runtimes can probe paths that are explicitly allowed. Directory-root reads are granted for ~/.config and ~/.cache so tools can discover XDG locations, plus a few explicit home-scoped files and directories from always-on profiles, such as git and ssh metadata and shared agent instruction folders. The practical consequence the README gives is that stat "$HOME" can succeed while ls "$HOME" and cat ~/secret.txt still fail unless a more specific rule grants that path. WORK_DIR works the same way: it exposes the selected workdir to built-in and appended rules through the workdir-literal, workdir-subpath and workdir-prefix helpers. Relative helper arguments begin with /, which lets a reusable profile target something like <workdir>/.env without hardcoding an absolute project path.
Symlink resolution for built-in system paths, and where it stops
Built-in profiles may reference macOS compatibility paths such as /etc, /private/etc/resolv.conf or /private/etc/localtime. At policy render time, Safehouse resolves built-in absolute paths from allow file-read* rules and emits matching grants for the real target path when the authored path is a symlink. This keeps host-specific system files working without widening the source profiles to recursive /private/etc access, which would be the lazy fix. The scope is deliberately limited, and the README says so: only built-in absolute literal and subpath read grants are covered. User-provided path grants normalize separately, and writable or metadata-only built-in rules are not auto-expanded by this mechanism today. If you write your own profile and hit a symlinked path that the sandbox refuses, that limitation is the first thing to check, because the automatic resolution will not have run for your rule.
Getting it running: Homebrew, the standalone script, and the wrapper pattern
Two install paths are documented. Homebrew is the short one: brew install eugene1g/safehouse/agent-safehouse. The standalone route downloads the release script directly: mkdir -p ~/.local/bin, then curl -fsSL https://github.com/eugene1g/agent-safehouse/releases/latest/download/safehouse.sh -o ~/.local/bin/safehouse, then chmod +x ~/.local/bin/safehouse. For day-to-day use the README recommends keeping machine-specific settings out of project config and putting them in a shell wrapper plus a local appended profile, so the same defaults work across claude, codex, amp or app launchers. The zsh and bash example exports SAFEHOUSE_APPEND_PROFILE pointing at ~/.config/agent-safehouse/local-overrides.sb and defines a safe function that calls safehouse with --add-dirs-ro="$HOME/server" and --append-profile="$SAFEHOUSE_APPEND_PROFILE", passing "$@" through. A second function, safe-claude, calls safe claude --dangerously-skip-permissions "$@". A fish equivalent is given using set -gx and $argv. The example local policy file shows the two rule styles side by side: allow file-read* with home-literal, home-subpath and subpath entries for host-specific exceptions, and a deny file-read* file-write* rule with workdir-literal "/.env" to keep root environment files unavailable even though the workdir is writable. The README's guidance is to use --add-dirs-ro or --add-dirs for normal shared-folder access and reserve --append-profile for machine-local exceptions or final overrides. One more behaviour worth knowing: Git worktrees are auto-detected at launch when the selected workdir itself is a Git worktree root. That worktree gets the shared Git metadata access it needs when its common dir lives outside the selected workdir, and the other existing linked worktrees for that repo become readable by default for cross-tree inspection. That snapshot does not update for already-running processes, so if you create worktrees under a stable parent such as ~/worktrees, the README recommends adding that root explicitly with --add-dirs-ro.
The limitation that matters: it is macOS-only and not a containment boundary
Two constraints are stated plainly and both change who should use this. The first is platform. Safehouse is tailored for macOS because it depends on sandbox-exec. There is no Linux path here. The README instead lists Linux-native alternatives: vetto, which it describes as a zero-daemon kernel sandbox using Landlock LSM and seccomp-bpf on Linux with Seatbelt on macOS and automated PATH shims via vetto enable <agent>; bubblewrap, an unprivileged user namespaces sandboxing utility; firejail, a SUID sandbox with ready-made application profiles; nono.sh, which combines Landlock with seccomp-notify and privilege elevation without restart; and sandlock, a pure-Python sandbox combining Landlock, seccomp-bpf and seccomp user notification that needs no root, containers or C compiler. If your team is on Linux, those are the actual alternatives and the difference is not cosmetic: Landlock and seccomp-bpf are kernel-level enforcement primitives, while sandbox-exec is a macOS facility with its own history and semantics. The second constraint is the threat model. The README's own framing is that this is a hardening layer, not a perfect security boundary against a determined attacker. If an agent is executing untrusted third-party code with the intent of escaping, a policy layer that the same process tree is subject to is not the control you want. The value is in accidental access, prompt-injected instructions that reach for files outside the project, and the general reduction of what a misbehaving agent can touch.
Maintenance, upgrades and the Apache-2.0 terms
The project ships tagged releases, with v0.12.0 dated 2026-09-07, v0.11.1 dated 2026-07-17 and v0.11.0 dated 2026-07-08, and the repository's last push is 2026-09-09. Two macOS CI workflows are referenced in the README badges, one for tests and one for end-to-end TUI tests. Beyond that, the material does not describe a support window, a deprecation policy or a compatibility matrix for macOS versions, so treat upgrade cost as an open question you resolve by reading the release notes for each tag. The licence is Apache-2.0, which is permissive and includes an explicit patent grant; that is a general property of the licence text, not legal advice, and if you are redistributing the script inside a commercial product you should have counsel read the NOTICE and attribution requirements rather than relying on a summary. Practically, the standalone install is a single script fetched by curl, so pinning to a specific release tag rather than latest is the only version control the install method gives you. The Homebrew route inherits whatever the tap serves.
Who this fits, and what to check before you commit
The fit is narrow and clear. You run coding agents on macOS, you have already accepted that they operate with broad file access, and you want that access expressed as an explicit policy you can read and edit. The composable profile model and the append-last ordering give you a real override mechanism rather than a settings file with a fixed set of toggles. The wrapper pattern in the README is the part worth copying: one local overrides file, one function per agent, and shared folders granted through --add-dirs-ro instead of a blanket home grant. The misfit is equally clear. Linux teams should look at the alternatives the project itself names. Anyone treating this as containment against deliberate escape is using it outside its stated scope. And anyone who wants a policy they never have to read should not adopt a deny-first tool, because deny-first means the first time a legitimate workflow breaks, you are the one writing the rule. Before rollout, verify the built-in profile for your specific agent against how you actually launch it, confirm your shared paths resolve through --add-dirs-ro rather than a wider grant, and decide where your local-overrides.sb lives before you write the first exception into it.
Editorial conclusion
Adopt Agent Safehouse if you run coding agents on macOS with broad permissions and want a default-deny file policy you can extend per machine. Do not adopt it if you need a hard security boundary against a determined attacker, or if your team works on Linux, where the project itself points to bubblewrap, firejail and Landlock-based tools instead. Before rolling it out, verify three things: that the built-in profile for your specific agent matches how you actually invoke it, that your shared folders are reachable through --add-dirs-ro rather than a broad home grant, and that your local overrides live in an appended profile so their deny rules load last.
Community notes