The Cipher

"The password is… Valley Forge. Or in our case, sandpit.toml."

Config Search Order

sandpit looks for its config in this order:

  1. --config <path> — explicit path via CLI flag
  2. --config ./sandpit.toml — reviewed project-local configuration
  3. ~/.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

Write rules

Read rules

Enforcement: In DYLD mode, file rules are enforced by hooking open() / openat(). In seatbelt mode, block_read and block_write are kernel-enforced (but allow_write is 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