pi setup

pi-docker-sandbox

Runs the pi coding agent only inside a hardened Docker container. pi has no permission prompts, so this container is the security boundary: it sees exactly one project, the read-only agent config, the memory of that project’s domain and its own state volume — nothing else from the host.

The agent config itself lives in pi-config. Which host path appears where in the container is defined in docs/path-contract.md, the single contract both repos follow.

Prerequisites#

  • macOS with Docker Desktop (or OrbStack) running — docker info must work
  • ~/pi/ containing pi-config/, pi-docker-sandbox/, brain/pi-memory-{core,private,work}/ and ws/{private,work}/ (the default domains; more with pi-sandbox domain add <name>)
  • Task runner and checks: brew install just bats-core shellcheck gitleaks (jq ships with macOS)

First-time setup#

bash
cd ~/pi/pi-docker-sandbox
cp .env.example .env                 # fill GIT_AUTHOR_* (and BRAIN_*_URL for memory sync)
export PATH="$PWD/bin:$PATH"         # or add this to your shell profile
pi-sandbox rebuild                   # builds pi-sandbox:<version>
pi-sandbox start ~/pi/ws/private/<repo>
# inside pi, once:  /login

.env holds no secrets; credentials are explained in docs/credentials.md.

Daily use#

TaskCommand
Start a sessionpi-sandbox start ~/pi/ws/<domain>/<repo>
Continue the last onepi-sandbox start <path> -- --continue
Also read another domain’s memory (read-only, this run)pi-sandbox start <path> --with-domain <name>
Add a domain (workspace folder + memory store)pi-sandbox domain add <name> or just domain-add <name>
Show the domainspi-sandbox domain list
Settings, MCP servers, instructions, skills and prompt templates per domain / customer / projectfiles in config/pi-config-<domain>/… (docs/config.md); check with pi-sandbox config show <path>
Session history per domain (kept 30 days by default)pi-sandbox sessions list, sessions prune <domain>|--all, sessions clear <domain>
Use a Keychain API key instead of the stored loginpi-sandbox start <path> --key-from-keychain
Shell in the same containerpi-sandbox shell <path>
Check the isolationpi-sandbox selftest <path>
Stop sessionspi-sandbox stop <path> or pi-sandbox stop --all
Rebuild the imagepi-sandbox rebuild [--no-cache]
Review and push memorypi-sandbox memory-sync <domain>
Clone or refresh whole forge groups into the workspacepi-sandbox workspace clone <domain> <host>/<group> (docs/workspace.md)
Check memory (read-only)pi-sandbox memory-check <domain>

All commands, flags, exit codes and warnings: docs/commands.md. Adding a project: docs/onboarding.md.

Repo layout#

text
Dockerfile                  image: pinned base digest + pinned pi, user pi (uid 1000)
compose.yaml                session container: mounts, env, hardening, limits
justfile                    task runner: `just check`, `just gate` (run `just` to list)
bin/pi-sandbox              the host command (bash 3.2 compatible)
container/entrypoint.sh     prepares agent state from the read-only config, runs pi
empty/                      empty placeholder for a missing memory store
scripts/                    `just check` helpers (definition and docs checks)
tests/                      bats suites, stubs, and the in-container isolation test
docs/                       path contract, commands, credentials, security, state, internals
.env.example                names of every local setting (copy to .env, git-ignored)

Documentation#

DocContent
docs/path-contract.mdEvery mount and env var, host path → container path
docs/commands.mdCommands, flags, exit codes, warnings, just recipes
docs/credentials.mdLogin vs. Keychain key, precedence, revoke and replace
docs/security.mdThreat model, hardening, network endpoints, accepted risks
docs/state.mdThe pi-agent-state volume, shared-history risks, deleting
docs/workspace.mdCloning whole forge groups with ghorg: commands, layout, tokens, refresh rules
docs/memory.mdMemory scope per session, session context, memory-sync review flow, memory check, sync credentials
docs/internals.mdWhat each script and compose file does
docs/release.mdsemantic-release, the released image, runner and secrets setup
docs/dependencies.mdRenovate: what is updated, commit types, PR checks, setup

Version bumps#

Versions change only deliberately (spec FR-009):

WhatPinned in
piDockerfile → ARG PI_VERSION
Base imageDockerfile → FROM node:24-bookworm-slim@sha256:… (resolve with docker buildx imagetools inspect node:24-bookworm-slim)
pi packagespi-config/agent/settings.json → packages (exact @x.y.z)

Renovate proposes all of these as pull requests (docs/dependencies.md). To bump by hand: change the pin → pi-sandbox rebuild → just gate → commit as build(deps): bump <component> to <version>. On main, that commit makes semantic-release cut a patch release and publish a new image (docs/release.md).

Releases#

Every push to main runs .forgejo/workflows/release.yml on the self-hosted runner: just check, then semantic-release tags v&lt;version&gt;, updates CHANGELOG.md and VERSION, pushes codeberg.org/black0ut-pi-setup/pi-sandbox:<version> (amd64 + arm64) and creates the Codeberg release. Before each image push, older images are pruned so the Codeberg package quota holds (the new and the previous release stay; docs/release.md). pi-sandbox uses the image matching VERSION and pulls it on every start; it warns when this checkout is not on main, because VERSION is then likely stale (docs/commands.md).

Quality gates#

Before every commit to main (constitution gates):

bash
just check   # shellcheck, bats, compose lint, sandbox definition check, docs check
just gate    # rebuild + in-container selftest with the pinned pi version

just quickstart runs the whole automated quickstart validation (isolation, warm start, config, refusals, gates, parallel sessions) and then lists the steps that need you.

In this part

Onboarding
Onboarding a project
Commands
pi-sandbox commands
Credentials
Workspace cloning
Workspace cloning (ghorg)