Writing .sb Files
Sandbox profiles use Sandbox Profile Language (SBPL), a Scheme-like DSL for macOS Seatbelt. Safehouse profiles are modular .sb files that follow a consistent structure.
Basic Structure
Every Safehouse profile includes:- Header comment (category, description, source path)
- Dependency metadata (optional
$$require=declarations) - Sandbox rules (allow/deny operations with matchers)
Operations
File Operations
| Operation | Description | Example |
|---|---|---|
file-read* | Read file contents, metadata, and list directories | (allow file-read* (literal "/tmp/foo")) |
file-read-metadata | Read file metadata (stat) only, no contents | (allow file-read-metadata (literal "/etc/hosts")) |
file-write* | Write, create, delete files | (allow file-write* (subpath "/tmp")) |
file-read* file-write* | Combined read/write access | (allow file-read* file-write* (home-subpath "/.myapp")) |
Network Operations
| Operation | Description | Example |
|---|---|---|
network-outbound | Establish outbound TCP/UDP connections | (allow network-outbound (remote ip)) |
network-inbound | Accept inbound connections (rare) | (allow network-inbound (local tcp "*:8080")) |
IPC Operations
| Operation | Description | Example |
|---|---|---|
mach-lookup | Connect to mach services | (allow mach-lookup (global-name "com.apple.SecurityServer")) |
mach-register | Register mach services (rare) | (allow mach-register (global-name-regex #"^com\\.myapp\\.")) |
ipc-posix-shm-* | POSIX shared memory access | (allow ipc-posix-shm-read-data (ipc-posix-name "com.apple.AppleDatabaseChanged")) |
IOKit Operations
| Operation | Description | Example |
|---|---|---|
iokit-open | Open IOKit user clients (GPU, hardware access) | (allow iokit-open (iokit-user-client-class "IOSurfaceRootUserClient")) |
Matchers
File Path Matchers
literal (exact path match)
literal (exact path match)
Matches a single file or directory exactly. Safest and most precise.Use case: Grant access to a specific config file or single directory.
When used on a directory,
literal grants access to list the directory’s immediate children (via readdir()), but not recursive access to subdirectories or their files.subpath (recursive directory match)
subpath (recursive directory match)
Matches a directory and all files/subdirectories recursively under it.Use case: Grant full access to a project directory or toolchain installation.
prefix (starts-with match)
prefix (starts-with match)
Matches paths that start with the given string.This matches Use case: Match versioned binaries or symlinks with a common prefix.
/usr/local/bin/node, /usr/local/bin/node-v18, /usr/local/bin/nodejs, etc.Less common in Safehouse profiles. Use
literal or subpath when possible for clarity.regex (regular expression match)
regex (regular expression match)
Matches paths using a POSIX Extended Regular Expression.Use case: Match dynamic paths with variable components (e.g., usernames, version numbers).
Helper Macros (Safehouse-Specific)
Safehouse provides helper macros defined in00-base.sb:
Always use
home-* macros instead of hardcoding /Users/alice. The HOME_DIR placeholder is replaced at runtime with the actual home directory path.Mach Service Matchers
global-name (exact service name)
global-name (exact service name)
Matches a specific mach service name.Use case: Grant access to specific macOS system services.
global-name-regex (regex service name)
global-name-regex (regex service name)
Matches mach service names using a regex pattern.Use case: Match dynamic service names (e.g., Chromium crashpad child processes).
Real Examples
Example 1: Aider Agent Profile
Fromprofiles/60-agents/aider.sb:
Breakdown:
home-prefix "/.local/bin/aider"matches/Users/alice/.local/bin/aider,/Users/alice/.local/bin/aider-install, etc.home-literalgrants access to specific config files.home-subpathgrants recursive access to cache and data directories.
Example 2: Keychain Integration
Fromprofiles/55-integrations-optional/keychain.sb:
Breakdown:
file-read-metadatagrants stat-only access to parent directories (no content listing).mach-lookupgrants access to security-related mach services.ipc-posix-shm-*grants shared memory access for keychain change notifications.
Example 3: Electron Integration (with Dependency)
Fromprofiles/55-integrations-optional/electron.sb:
Key features:
$$require=55-integrations-optional/macos-gui.sb$$auto-injectsmacos-gui.sbwhenelectron.sbis enabled.#safehouse-test-id:*#markers are used by test assertions to verify rule structure/order.global-name-regexmatches dynamic crashpad service names.
Authoring Checklist
When creating a new profile:Choose the right stage prefix
| Stage | Purpose |
|---|---|
30-toolchains/ | Language runtimes, package managers |
40-shared/ | Cross-agent resources |
50-integrations-core/ | Always-on integrations (git, gh) |
55-integrations-optional/ | Opt-in integrations (docker, ssh, keychain) |
60-agents/ | Agent-specific grants |
65-apps/ | App bundle-specific grants |
Declare dependencies (if needed)
$$require= for implicit optional integration injection. For documentation-only dependencies, use comments:Write sandbox rules
Prefer
literal > subpath > prefix > regex (narrowest first).Use home-* macros instead of hardcoded /Users/alice.Add test markers (optional)
assert_policy_order_literal and assert_policy_contains in tests.Best Practices
Narrow Grants
Use
literal for single files/directories. Only use subpath when you need full recursive access.Use Home Macros
Always use
home-subpath, home-literal, home-prefix instead of hardcoded /Users/alice.Document Why
Explain why a grant is needed, not just what it does. Future maintainers will thank you.
Test Behavior
Add
assert_allowed and assert_denied tests for every new profile. Verify both positive and negative cases.Debugging Sandbox Denials
Watch Live Denials
Recent Denial History
Kernel-Level Stream
Run these commands before invoking
safehouse to capture denials as they happen.Reference Material
Official Sources
- macOS built-in profiles:
/System/Library/Sandbox/Profiles/and/usr/share/sandbox/ - Safehouse profiles:
profiles/(primary style reference) - Assembled examples:
dist/profiles/safehouse.generated.sbanddist/profiles/safehouse-for-apps.generated.sb
Matcher Preference Order
literal— Exact path match (safest, most precise)subpath— Recursive directory match (broader, use when needed)prefix— Starts-with match (less common, use for versioned paths)regex— Regular expression match (highest complexity, use sparingly)
Next Steps
Policy Architecture
Understand assembly order, profile layers, and dependency system.
Customization
Learn about machine-local overrides and
--append-profile for runtime customization.