pi-guard-sandbox
v0.2.4
Published
A lightweight OS-level guard for Pi: agents stay useful, sensitive paths stay blocked, and writes stay inside the workspace you allow. No container overhead. No workflow drama.
Readme
[!TIP] Two rounds of real-world testing — all passed. Routine bash, file I/O, and git operations feel seamless. Out-of-bounds writes, dangerous commands, and sensitive reads are blocked on contact.
Platform: Linux / WSL. macOS may work but hasn't been tested. Windows is not supported.
Pi Guard is a high-agency, OS-level sandbox for Pi. Let your Agent work freely where it should, block it where it shouldn’t — with real boundary enforcement, interactive permission prompts, and no heavy workflow overhead.
- 🧠 High-agency: full freedom inside the workspace, hard stops only at the boundary
- 🪶 Clean: minimal setup, intuitive config, low-friction daily use
- 🛡️ Hardened: bash runs in a real sandbox, not string-matching theater
- 🤝 Composable: coexists with
pi-tool-displayand other bash-overriding extensions - 🎯 One package, done: no need to stitch together a stack of guardrail tools
Changelog
- 0.2.1 — Sandbox now inherits host environment variables and uses the real
HOME. Fixes tools that read config files or API keys from~/.paths (e.g. Tavily). - 0.2.0 — Network toggle:
/guard non//guard noffto allow or block all outbound network. Default open. Footer shows· network: open|blocked. - 0.1.0 — Initial release: write-boundary protection, read-only / workspace-write modes, sandboxed bash, dangerous command blocking.
1. Installation
System dependencies
| Tool | Purpose | Install |
|------|---------|---------|
| bwrap | Linux process sandbox | sudo apt install bubblewrap |
| socat | Sandbox network proxy | sudo apt install socat |
| rg | File scanning | sudo apt install ripgrep |
Install the extension
From npm
pi install npm:pi-guard-sandbox # global — all projects
pi install -l npm:pi-guard-sandbox # project-local onlyAfter installation, enter your project and run /guard i to initialize.
From this repo
If you've cloned this repository, the extension is already at .pi/extensions/pi-guard/:
cd .pi/extensions/pi-guard && npm installStart Pi, then run /guard i.
For a global install from this repo: copy
.pi/extensions/pi-guard/to~/.pi/agent/extensions/pi-guard/and runnpm installthere.
2. What Guard leaves in your project
Know what you're getting into.
| Item | Description |
|------|-------------|
| .pi/pi-guard.json | Created on init — all Guard configuration |
| footer status line | Persistent mode indicator at the bottom of Pi |
| sandboxed bash | All Agent bash commands run in a sandbox, not directly on the host |
| vendor/ directory | ~1.8 MB — sandbox runtime (forked from @anthropic-ai/sandbox-runtime) |
| .pi/extensions/pi-guard/ | The extension code itself |
Delete
.pi/pi-guard.jsonand run/reloadto disable Guard.
3. Quick start
Start Pi in your project directory:
/guard i → initialize — creates config
/guard r → switch to read-only mode
/guard w → switch to workspace-write (default)Guard takes effect immediately after initialization. The footer shows the current mode.
4. Command reference
| Command | Shortcut | Purpose |
|---------|----------|---------|
| /guard | — | Show status and configuration |
| /guard init | /guard i | Create .pi/pi-guard.json and enable Guard |
| /guard read-only | /guard r | Switch to read-only |
| /guard workspace-write | /guard w | Switch to workspace-write |
| /guard network on | /guard non | Allow all outbound network (default) |
| /guard network off | /guard noff | Block all outbound network |
Note: Even with network open,
ping(ICMP) is unavailable inside the sandbox. Bubblewrap dropsCAP_NET_RAWby default. HTTP/HTTPS (curl, wget, git, etc.) work normally.
5. Mode comparison
| | read-only | workspace-write | |---|---|---| | Read inside workspace | ✅ | ✅ | | Read outside workspace | ✅ (sensitive paths excluded) | ✅ (sensitive paths excluded) | | Write inside workspace | ❌ | ✅ | | Write outside workspace | ❌ | ❌ (requires approval) | | Bash commands | ✅ (no persistent writes) | ✅ (workspace + /tmp writable) | | Bash writes outside workspace | ❌ | ❌ | | Dangerous commands | blocked + approval | blocked + approval |
Sensitive paths (unreadable)
~/.ssh ~/.aws ~/.gnupg ~/.git-credentials
~/.npmrc ~/.pypirc ~/.netrc ~/.env ~/.env.*
What Guard does NOT protect
User-typed !cmd / !!cmd are not guarded. Guard only covers Agent-initiated tool calls.
6. Troubleshooting
| Status | Cause | Action |
|--------|-------|--------|
| Guard: uninitialized | Not yet initialized | Run /guard i |
| Guard: invalid-config | JSON syntax error | Fix .pi/pi-guard.json, then /reload |
| Guard: sandbox-unavailable | Missing system deps or npm install skipped | Follow Section 1 — check bwrap, socat, rg |
7. Configuration: .pi/pi-guard.json
/guard init generates this file. You can edit it by hand (requires /reload to take effect).
Full example
{
"mode": "workspace-write",
"network": "open",
"sensitiveReadDeny": [
"~/.ssh",
"~/.aws",
"~/.npmrc"
],
"protectedPaths": {
"block": [
".git",
"node_modules"
],
"approval": [
".env",
".env.*",
".pi/pi-guard.json"
]
},
"bashPolicy": {
"directBlock": [
"sudo",
"su",
"dd"
],
"requireApproval": [
"rm-rf",
"git-reset-hard",
"git-clean-fd"
]
}
}Field reference
| Field | Description |
|-------|-------------|
| mode | "readonly" or "workspace-write". Switch with /guard r / /guard w |
| network | "open" (outbound allowed) or "blocked" (all outbound denied). Switch with /guard non / /guard noff |
| sensitiveReadDeny | Paths blocked from all Agent reads. Supports ~ and globs |
| protectedPaths.block | Paths where write / edit are rejected outright |
| protectedPaths.approval | Paths where write / edit trigger an approval prompt |
| bashPolicy.directBlock | Bash commands rejected immediately |
| bashPolicy.requireApproval | Bash commands requiring approval |
Adding your own sensitive paths
"sensitiveReadDeny": [
"~/.ssh",
"~/.aws",
"~/my-project/secrets.yml"
]Adding custom dangerous commands
"bashPolicy": {
"directBlock": [
"sudo",
"docker-host-root-bind"
],
"requireApproval": [
"rm-rf",
"bash-c"
]
}Entries in
bashPolicyare policy IDs, not raw regex. See the default config generated byinitfor the full list.
