Skip to content

sionic-ai/pi-justbash-sandbox

Repository files navigation

@sionic-ai/pi-justbash-sandbox

Sandboxed bash, read, write, and edit tools for @mariozechner/pi-coding-agent (badlogic/pi-mono) backed by just-bash.

Why

Out of the box, pi runs shell and filesystem tools against the host. When pi must be contained to a per-session workspace — no access to the rest of the host, deterministic cleanup on exit — this extension:

  • Replaces bash with a just-bash sandbox running inside a per-session root directory (ReadWriteFs with allowSymlinks: false).
  • Replaces read / write / edit so they only touch the sandbox root.
  • Disables grep at both the tool layer and the tool_call gate — use bash grep inside the sandbox instead.
  • Creates and cleans up the sandbox root on every session_start / session_shutdown, on session_before_switch and session_before_fork, and on SIGINT / SIGTERM / SIGHUP.
  • Reaps orphaned sandboxes older than 12 hours at load time.

See docs/ARCHITECTURE.md for the data flow, docs/RESEARCH.md for the source-verified upstream facts the design is anchored to, and docs/AGENT_BRIEF.md for the phase-by-phase implementation roadmap.

Install

This package is not published to npm. Install it from GitHub, a local clone, or a pinned git ref — pi-mono supports all three natively. The package ships its built factory under dist/, so consumers only need to point pi at the repository; no intermediate npm registry is ever involved.

Prerequisites:

  • pi (aka @mariozechner/pi-coding-agent) >= 0.67.68 installed and on your PATH. See badlogic/pi-mono for install instructions. pi supplies @mariozechner/pi-coding-agent, @mariozechner/pi-agent-core, and @sinclair/typebox at runtime, so consumers do not need to install those separately.

Option A — Install via pi install

pi-mono's package manager speaks git and local paths directly. Each form below pulls the repo, runs npm install in it (for just-bash), builds nothing itself — this repo already commits dist/ on releases (see Release flow) — and loads the extension.

# Pinned git tag or branch (recommended)
pi install git:github.com/sionic-ai/pi-justbash-sandbox@main

# SSH
pi install git:git@github.com:sionic-ai/pi-justbash-sandbox

# Raw URL also works
pi install https://github.com/sionic-ai/pi-justbash-sandbox

# Local clone (absolute or relative path)
pi install /absolute/path/to/pi-justbash-sandbox
pi install ./pi-justbash-sandbox

To try it without persisting to settings:

pi -e git:github.com/sionic-ai/pi-justbash-sandbox
pi -e /absolute/path/to/pi-justbash-sandbox

Option B — Declare it in .pi/settings.json

// .pi/settings.json
{
  "packages": [
    "git:github.com/sionic-ai/pi-justbash-sandbox@main"
  ]
}

pi auto-installs missing packages on startup, so this file alone is enough for collaborators to pick up the sandbox on their next pi run. Use .pi/settings.json for project-scoped installs and ~/.pi/agent/settings.json for global installs.

Option C — Inline factory (embedding pi via the SDK)

When you embed pi inside your own Node or Bun program, import the default factory directly from the cloned repo (no registry lookup):

import { DefaultResourceLoader } from "@mariozechner/pi-coding-agent";
import justbashSandbox from "./vendor/pi-justbash-sandbox/dist/index.js";

const resourceLoader = new DefaultResourceLoader({
  extensionFactories: [justbashSandbox],
});

Any path resolvable by Node / Bun ESM works here — a git submodule, a vendored subtree, a workspace package, a file: spec in package.json, etc. The only hard dependency on the filesystem side is that dist/ has been built (pnpm build / bun run build).

Once loaded, the extension takes over the host-touching tools as soon as pi emits session_start; from there every bash / read / write / edit tool call runs against the per-session sandbox root.

CLI flags

Flag Type Default Meaning
--sandbox-root <path> string $TMPDIR/pi-justbash Base directory under which per-session sandbox roots are created.
--sandbox-max-file-size-mb <n> string 10 Override the maximum file read size for the sandbox fs.
--sandbox-redact-env <bool> string true Master switch. Redact host env-var values (API keys, tokens, database URLs, ...) from bash output, file read/write, edit, and host-binary-bridge stdout/stderr. Also controls whether the agent's shell inherits secret entries (see --sandbox-strip-bash-env).
--sandbox-strip-bash-env <bool> string follows --sandbox-redact-env Strip secret-classified entries from the env handed to just-bash so the agent cannot expand $SECRET inline. Defense-in-depth on top of output redaction.
--sandbox-redaction-marker <s> string [REDACTED] Replacement string used when a secret value is redacted.
--sandbox-redact-env-allow <csv> string Comma-separated env var names to exempt from redaction + stripping even when they match the secret heuristic. Propagated to bash env filter, host bridge env filter, and the output redactor.
--sandbox-redact-env-deny <csv> string Comma-separated env var names to force-redact regardless of the default heuristic. Also forces stripping from the bash env and host bridge env.
--sandbox-redact-min-length <n> string 4 Minimum value length required for a secret value to enter the output-value replacement table (prevents over-redaction of short strings like "0" / "1"). The name-form redactor (NAME=value) runs regardless of this threshold.
--sandbox-host-binary-env-allow <csv> string Comma-separated env var names allowed to pass through to host binary bridges (e.g. storm) despite being classified secret. Independent of --sandbox-redact-env-allow.

All redaction flags also accept SANDBOX_* env var equivalents (SANDBOX_REDACT_ENV, SANDBOX_STRIP_BASH_ENV, SANDBOX_REDACTION_MARKER, SANDBOX_REDACT_ENV_ALLOW, SANDBOX_REDACT_ENV_DENY, SANDBOX_REDACT_MIN_LENGTH, SANDBOX_HOST_BINARY_ENV_ALLOW).

Redaction threat model

The sandbox treats the LLM agent as untrusted and assumes host env vars (API keys, access tokens, connection strings, SSH_AUTH_SOCK) are secrets that must not reach the agent. Three cooperating layers enforce this:

  1. Output redaction. Every byte streamed back through bash, read, write, and edit passes through a Redactor that replaces known-secret values with the redaction marker and rewrites any NAME=value it spots whose name matches the secret heuristic (catches printenv, env, declare -p, (NAME=v), $(NAME=v), ${NAME=v}, and `NAME=v`). ANSI escape sequences are stripped before matching so a split-by-colour bypass cannot sneak a secret through.
  2. Bash env stripping. Before just-bash constructs its shell, secret entries are removed from the env map so the agent's $ANTHROPIC_API_KEY is empty and env / printenv cannot find a value to print. This prevents the agent from using a secret (e.g. as a curl header) even when the output would have been redacted. Controlled by --sandbox-strip-bash-env.
  3. Host binary bridge env filtering. Host binaries exposed via --sandbox-host-binaries (e.g. storm) inherit only the non-secret subset of process.env, with an explicit opt-in list via --sandbox-host-binary-env-allow for the tokens the bridge itself needs. Stdout / stderr of the spawned binary also pass through the redactor.

Binary safety. read / edit only redact content that passes a strict UTF-8 whitelist (no NUL byte in the first 8 KiB, strictly decodable as UTF-8, no image magic bytes). Image and binary files are returned untouched so MIME sniffing and downstream image tooling keep working.

What redaction does NOT cover. Redaction hides values from LLM output; it does not prevent a host binary you chose to bridge from writing wherever its flags allow (e.g. storm -o /absolute/path). Restrict --sandbox-host-binaries to tools whose filesystem reach you are comfortable granting.

Tool behaviour at a glance

pi tool In this extension
bash Runs inside a fresh just-bash Bash per call, bound to the session's ReadWriteFs. stdout + stderr are flushed through pi's onData; non-zero exits and 124/130 (timeout / abort) propagate unchanged.
read Reads through ReadWriteFs.readFileBuffer; image MIME detection covers PNG, JPEG, GIF, and WebP via their magic bytes.
write Writes through ReadWriteFs.writeFile after auto-creating the virtual parent chain.
edit Shares the sandbox fs with read + write, so pi's diff always applies to the exact bytes it just read.
grep Disabled. grep is registered as a same-named stub that throws a sandbox notice, and a tool_call handler returns { block: true, reason } so lingering grep calls from other extensions are rejected too.
ls, find Left untouched — pi's defaults still operate on the host cwd.

Troubleshooting

  • "cwd ... is outside the sandbox" in bash output — pi handed the bash tool a cwd that does not live under the sandbox root. The adapter returns exit code 126 in that case so the agent can recover. Point --sandbox-root at a directory that actually contains the cwd, or do the work via a command that cds from the sandbox root.
  • Hitting file-size limits on read — raise --sandbox-max-file-size-mb; the default matches ReadWriteFs (10 MiB).
  • Stale sandbox dirs piling up in $TMPDIR/pi-justbash — the orphan reaper only removes dirs older than 12 hours on startup. Delete the base dir manually if you need to reclaim space sooner; it is safe while no pi process is running.

Development

pnpm install       # or: bun install
pnpm test          # or: bun run test
pnpm lint          # or: bun run lint
pnpm typecheck     # or: bun run typecheck
pnpm build         # or: bun run build

Both pnpm and bun are first-class; CI runs the full matrix on each push against Ubuntu and macOS.

Release flow

This package is not distributed through npm (package.json sets "private": true and a prepublishOnly guard blocks accidental npm publish). Releases are plain git tags and the compiled dist/ output is checked into the repository on tag boundaries so that pi install git:...@<tag> works without a separate build step on the consumer's side.

To cut a release locally:

pnpm install
pnpm lint && pnpm typecheck && pnpm test && pnpm build
git add dist
git commit -m "build(dist): release vX.Y.Z"
git tag vX.Y.Z
git push --follow-tags

Consumers then pin the tag:

pi install git:github.com/sionic-ai/pi-justbash-sandbox@vX.Y.Z

License

MIT — see LICENSE.

About

Sandboxed bash/read/write tools for pi-mono via just-bash. Disables grep by default. Per-session filesystem isolation with cleanup.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors