pi-os-guard

OS-sandboxed read-only / restricted execution for pi (bubblewrap on Linux, sandbox-exec on macOS).

Packages

Package details

extension

Install pi-os-guard from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-os-guard
Package
pi-os-guard
Version
0.1.2
Published
Aug 3, 2026
Downloads
469/mo · 20/wk
Author
oxel40
License
MIT
Types
extension
Size
46.4 KB
Dependencies
0 dependencies · 3 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-guard

Distributed as the npm/git package pi-os-guard (the name pi-guard was already taken on npm). The extension, its /guard command, and its config keys all keep the pi-guard name internally.

OS-sandboxed read-only / write-restricted execution for pi. Routes the write-capable built-in tools (write, edit, bash) and ! commands through a per-command OS sandbox — bubblewrap (bwrap) on Linux, sandbox-exec on macOS — so the operating system, not a heuristic, enforces the write boundary on the real host filesystem. No container, no image: the sandboxed shell shares your installed binaries and environment.

Toggle the mode live during a session with /guard.

Why the OS, not a heuristic

An in-process guard can only block or rewrite a tool call; it can't change what a command is allowed to do, so "read-only for bash" degenerates into guessing whether a command writes. That guess is unsound. Here, commands run under a read-only view of the filesystem, so writes fail at the syscall for any command — including ones no heuristic would catch.

This is a real write boundary, but not a hardened sandbox: it does not confine reads (your whole filesystem is readable), and (by default) the process keeps network access. Use it to make a session safely "look but don't touch" and confine writes — not to run actively malicious code. For stronger isolation add a VM/micro-VM.

Modes

Mode Filesystem Writes
off host tools (pi default) normal
readonly whole FS readable everything fails at the syscall
restricted whole FS readable only cwd + allow dirs (and scratch) succeed; rest fails

Identity paths. The sandbox shares the host filesystem at the same paths — /Users/you/project is the same path inside and out — so absolute paths, $PWD, and tool output line up with the host. There is no /workspace remapping.

Home is readable, not writable. Your $HOME is readable in every mode, so skills (~/.agents/skills), git (~/.gitconfig), and gh (~/.config/gh) work against your real config. The trade-off: guard confines writes, not reads — the model can read anything you can (including ~/.ssh), so this is not a secrets boundary. The sandbox inherits your shell env (tokens included), matching that posture.

Reads run on the host. read, ls, find, and grep are pi's normal host tools — a read can't breach a write boundary, so there's nothing to sandbox, and they stay fast.

Session scratch. Each session gets one writable scratch dir, pinned as TMPDIR in every command, so temp files created by one command are visible to the next. It's removed when the session ends (not persisted across reboots). In readonly mode the scratch (and TMPDIR) is the only writable path.

Fail-closed: in readonly/restricted with no backend available, tools error out. The extension never silently falls back to host execution.

Backends

  • Linux — bubblewrap (bwrap). Read-only bind of / plus read-write binds for the writable dirs (cwd + allow-list + scratch). Fresh /tmp, /proc (--unshare-pid), and /dev; network left on. Install with apt install bubblewrap / dnf install bubblewrap if missing.
  • macOS — sandbox-exec (Seatbelt). An (allow default) / (deny file-write*) profile that re-allows writes only under the writable dirs (canonicalized with realpath) plus the usual devices. Always present at /usr/bin/sandbox-exec. (Apple marks sandbox-exec deprecated, but it still works and needs no entitlements/signing.)

What's not provided vs a container: no PID-namespace cutoff on macOS and no network isolation — this is a write boundary by design.

Install

pi install npm:pi-os-guard
# or
pi install git:github.com/Oxel40/pi-os-guard

No npm install needed — it uses only pi's bundled APIs and Node built-ins. Requires bubblewrap on Linux; macOS works out of the box. Windows is not supported (yet).

Commands

/guard                 # status (backend, mode, workspace, allow, scratch)
/guard readonly        # switch to read-only  (rebuilds sandbox)
/guard restricted      # switch to restricted (rebuilds sandbox)
/guard off             # use host tools
/guard allow <dir>     # add an extra read-write dir (restricted) and rebuild
/guard backend <name>  # bwrap | sandbox-exec | auto
/guard rebuild         # force-rebuild the sandbox

Default startup mode is readonly. Override at launch with pi --guard off, pi --guard readonly, or pi --guard restricted.

Configuration

Merged from global then project, with env-var overrides on top:

  • ~/.pi/agent/extensions/pi-guard.json (global)
  • <cwd>/.pi/pi-guard.json (project-local, requires project trust)
{
  "backend": "auto",
  "mode": "readonly",
  "allow": []
}
  • backend: auto | bwrap | sandbox-exec (env PI_GUARD_BACKEND)
  • mode: default startup mode (readonly by default; env PI_GUARD_MODE)
  • allow: extra read-write dirs for restricted mode

Concurrent Subagents (spawn_agents)

pi-guard provides a spawn_agents tool that spawns multiple pi subagents concurrently, each inheriting the same guard mode. In TUI mode, it shows a non-blocking status widget above the input box — the session remains usable while it runs, and the widget never takes keyboard focus.

Features

  • Live status updates: Each agent shows its current state with colored icons:

    • pending (dim)
    • starting (muted)
    • spinner (braille frames) running (accent)
    • completed (success)
    • failed (error)
  • Event tracking: See what each agent is doing in real-time:

    • Calling: bash while a tool call is in flight, ✓ bash / ✗ bash (error) once it finishes
    • Thinking... / Generating response... during LLM calls
  • Progress summary: Bottom bar shows Completed: X | Running: Y | Failed: Z

  • Abort support: Ctrl+C cancels the tool call via pi's standard tool-cancellation mechanism (stops all in-flight subagents)

  • Graceful degradation: Falls back to simple mode when not in TUI (print, JSON, or RPC mode)

Visual Example

When spawning 3 concurrent agents:

  Spawning 3 subagents

 ✓ [1/3] COMPLETED  Analyze the error logs in /var/log/app.log...
 ⠹ [2/3] RUNNING    Fix the failing unit test in test_auth.py...
    └─ Calling: bash
 ○ [3/3] PENDING    Update the README with new installation steps...

  Completed: 1 | Running: 1 | Failed: 0
  Working... (Ctrl+C to cancel)

Usage

The agent automatically uses spawn_agents when appropriate, or you can request it:

# In a pi session
Please use spawn_agents to run these 3 tasks concurrently:
1. Check system logs for errors
2. Run the test suite
3. Update the documentation

Each subagent runs in its own pi process with the same guard mode (readonly/restricted), so they can work in parallel while respecting the write boundary.

Lifecycle

Each session builds the sandbox lazily on first use and creates one scratch dir. There is no long-lived process to reap: the scratch dir is removed on session shutdown (quit/new/resume/fork/reload). A dir leaked by a hard crash is small and reaped by the OS temp cleaner.