Skip to main content
Agent Safehouse is built with Bash and Sandbox Profile Language (.sb) policy modules. Contributions should maintain least-privilege security boundaries while prioritizing agent productivity and developer experience.

Project Layout

bin/ and bin/lib/

Runtime CLI and policy assembly logic (Bash)

profiles/

Authored policy modules (.sb), organized by numeric stage

tests/

Policy behavior tests and helpers

scripts/generate-dist.sh

Deterministic packaging pipeline

dist/

Generated distribution artifacts (do not edit directly)

docs/

VitePress documentation site and Cloudflare deploy tooling
Never hand-edit dist/* files. Make functional changes in bin/ and profiles/, then regenerate dist/ with ./scripts/generate-dist.sh.

Development Setup

To test your local changes (not an installed safehouse on PATH):
1

Add shell override

Add this to your ~/.zshrc or ~/.bashrc:
~/.zshrc
# Agent Safehouse local dev override
export AGENT_SAFEHOUSE_REPO="$HOME/dev/agent-safehouse"
safehouse() { "$AGENT_SAFEHOUSE_REPO/bin/safehouse.sh" "$@"; }
2

Reload shell

source ~/.zshrc
3

Verify override is active

type -a safehouse
You should see your function listed first, before any installed binary.

Contribution Rules

  • Do not hand-edit dist/*
  • Make functional changes in bin/ and profiles/, then regenerate dist/ when required
  • Keep policy changes least-privilege; avoid broad grants unless needed
  • Preserve stage ordering semantics (later rules win)
  • Keep each .sb module standalone for its capability

Contribution Philosophy

Agent Safehouse balances security and developer experience:
  • Follow least-privilege boundaries, but prioritize agent productivity
  • Prefer the narrowest rule that unblocks real workflows
  • If adding access to sensitive paths/integrations, document why it is needed and why narrower alternatives were insufficient
  • Avoid policy churn that improves theoretical security but breaks common agent/toolchain behavior without clear benefit

Authoring .sb Profiles

File Organization

Profiles are organized by numeric stage prefix:
StagePurposeExamples
00Base policy structure00-base.sb
10System runtime fundamentals10-system-runtime.sb
20Network access20-network.sb
30Toolchainsnode.sb, python.sb, rust.sb
40Shared utilitieshttp-clients.sb
50Core integrations (always on)git.sb, scm-clis.sb
55Optional integrationsdocker.sb, ssh.sb, 1password.sb
60Agent profilesclaude-cli.sb, cursor.sb
65App profilesclaude-desktop.sb
Later rules win. A deny rule in stage 60 overrides an allow rule from stage 10.

Profile Header Template

Every .sb file should start with:
profiles/55-integrations-optional/docker.sb
;; Category: Optional Integration
;; Integration: Docker Desktop
;; Description: Docker CLI, daemon socket, VM network access
;; Source: profiles/55-integrations-optional/docker.sb

#safehouse-test-id:docker#

(allow mach-lookup
  (global-name "com.docker.vmnetd")
)
Components:
  • Category: Profile classification (Base, System Runtime, Toolchain, Integration, Agent, App)
  • Integration/App: Human-readable name
  • Description: Brief summary of what access is granted
  • Source: Relative path to this file
  • Test ID marker (optional): #safehouse-test-id:*# for ordering tests

Dependency Metadata

Use $$require=path/to/profile.sb$$ when implicit optional integration injection is needed:
profiles/60-agents/cursor.sb
;; Requires: electron (implicit via --enable=electron)
$$require=profiles/55-integrations-optional/electron.sb$$
$$require=...$$ is machine-read by policy assembly. ;; Requires: comments are documentation only.

Rule Snippets

;; Exact single-path allow
(allow file-read*
  (literal "/Users/alice/.gitconfig")
)
Prefer literal over subpath whenever possible. Recursive directory grants expand the attack surface.

Local Validation

1

Run policy tests

Validate behavior (macOS only, must be outside an existing sandbox):
./tests/run.sh
2

Regenerate dist artifacts

Required after profile or runtime changes:
./scripts/generate-dist.sh
3

Verify generated output

Check that dist/ artifacts updated correctly:
git status
git diff dist/
If tests cannot run because your session is already sandboxed, call that out in your PR and include static validation details instead.

Required Steps by Change Type

Change TypeSteps Required
Profiles or runtime (profiles/*.sb, bin/safehouse.sh, bin/lib/*.sh)1. Update/add tests
2. Run ./scripts/generate-dist.sh
3. Include regenerated dist/ files in PR
Tests only (tests/**)1. Run ./tests/run.sh
Docs only (docs/**, README.md)No dist regeneration needed

Adding Tests

When adding new policy behavior:
1

Add test section

Create or update a file in tests/sections/:
tests/sections/20-integrations.sh
run_section_docker() {
  section_begin "Docker Integration"
  
  assert_allowed_if_exists "$POLICY_DOCKER" \
    "connect to Docker socket" \
    "/var/run/docker.sock" \
    docker ps
  
  assert_denied "$POLICY_DEFAULT" \
    "Docker socket denied without --enable=docker" \
    docker ps
}

register_section run_section_docker
2

Use existing helpers

Leverage helpers from tests/lib/common.sh:
  • assert_allowed / assert_denied
  • assert_policy_contains / assert_policy_not_contains
  • assert_policy_order_literal
3

Validate behavior

./tests/run.sh
Prefer precise tests for ordering and policy-shape regressions when changing assembly logic or module dependencies.

Pull Request Checklist

Before submitting:
  • Explain what changed and why
  • Describe security/least-privilege impact (especially for new allow rules)
  • Include test evidence (./tests/run.sh output) or state why tests were not runnable
  • Confirm whether dist/ was regenerated and committed (when required)
  • Verify CI passes on your branch
## Summary

Adds optional kubectl integration for Kubernetes CLI access.

## Changes

- `profiles/55-integrations-optional/kubectl.sb`: Allow kubectl binary execution, kubeconfig read, and API server network access
- `tests/sections/20-integrations.sh`: Added kubectl canary tests
- `dist/`: Regenerated all artifacts

## Security Impact

- Grants read access to `~/.kube/config` (contains cluster credentials)
- Allows outbound network connections (required for cluster API calls)
- **Opt-in only**: Requires `--enable=kubectl`

## Testing

Design Guidance for Reviews

When reviewing contributions:
  • Prefer narrow path matchers (literal > subpath when possible)
  • Avoid introducing new sensitive-path exposure unless justified
  • Keep optional integrations opt-in unless required by selected profiles
  • Treat policy assembly order as a first-class behavior constraint

Reference Material

For Sandbox Profile Language examples:
  • Primary source: Authored modules under profiles/ (style/source-of-truth for this project)
  • Assembled examples: dist/profiles/safehouse.generated.sb and dist/profiles/safehouse-for-apps.generated.sb
  • macOS built-in profiles: /System/Library/Sandbox/Profiles/ and /usr/share/sandbox/
  • External prior art: Listed in README.md under Reference & Prior Art

Next Steps

Testing

Run the test suite and validate behavior

Debugging

Learn how to diagnose sandbox denials

Build docs developers (and LLMs) love