pi setup

Session

Security model

pi has no permission prompts by design. The container is the security boundary (constitution Principle I). This page is the threat model (spec FR-023), the hardening that implements it, the network endpoints (FR-011) and every accepted risk.

Threat model#

In scope — what the sandbox protects the host against:

ThreatMitigation
Mistakes by the agent (deleting files, running destructive commands)Only the selected project, the memory stores and the agent state are writable; the image and config are read-only
Prompt injection through project files, web content or memorySame boundary: an injected instruction can only act inside the container and its mounts
A malicious or compromised pi package or extensionSame boundary; packages are pinned and reviewed (pi-config/docs/migration.md) — but see “Packages” below
Reaching host secretsNo host home, ~/.ssh, SSH agent, host git config, host ~/.pi/agent, Keychain or docker.sock in the container; tested by pi-sandbox selftest
Pushing code or memory without reviewMemory credentials exist only in the separate sync container. Code can be pushed from a session only when the owner configured a push credential for the domain and git host (credentials.md): default and protected branches, force pushes and ref deletion are refused by an in-session guard (defence in depth) and by the forge’s branch protection (the guarantee)

Out of scope for v1:

  • escape from the container runtime or the Docker Desktop VM, and kernel exploits;
  • a compromised host (the Mac itself);
  • exfiltration of data over the network (outbound access is unrestricted, see below).

Accepted risks, each tied to its requirement:

RiskRequirementWhere documented
Session history is shared across domains, kept without a time limit and may contain customer data seen in work sessionsFR-008astate.md
An injected API key is readable by the agent during the sessionFR-008bcredentials.md
Unrestricted outbound network: the agent can reach the LAN/homelab and send project code, memory or session content to any internet hostFR-011below
Memory “never store” rules are behavioural (instructions + owner review), not technicalFR-016amemory.md
The sync container runs the same image as the agentFR-019amemory.md
The Codeberg token for core/private memory can write to every repo of the owner’s account (Codeberg has no per-repo deploy tokens)FR-019amemory.md
Project trust decisions are shared across domainsEdge Casesstate.md
Packages and extensions run with the agent’s full accessFR-024below
A C/C++ toolchain is available in the image (for native package builds)FR-024Hardening table
kubeconfig (without a level kube.json; with one, only the kube-proxy sidecar holds it and the agent can only read (pod logs included, secrets excluded) and rollout restart through it) / talosconfig, STACKIT, Hetzner Cloud and GitHub sign-ins in the domain volume are readable by the agent in that domain’s sessions and allow what their token allowsFR-008bcredentials.md
A forge token for workspace cloning is readable inside the workspace container during the run (not in sessions); it has whatever access the token grantsFR-008bworkspace.md
SSH keys of the ssh.json hosts: only the ssh-agent sidecar holds them, but the agent can use them during the session to log in to any server that accepts them (keys are not bound to their host inside the agent) and can read what those servers holdFR-008bconfig.md
The push credential of a domain and host (SSH key or HTTPS token) is readable by the agent during the session (the entrypoint’s /proc/<pid>/environ holds it; the ssh-agent socket can sign) and allows whatever it is allowed to do on the forge; the pre-push guard is bypassable by the agent and only the forge’s branch protection is a real limit. Mitigation: a dedicated credential per domain and host, a machine user or deploy key with least access, branch protectionFR-008bcredentials.md
An autonomous /yolo run (pi-config spec 012, constitution v1.3.0) can merge its own PR and deploy, production included, with the push credential and forge access the session already has; a wrong change can reach production in ways rollback does not undo. The gates are behavioural; the real limits are the forge’s branch protection with required CI and the credential’s scope. Revocation: revoke the push key and forge token, remove the project’s ## Yolo block, or delete agent/prompts/yolo.md in pi-config. No sandbox changeFR-008bpi-config docs/config-reference.md (agent/prompts/yolo.md)
AWS credentials passed to an aws-api MCP server are readable by the agent during the session and allow what the agent’s permission set allows (read and write where granted)FR-008bconfig.md

Hardening (compose.yaml)#

SettingWhy
user: "1000:1000"pi runs as the unprivileged user pi
cap_drop: [ALL]No Linux capabilities; none are added back
security_opt: no-new-privileges:trueNo privilege gain through setuid binaries
read_only: trueThe image filesystem cannot be changed; only declared mounts and tmpfs are writable
tmpfs: /tmp, /run/pi-sessionIn-memory scratch space, gone on exit
init: trueDocker’s minimal init (mounted read-only at /usr/sbin/docker-init) reaps processes and forwards signals
No ports:, default bridge networkOutbound only; nothing can connect in; no host network or shared namespaces
No privileged, no docker.sock, no host home or SSH mountsSee the threat model
openssh-client installed, but inert without a keyPush is opt-in per domain and host: no credential means no agent, no token file, no GIT_SSH_COMMAND, no hook path; the selftest checks both states
python3, make, g++ installedNeeded so node-gyp can compile native addons of pinned packages without a linux-arm64 prebuilt (node-pty for plannotator). Accepted risk: the agent can compile and run native code — inside the same boundary

scripts/check-sandbox-definition.sh (run by just check) fails if any of these is weakened, if a mount outside path-contract.md appears, or if the base image or pi version is unpinned (spec SC-008).

Resource limits#

LimitDefaultChange in
CPUs4compose.yaml → cpus
Memory8 GiBcompose.yaml → mem_limit
Processes512compose.yaml → pids_limit

Generous enough for LSP-heavy extensions; adjust deliberately and note it here.

Network (FR-011)#

Outbound access is unrestricted in v1. The agent relies on:

EndpointUsed for
api.anthropic.com (and any other configured model provider’s API host)Model requests
Your model proxy proxy host (configured with /login model-proxy)Model requests via pi-provider-model-proxy
registry.npmjs.orgInstalling pinned pi packages into the agent state
*.api.stackit.cloud, api.hetzner.cloud, api.github.comSTACKIT CLI, Hetzner Cloud CLI and GitHub CLI after a sign-in in the domain (credentials.md)
Hermit package sources (GitHub releases and the package hosts the manifests name)Toolsets of tools.json and the repos’ own Hermit pins, downloaded into the domain’s cache. The Hermit binary and the package index are pinned in the image; the packages are verified by the checksums of their manifests. A repo’s own Hermit pins are not verified by the image: trust a Hermit-managed repo like its scripts (credentials.md)
The API servers in your kubeconfig / talosconfigkubectl and talosctl after you put a config into the domain (credentials.md)
aws-mcp.us-east-1.api.awsAWS MCP Server: documentation, skills and — with an aws-api server and credentials — AWS API calls (config.md)
The forge hosts of pi-sandbox workspace (API and HTTPS clones)Workspace container only (workspace.md)
gitlab.com, github.com, codeberg.orgClones, fetches and pulls of public dependencies; memory sync pushes (sync container only)
Arbitrary hostsWeb search and fetch tools (for example pi-web-access)

Residual risk: the agent can reach every host the Mac can reach, including the LAN and homelab services, and it could send project code, memory or session content anywhere. This list exists so that an egress allow-list can be added on purpose later.

Inbound: nothing. No published ports, no host network mode, no shared network namespace.

Released image (supply chain)#

Sessions run codeberg.org/black0ut-pi-setup/pi-sandbox:<VERSION>, built and pushed by the release pipeline on the self-hosted runner from this repo’s pinned Dockerfile. Whoever controls that runner or the release token can publish an image the sandbox will pull. Keep the runner dedicated to these repos and the token limited to write:repository + write:package (release.md). pi-sandbox rebuild builds the same tag locally from source if you prefer not to trust the registry.

Memory check#

The memory check guards what leaves the machine through memory-sync: secrets block a store, possible personal data must be confirmed per finding (memory.md). It is a safety net, not a guarantee — detection is heuristic. The on-demand pi-sandbox memory-check container has no network, no credentials and read-only stores. gitleaks comes from the official image pinned by digest (same supply-chain rules as the base image); reports never contain the matched values.

Packages and extensions (FR-024)#

pi packages and extensions run inside the pi process with the agent’s full access in the container, including the agent state and stored credentials. Only packages kept in the migration review, or added later with a documented source, exact pinned version and reason in pi-config/docs/config-reference.md, may be installed.