@foxfirecodes/pi-sandbox
OS-level sandboxing for pi with interactive permission prompts (fork)
Package details
Install @foxfirecodes/pi-sandbox from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@foxfirecodes/pi-sandbox- Package
@foxfirecodes/pi-sandbox- Version
0.3.0- Published
- Jun 1, 2026
- Downloads
- 434/mo · 15/wk
- Author
- foxfirecodes
- License
- MIT
- Types
- extension
- Size
- 1.8 MB
- Dependencies
- 2 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-sandbox
Install with pi install npm:@foxfirecodes/pi-sandbox!
Sandbox for pi.
Sandboxes pi like this:
- read/write/edit: direct control using allow/deny lists
- bash: uses
@foxfirecodes/sandbox-runtimeto control network and file system access
When a blocked action is attempted, the user is prompted to allow it temporarily or permanently rather than silently failing.

Notes
There is an example config at sandbox.json. It was quite a few things added to get this extension to work with agent-browser and other common tools.
These open significant security loopholes, so shouldn't be used in a sensitive context or when you don't need browser support.
You may need to trial and error to find additional things you need to allow.
Quickstart
Prerequisites
pi-sandbox delegates the OS-level bash sandbox to
@foxfirecodes/sandbox-runtime,
published from a fork of Anthropic's
anthropic-experimental/sandbox-runtime.
The sandbox runtime checks for ripgrep (the
rg binary) on both macOS and Linux at sandbox-init time. If rg
is not on the PATH that pi was launched with, sandbox initialization
fails with:
Sandbox initialization failed: Sandbox dependencies not available: ripgrep (rg) not found
Install ripgrep before enabling the extension:
| Platform | Install |
|---|---|
| macOS (Homebrew) | brew install ripgrep |
| macOS (MacPorts) | sudo port install ripgrep |
| Linux (Debian/Ubuntu) | sudo apt install ripgrep |
| Linux (Fedora/RHEL) | sudo dnf install ripgrep |
| Linux (Arch) | sudo pacman -S ripgrep |
| From source / other | https://github.com/BurntSushi/ripgrep#installation |
If which rg succeeds in your shell but pi still reports rg not found, pi is being launched from a parent process whose PATH does
not include the directory containing rg (common when GUI launchers
inherit a minimal non-login PATH). On macOS, /opt/homebrew/bin and
/usr/local/bin are the usual culprits — make sure your launcher's
environment includes whichever one your install uses.
Install
pi install npm:pi-sandbox
Configure
Add a config like this either to ~/.pi/agent/sandbox.json (global) or to .pi/sandbox.json (local).
Local config takes precedence over global.
Note below that the order of precedence for filesystem read and write are opposite.
{
"enabled": true,
"allowBrowserProcess": true, // If you want to use agent-browser or similar Chrome setup
"commandPatterns": {
// Bash/! commands matching allowPatterns bypass the sandbox entirely.
// Bash/! commands matching denyPatterns are hard-blocked.
// Non-regex entries are shell-style globs matched against the whole command.
// re:/pattern/flags entries are JavaScript regexes.
// denyPatterns take precedence over allowPatterns.
"allowPatterns": ["gh auth status"],
"denyPatterns": []
},
"network": {
"allowLocalBinding": true, // ditto
"allowAllUnixSockets": true, // ditto
"allowedDomains": ["github.com", "*.github.com"],
"deniedDomains": []
},
"filesystem": {
// For READS:
// - ANY read is prompted unless the path is already in allowRead
// - Granting a prompt adds to allowRead, which overrides denyRead
// - denyRead is not a hard-block; it just marks regions as denied by default
"denyRead": ["/Users", "/home"],
"allowRead": [".", "~/.config", "~/.local", "Library"],
// For WRITES:
// - empty ALLOW means no write access at all
// - DENY takes precedence and is never prompted
"allowWrite": [".", "/tmp"],
"denyWrite": [".env", ".env.*", "*.pem", "*.key"]
}
}
Usage
pi --no-sandbox disable sandboxing for the session
/sandbox show current configuration and session allowances
What it does
Bash commands are wrapped with sandbox-exec (macOS) or bubblewrap
(Linux) to enforce network and filesystem restrictions at the OS level.
Commands matching commandPatterns.allowPatterns bypass this entirely: no OS
sandbox wrapper, no network pre-check, and no write-block retry prompt.
Commands matching commandPatterns.denyPatterns are hard-blocked before
execution. Deny patterns take precedence over allow patterns.
Read, write, and edit tool calls are intercepted before execution and checked against the same filesystem policy. The OS-level sandbox cannot cover these tools because they run directly in the Node.js process rather than in a subprocess.
When a block is triggered, a prompt appears with four options:
- Abort (keep blocked)
- Allow for this session only
- Allow for this project — written to
.pi/sandbox.json - Allow for all projects — written to
~/.pi/agent/sandbox.json
Session allowances are held in memory only. They are never written to disk and the agent has no way to read or modify them. They are reset when the extension reloads or pi restarts.
What is prompted vs. hard-blocked
| Rule | Behaviour |
|---|---|
Command matches commandPatterns.denyPatterns |
Hard-blocked, no prompt (bash and !cmd) |
Command matches commandPatterns.allowPatterns |
Sandbox bypassed entirely (bash and !cmd) |
Domain not in allowedDomains |
Prompted (bash and !cmd) |
Path not in allowRead |
Prompted (read tool); granting adds to allowRead |
Path not in allowWrite |
Prompted (write/edit tools and bash write failures) |
Path in denyWrite |
Hard-blocked, no prompt |
Domain in deniedDomains |
Hard-blocked at OS level, no prompt |
If a path is added to allowWrite via a prompt but is also present in
denyWrite, it remains blocked. A warning is shown explaining which config
files to check.
commandPatterns.allowPatterns and commandPatterns.denyPatterns support
shell-style * and ? globs matched against the whole command after trimming
leading/trailing whitespace. Entries of the form re:/pattern/flags are treated
as JavaScript regexes. A pattern of "*" in allowPatterns bypasses sandboxing
for every bash/!cmd command; a pattern of "*" in denyPatterns blocks every
bash/!cmd command. pi-sandbox shows a warning when either is configured. Keep
allow patterns narrow: if an allow pattern matches a compound shell command such
as cmd && other-cmd, the entire command runs unsandboxed.
allowedDomains supports *.example.com wildcards. It also supports "*" to
allow all domains; pi-sandbox shows a warning when this is configured because it
removes per-domain prompts and can be easy to add accidentally. allowWrite uses prefix
matching, so . covers the entire current working directory.
On Linux, broad read allowances can coexist with more-specific nested write
allowances under the same directory. For example, allowRead: ["~/.cache"] with
allowWrite: ["~/.cache/uv", "~/.cache/mise"] exposes the cache tree read-only
while restoring the selected subtrees writable afterward. denyWrite and the
runtime's mandatory sensitive-path write denies are still applied last, so they
continue to override both broad reads and nested writes.
⚠️ Read and write have different precedence rules:
- Read: Every read is prompted unless the path is already in
allowRead.denyReadis not a hard-block — it marks regions as denied by default, but granting a prompt adds the path toallowRead, overridingdenyRead.- Write:
denyWritetakes precedence overallowWriteand is never prompted. A path indenyWriteis always blocked, even if it matchesallowWrite.
If neither file exists, built-in defaults apply (see above for the defaults).
The footer shows a lock indicator while the sandbox is active.
Ackowledgements
Based on code from badlogic/pi-mono by Mario Zechner, used under the MIT License.