# Policy Architecture `safehouse` composes a final sandbox policy from modular profiles, then runs your command under `sandbox-exec`. Policy assembly order: | Layer | Coverage | |-------|----------| | `00-base.sb` | Default deny, helper functions, HOME and selected-workdir definitions | | `10-system-runtime.sb` | macOS runtime binaries, temp dirs, IPC | | `20-network.sb` | Network policy | | `30-toolchains/*.sb` | Apple Toolchain Core, Node, Python, Go, Rust, Bun, Java, PHP, Perl, Ruby | | `40-shared/*.sb` | Shared cross-agent modules | | `50-integrations-core/*.sb` | Core integrations (`container-runtime-default-deny`, `git`, `scm-clis`, `ssh-agent-default-deny`, `worktree-common-dir`, `worktrees`) | | `55-integrations-optional/*.sb` | Opt-in integrations (`--enable=...`) | | `60-agents/*.sb` | Per-agent profile selection by command basename | | `65-apps/*.sb` | Per-app bundle selection (`Claude.app`, `Cursor.app`, `Visual Studio Code.app`) | | Config/env/CLI grants | Trusted `.safehouse` config, env grants, CLI grants, auto-detected app bundle read grant, selected workdir, launch-time active-worktree common-dir grant, and launch-time sibling worktree read grants | | Appended profiles | User profile overlays via `--append-profile` (loaded last) | | Terminal deny rules | Unconditional deny rules emitted after all path grants and appended profiles | ## Ordering Rules Matter Later rules win. If behavior is unexpected, check ordering first. Important implications: - Broad late grants (for example `--add-dirs` or `--enable=wide-read`) can reopen earlier read denies. - Appended profiles (`--append-profile`) are the correct final override layer for must-not-read path denials. - Active linked-worktree common-dir detection happens at launch. - Sibling worktree read grants are a launch-time snapshot. New worktrees created after the process starts are not added to the active policy. ## Path Matchers Safehouse uses standard sandbox matchers: - `literal`: exact path - `subpath`: recursive path - `prefix`: starts-with path - `regex`: regex matcher Ancestor `literal` read grants are intentionally emitted for traversal compatibility. ## Built-In Absolute Path Resolution Built-in `profiles/*` modules are authored with explicit absolute paths such as `/etc`, `/private/etc/localtime`, or `/private/var/select/sh`. During policy rendering, Safehouse checks built-in absolute `literal` and `subpath` entries in `allow file-read*` stanzas and resolves symlink targets on the current host. If a path resolves somewhere else, Safehouse emits: - ancestor `literal` grants for the resolved target path - a matching `literal` or `subpath` read grant for that resolved target This keeps built-in macOS compatibility paths working when the authored path is a symlink but the sandbox needs the real target path to match. Current scope is intentionally narrow: - built-in `profiles/*` content only - absolute `literal` and `subpath` rules only - `allow file-read*` stanzas only Dynamic CLI/config grants already normalize user-provided paths separately. Read/write, metadata-only, `home-*`, `prefix`, and `regex` rules are not auto-expanded by this mechanism today. ## Home Placeholder Replacement `profiles/00-base.sb` uses `HOME_DIR` placeholder token: - `__SAFEHOUSE_REPLACE_ME_WITH_ABSOLUTE_HOME_DIR__` Assembly logic in `/Users/eugene/server/agent-safehouse/bin/lib/policy/render.sh` replaces this with the actual absolute home path. `HOME_DIR` exists so profiles can express narrow home-relative rules through the shared helpers: - `home-literal` - `home-subpath` - `home-prefix` It is not a blanket grant for `$HOME`. Safehouse also generates a `file-read-metadata` block for `/`, the path to `$HOME`, and `$HOME` itself. That metadata-only traversal lets runtimes `stat` or walk toward explicitly allowed home-scoped paths without granting recursive reads of the whole home directory. In practice, that means `stat "$HOME"` can succeed while `ls "$HOME"` and `cat ~/secret.txt` still fail unless a more specific rule grants them. ## Selected Workdir Definition Safehouse renders the selected workdir into `WORK_DIR` so built-in and appended profiles can express reusable workdir-relative rules through: - `workdir-literal` - `workdir-subpath` - `workdir-prefix` Relative arguments must begin with `/`. The helpers also handle `/` as the selected workdir without producing double-slash paths. For example, a final appended policy can deny the root `.env` file after the normal workdir grant: ```scheme (deny file-read* file-write* (workdir-literal "/.env")) ``` When `--workdir=` disables automatic workdir access, `WORK_DIR` is defined as `#f`. A policy that tries to use a `workdir-*` helper then fails to compile rather than deriving a path from an empty string. ## Terminal Deny Rules To prevent accidentally granting access to some critical files Safehouse unconditionally emits a final deny block as the very last section of every generated policy. Since this block is always last, it wins over any earlier write grant regardless of how the workdir, additional profiles, etc are configured. Currently this is used to protect the workdir config file (`/.safehouse`) from being modified from inside the sandbox. To opt out pass `--allow-workdir-config-writes`. See also: [Bin Architecture](/docs/bin-architecture)