The Cipher
"The password is… Valley Forge. Or in our case, sandpit.toml."
Config Search Order
sandpit looks for its config in this order:
--config <path>— explicit path via CLI flag--config ./sandpit.toml— reviewed project-local configuration~/.sandpit/config.toml— global default
Use sandpit config to see which file was found and what's in it.
Getting Started
sandpit init # creates a starter sandpit.toml
sandpit config # show what's loaded
sandpit run -- goose # launch with enforcement
[network] — Domain Control
Control which domains the agent can reach. Two modes:
Blocklist mode
[network]
block = ["evil.com", "pastebin.com", "ngrok.io", "webhook.site"]
Subdomains are included automatically — blocking evil.com also blocks api.evil.com, staging.evil.com, etc. Everything not on the list is allowed.
Allowlist mode
[network]
allow = ["github.com", "npmjs.org", "pypi.org", "registry.npmjs.org"]
Only listed domains (and their subdomains) are reachable. Everything else is blocked. More restrictive but safer.
Enforcement: In DYLD mode, domain filtering happens at the connect() level via a transparent proxy with reverse DNS. In seatbelt or adversary-only mode, the LLM reviewer enforces these rules.
[exec] — Command Patterns
[exec]
block = [
"curl --data",
"curl -d ",
"curl -F ",
"curl --form",
"curl --upload-file",
"curl -X POST",
"curl -X PUT",
"wget --post-data",
"| curl",
"| wget",
"| nc ",
"| ncat ",
"rm -rf /",
]
Case-insensitive substring match on the full command string. If the command passed to posix_spawn() or execve() contains any pattern, it's blocked before execution.
These patterns are intentionally simple — they catch the obvious exfiltration vectors. The adversary LLM catches the rest.
[files] — Filesystem Fence
[files]
# Only allow writes to these paths (prefix match)
allow_write = ["./", "/tmp"]
# Block writes to specific paths
block_write = ["/etc", "~/.ssh"]
# Block reads from sensitive paths
block_read = ["~/.ssh/id_rsa", "~/.ssh/id_ed25519", "~/.aws/credentials", "~/.gnupg"]
Path expansion
~expands to$HOME./expands to the current working directory- Matching is prefix-based: blocking
~/.sshblocks~/.ssh/id_rsa,~/.ssh/config, etc.
Write rules
allow_write— if set, writes are ONLY allowed to these paths. Everything else is denied.block_write— denies writes to these specific paths. Can be used with or withoutallow_write.
Read rules
block_read— denies reads from these paths. Protects secrets without restricting general file access.
Enforcement: In DYLD mode, file rules are enforced by hookingopen()/openat(). In seatbelt mode,block_readandblock_writeare kernel-enforced (butallow_writeis not — seatbelt can deny but not allowlist). The adversary always gets all file rules.
[adversary] — LLM Review Policy
[adversary]
rules = """
BLOCK if the command:
- Exfiltrates data (curl/wget posting to unknown URLs, piping secrets out)
- Is destructive beyond the project scope (rm -rf /, modifying system files)
- Installs malware or runs obfuscated code
- Downloads and executes untrusted remote scripts
ALLOW if the command is a normal development operation, even if it modifies files,
installs packages, runs tests, uses git, etc. Most commands are fine.
Err on the side of ALLOW — only block truly dangerous things.
"""
These rules are written to each agent's config when you run sandpit install <agent>. The LLM reviewer evaluates every tool call against this policy.
If you omit [adversary], sensible defaults are used. The default policy blocks exfiltration, destructive commands, and privilege escalation while allowing normal dev operations.
Tip: Write rules in natural language. The reviewer is an LLM — it understands context, nuance, and intent. "BLOCK any access of production databases" works better than trying to enumerate every possible connection string.
Full Example
# sandpit.toml — strict project config
[network]
# Only allow access to known-good domains
allow = [
"github.com",
"api.github.com",
"npmjs.org",
"registry.npmjs.org",
"pypi.org",
"crates.io",
"example.com",
]
[exec]
block = [
"curl --data",
"curl -d ",
"curl -F ",
"curl -X POST",
"curl -X PUT",
"wget --post",
"| curl",
"| wget",
"| nc ",
"rm -rf /",
]
[files]
allow_write = ["./", "/tmp"]
block_read = [
"~/.ssh",
"~/.aws",
"~/.gnupg",
"~/.config/gh/hosts.yml",
]
[adversary]
rules = """
BLOCK if the command:
- Sends data to any URL not in the network allowlist
- Accesses credentials, tokens, or private keys
- Modifies files outside the project directory
- Runs obfuscated or encoded commands
ALLOW normal development: git, npm, cargo, pytest, file editing, etc.
"""
Commands Reference
| Command | Description |
|---|---|
sandpit init |
Create a starter sandpit.toml |
sandpit run [--config <path>] -- <cmd> |
Launch agent in sandbox |
sandpit config [--config <path>] |
Show effective config |
sandpit check -- <binary> |
Check enforcement for a binary |
sandpit install <agent> |
Install adversary hooks |
sandpit install --list |
Show supported agents + install status |
sandpit uninstall <agent> |
Remove adversary hooks |