pi-playpen
Global, multi-project OS sandboxing for the pi coding agent
Package details
Install pi-playpen from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-playpen- Package
pi-playpen- Version
0.1.3- Published
- Aug 25, 2026
- Downloads
- 137/mo · 24/wk
- Author
- moreheadm
- License
- MIT
- Types
- extension
- Size
- 140.9 KB
- Dependencies
- 4 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-playpen
pi-playpen is a global, multi-project sandbox extension for pi. It uses @anthropic-ai/sandbox-runtime for OS-level Bash isolation and replaces Pi's filesystem tools with policy-aware equivalents.
There are no per-command approval dialogs. A sandbox exposes only configured projects plus a read-only system runtime needed to launch commands. Pi's installed package and standard global extension/package roots are automatically readable by both Bash and the direct read/search/list tools. Standard OS paths and common Homebrew, MacPorts, Linuxbrew, Nix, and /opt installations are available to Bash automatically. User-managed toolchains and extension paths outside those standard roots must be named explicitly in trusted global configuration. If an operation is not exposed, Pi receives a denial and the user must change trusted configuration.
Status
This is an initial implementation for macOS and Linux. Linux needs bwrap, socat, and rg; macOS needs rg. Windows is intentionally deferred.
Install
pi install /path/to/pi-playpen
# or, once published:
pi install npm:pi-playpen
Install dependencies when running from a checkout:
npm install
npm run verify
Global configuration
Create ~/.pi/agent/extensions/pi-playpen/config.jsonc; see examples/global-config.jsonc.
{
"version": 1,
"enabled": true,
"defaultSandbox": "work",
"sandboxes": {
"work": {
"projects": [
{ "root": "~/Programming/app", "access": "read-write" },
{ "root": "~/Programming/shared", "access": "read-only" },
{
"root": "~/.pi",
"access": "read-write",
"denyRead": ["agent/auth.json"],
"denyWrite": ["agent/auth.json"],
},
],
"sandboxRuntime": {
"network": {
"allowedDomains": [],
"deniedDomains": [],
},
"filesystem": {
// Extra read-only user toolchains or extension paths outside Pi's
// automatic roots. Prefer narrow paths such as ~/.nvm/versions.
"allowRead": [],
// Additional host restrictions; Snowglobe adds its own root/project rules.
"denyRead": [],
// Runtime and direct-tool writes; these may be outside project roots
// (for example, /tmp).
"allowWrite": [],
"denyWrite": [],
},
},
},
},
}
Snowglobe is enabled independently of Pi's launch directory. The active sandbox is selected by, in order:
--playpen <name>;defaultSandbox;- the sole configured sandbox.
A missing default with multiple sandboxes is a locked failure, never a cwd-based guess. Sandboxes have no working directory; relative paths use Pi's current directory and are denied unless that directory is inside an accessible project. Projects may overlap, including nested project roots; the most specific matching root supplies the direct-tool policy. Nested path declarations are accepted and semantically deduplicated for the runtime policy. A child with no writes can be nested under a writable parent; a partially writable child under a writable parent is rejected because Sandbox Runtime cannot safely express that boundary.
Set trusted global enabled: false to disable Snowglobe. A project configuration cannot disable or widen it. Project allowRead/allowWrite roots must remain inside that project's root. sandboxRuntime.filesystem.disabled is rejected, runtime allow entries must be literal paths, and runtime write allowances are shared with direct write/edit tools. Set sandboxRuntime.network.allowAllUnixSockets only when a trusted tool requires Unix-domain IPC; this weakens the Linux seccomp boundary. allowLocalBinding is not a Linux Unix-socket workaround. Unknown configuration properties are errors rather than silently ignored typos. Remove any old customTools section from existing global configurations; non-managed tools are now allowed without a Playpen tool allowlist.
Project configuration
A member project can narrow its policy in .pi/extensions/pi-playpen/config.jsonc:
{
"version": 1,
"bySandbox": {
"work": {
"ignoreFiles": [".cursorignore", ".claudeignore"],
"denyRead": ["secrets/**"],
"allowWrite": ["src", "test", "docs"],
"denyWrite": ["secrets/**"],
"deniedDomains": ["uploads.example.com"],
},
},
}
Local path entries must be project-relative and cannot use .., ~, or absolute paths. Local allowRead/allowWrite entries must be inside the global allowance. Denies and ignore-file names are additive. Local configuration cannot add projects, network allowlist entries, host filesystem access, or runtime weakening flags.
Ignore files hide matching paths from read, write, edit, ls, find, and grep; their own files are also inaccessible. Existing ignored paths are compiled into the Bash runtime's read/write restrictions. The project Snowglobe configuration and global Snowglobe configuration are write-protected.
Commands
/playpen— active sandbox and project policy./playpen-doctor— state and basic runtime diagnostics./playpen-use <name>— switch sandboxes. This resets and reinitializes Sandbox Runtime; a failed switch attempts to restore the prior sandbox and locks tools if restoration fails./playpen-add [folder]— choose a sandbox and add the folder as a read-write project, then reinitialize that sandbox. With no folder, choose the current directory or enter another folder in the dialogue; relative folders are resolved from the current directory./playpen-on— enable Playpen for the session, restoring the previously active sandbox or selecting one from configuration./playpen-off— disable Playpen enforcement for the session and restore Pi's normal tools.
The on/off commands are session-scoped and do not change global configuration. Turning Playpen off intentionally removes its sandbox boundary until /playpen-on is used.
Security model
bashand!commands run through Sandbox Runtime.- Pi's seven built-ins (
read,write,edit,ls,find,grep,bash) are replaced or guarded. - Managed tool calls, runtime initialization, switching, and shutdown are serialized by Snowglobe's execution gate. This prevents Snowglobe-internal policy races; it cannot prevent an unrelated host process from swapping a path between a direct tool's validation and use.
- Reads and writes resolve canonical paths and must remain inside a configured project or an applicable trusted read/write root.
- Symlinks are not traversed by directory walkers.
- Non-managed model-invoked tools are allowed by default and are not sandboxable through generic interception; MCP and other extension tools execute as trusted host code. Snowglobe still verifies that its seven managed tool names resolve to its own registrations and blocks calls after a name collision.
- Other installed Pi extensions are trusted host code and remain outside Snowglobe's security boundary.
Sandbox Runtime supplies OS protection for subprocesses. Snowglobe emits a root read deny, re-allows a documented read-only system runtime, automatically discovered pi/extension roots, and configured project paths, and compiles project deny/ignore rules into runtime restrictions. Before initialization, duplicate and redundant same-effect paths are removed without enumerating directory contents. Intentional parent-write/child-deny and read deny/allow-back boundaries are retained; SRT may still emit repeated ordered mounts internally because mount order is significant. Pi's installed package directory and standard global extension/package roots are shared with direct read, ls, find, and grep calls as read-only roots. Project-local extension paths remain subject to their containing project policy. Explicit sandboxRuntime.filesystem.allowRead roots are also shared with direct tools. Automatically added system/runtime paths remain Bash-only, so a direct ls /usr/bin may still be denied even though Bash can execute /usr/bin/git. Runtime allowRead entries never grant direct writes; runtime allowWrite entries are shared with direct write/edit tools. On Linux, Bubblewrap cannot enforce arbitrary future glob matches: Snowglobe expands current matches, and a literal directory or trailing /** rule protects the whole subtree. Use literal directory/file deny paths for hard Bash restrictions on paths that do not yet exist. A read-masked directory is backed by an ephemeral tmpfs, so a write inside it may report success but cannot modify the hidden host directory.
Development
npm run check
npm test
npm run verify
The project prefers jj for source control; ensure it is installed before committing changes.