No description
  • Shell 91.3%
  • Dockerfile 8.7%
Find a file
2026-09-26 22:31:51 +01:00
AGENTS.md claude 2026-09-25 07:11:58 +01:00
Dockerfile Update Dockerfile 2026-09-26 22:31:51 +01:00
pid add claude 2026-09-24 20:24:58 +01:00
README.md claude 2026-09-25 07:11:58 +01:00

pid

pid runs the Pi coding agent in a locked-down rootless Docker sandbox while keeping Pi state, its executable, and package caches in dedicated Docker volumes. Pi home can instead use a host directory. The launcher and Dockerfile are distributed through Git and update automatically after installation.

Requirements

The host needs:

  • Linux with rootless Docker Engine 26 or newer
  • The rootless Docker socket at $XDG_RUNTIME_DIR/docker.sock
  • The Docker systemd cgroup driver and seccomp enabled
  • git, flock (normally from util-linux), and sha256sum
  • ~/.local/bin on PATH

The launcher deliberately refuses to use a non-rootless Docker daemon.

Install

git clone https://git.tsbprodesk.co.uk/localuser/pid.git
cd pid
./pid --install

This clones the managed source into ~/.local/share/pid/source and creates:

~/.local/bin/pid -> ~/.local/share/pid/source/pid

Running pid --install again performs a clean reinstall. Installed launchers fetch their configured Git branch before each invocation and accept only fast-forward updates. A changed Dockerfile or AGENTS.md causes the image to be refreshed on the next command that needs it.

Host-backed Pi home

Set PID_PI_HOME_DIR to use a host directory instead of the pid-pi-home Docker volume. The directory is mounted read-write at /pi-home for every Pi, shell, import, seed, and extension-update container:

export PID_PI_HOME_DIR="/home/localuser/Documents/syncthing/dotfiles/pid-pi-home"
pid

The path must be absolute and cannot contain commas or newlines. It is created with mode 700 if it does not exist, and must be readable, writable, and searchable by the current user. An empty directory is seeded on first launch; an existing pid-pi-home Docker volume is not copied automatically. Put the export in your shell profile to use it on every invocation.

A configured host directory takes precedence over PID_STATE_VOLUME. It is never deleted by --rebuild, --reset-state, or --reset-all; those commands still remove the applicable core and cache volumes. An explicit --rebuild replaces installed npm and Git packages and removes loose global extensions, but preserves the rest of the host-backed Pi home.

The directory contains credentials, sessions, installed packages, and other potentially sensitive data. When using Syncthing, share it only with trusted devices and avoid running Pi against the same synchronized home on multiple machines at once. Exit Pi and let Syncthing finish before switching machines.

Syncthing-managed Pi files

The three special Pi files can be supplied as absolute host paths. They are mounted read-write, so changes made by Pi are available to Syncthing:

export PID_SETTINGS_FILE="$HOME/Sync/pi/settings.json"
export PID_PROVIDERS_FILE="$HOME/Sync/pi/models.json"
export PID_AUTH_FILE="$HOME/Sync/pi/auth.json"

PID_MODELS_FILE is accepted as an alias for PID_PROVIDERS_FILE.

The files must already exist and be readable and writable. PID_AUTH_FILE must also be owned by the current user and inaccessible to group/others. New JSON files can be initialized with:

mkdir -p "$HOME/Sync/pi"
printf '{}\n' >"$HOME/Sync/pi/settings.json"
printf '{}\n' >"$HOME/Sync/pi/models.json"
printf '{}\n' >"$HOME/Sync/pi/auth.json"
chmod 600 "$HOME/Sync/pi/"*.json

Put the exports in your shell profile to apply them automatically. Package registrations are seeded initially and replaced from the rebuilt image during pid --rebuild. Other settings are preserved. Between rebuilds, packages can be managed normally with pi install and pi remove.

Credential warning: auth.json contains reusable authentication tokens. Protect the Syncthing devices and transport, keep the file at mode 600, and do not commit it to Git. Pi, extensions, and commands running in the sandbox can read and update this file.

Docker binds each configured file directly. If Syncthing atomically replaces a file while pid is already running, that container can retain the old inode. Avoid simultaneous edits, or restart pid after a Syncthing update; every new container binds the current file.

Without PID_AUTH_FILE, credentials can instead be copied once from the host's normal Pi configuration into the isolated state volume:

pid --import-auth

This reads ${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/auth.json.

Commands

Command Description
pid [PI arguments...] Update as needed and launch Pi in the current workspace. Arguments are forwarded to Pi.
pid --install Clone/reinstall the configured Git distribution and repair the launcher symlink. Does not require Docker.
pid --update Update the Git launcher, refresh the image if needed, update Pi core, and update extensions. Cannot be combined with either skip-update control.
pid --skip-update [PI arguments...] Launch without Git self-update or Pi core/extension update checks.
pid --shell Open Bash in the sandbox without performing Pi core or extension updates. Git self-update still runs unless combined as pid --skip-update --shell.
pid --claude [Claude arguments...] Launch Claude Code instead of Pi in the same sandbox. See Claude Code.
pid --rebuild Rebuild the image without cache, replace extensions, then remove the dedicated pid volumes. A configured PID_PI_HOME_DIR otherwise remains intact.
pid --import-auth Copy the host Pi auth.json into the configured Pi home.
pid --reset-state Remove volume-backed Pi home/state and the extension cache. A configured PID_PI_HOME_DIR is preserved.
pid --reset-core Remove the persistent Pi executable and its update cache.
pid --reset-all Remove the configured pid volumes and attached pid containers without rebuilding. A configured PID_PI_HOME_DIR is preserved.
pid --doctor Print Docker security, version, paths, resource limits, volumes, and external-file configuration.
pid --help Show concise command help.

Wrapper commands are selected by the first argument. --skip-update is the only prefix and must come first; for example, pid --skip-update --shell. Maintenance commands such as --update do not accept trailing arguments.

Update controls

For a one-off fully offline-style launch that skips update checks:

pid --skip-update

The equivalent environment controls are separate:

PID_SKIP_SELF_UPDATE=1 pid  # Skip only the Git launcher fetch
PID_SKIP_UPDATE=1 pid       # Skip Pi core and extension update checks

Do not combine --update with --skip-update or PID_SKIP_UPDATE=1; the launcher rejects contradictory update instructions.

PID_SKIP_UPDATE=1 still permits a required local image build when no image exists or the checked-in Dockerfile or AGENTS.md changed.

Claude Code

pid --claude launches Claude Code in the same image with the same sandbox restrictions and resource limits as Pi. Pi state, the Pi core, extension caches, and the PID_SETTINGS_FILE/PID_PROVIDERS_FILE/PID_AUTH_FILE mounts are not used. Remaining arguments are forwarded to claude:

pid --claude
pid --claude --continue
pid --skip-update --claude
  • Claude's home (~/.claude, ~/.claude.json, credentials, sessions, and the Claude Code binary) is the pid-claude-home volume, mounted at /claude-home. Set PID_CLAUDE_HOME_DIR to an absolute host directory to use that instead; it is created with mode 700 if missing.
  • On first launch Claude Code is installed with the official native installer (https://claude.ai/install.sh). Claude's own background auto-updater then keeps it current. --skip-update or PID_SKIP_UPDATE=1 sets DISABLE_AUTOUPDATER=1 for the session.
  • Log in inside the container on first use; the login persists in Claude's home. Host credentials and ANTHROPIC_API_KEY are not forwarded.
  • The repository AGENTS.md is synchronised to ~/.claude/CLAUDE.md on every launch, overwriting local edits to that file.
  • Claude runs with --dangerously-skip-permissions. Because the container user is root, IS_SANDBOX=1 is set so Claude accepts that flag. Review projects before opening them with pid --claude.

--reset-all and --rebuild also delete the pid-claude-home volume (logging Claude out and removing its sessions). A configured PID_CLAUDE_HOME_DIR is preserved.

--rebuild data loss

pid --rebuild first completes a new image build. Only after a successful build will it remove containers attached to, and then delete the configured pid volumes. Without PID_PI_HOME_DIR, their default names are:

  • pid-pi-home
  • pid-pi-core
  • pid-pi-core-cache
  • pid-pi-extension-cache

If the advanced PID_*_VOLUME variables are set, those resolved volume names are deleted instead. When PID_PI_HOME_DIR is set, the state volume is not created or deleted. Its npm and Git package directories are replaced from the image, loose global extensions are removed, and settings.packages is reset to the image package list. Credentials, sessions, and other settings are preserved.

Without a host-backed Pi home, this deletes volume-held credentials, settings, sessions, installed packages, and caches. Files supplied through PID_SETTINGS_FILE, PID_PROVIDERS_FILE/PID_MODELS_FILE, and PID_AUTH_FILE are host bind mounts and are not deleted. The next launch recreates the volumes and seeds the bundled extensions.

Environment variables

Distribution and Docker

Variable Default Purpose
PID_REPO_URL Project repository Clone URL used by --install.
PID_REPO_REF main Branch or tag used for installation and self-updates. The installer records it for later runs.
PID_INSTALL_ROOT ~/.local/share/pid Managed installation root.
PID_IMAGE pid-sandbox:latest Docker image name.
PID_IMAGE_DIR ~/.local/share/pid Manual-install Docker build context fallback. A managed Git checkout is detected automatically.
PID_DOCKER_HOST $XDG_RUNTIME_DIR/docker.sock Alternative rootless Docker endpoint, including the unix:// prefix.

External Pi home and files

Variable Container destination
PID_PI_HOME_DIR /pi-home (replaces the PID_STATE_VOLUME mount)
PID_CLAUDE_HOME_DIR /claude-home for --claude (replaces the PID_CLAUDE_STATE_VOLUME mount)
PID_SETTINGS_FILE /pi-home/agent/settings.json
PID_PROVIDERS_FILE /pi-home/agent/models.json
PID_MODELS_FILE Alias for PID_PROVIDERS_FILE
PID_AUTH_FILE /pi-home/agent/auth.json

The Pi home path must be an absolute directory without commas or newlines. The other paths must be absolute regular files with the same character restrictions and must be readable and writable. The auth file additionally requires current-user ownership and no group/other permission bits.

Resources and updates

Variable Default Purpose
PID_MEMORY 12g Normal sandbox memory limit.
PID_PIDS 1024 Normal sandbox process limit.
PID_TMP_SIZE 2g Writable /tmp tmpfs size.
PID_SHM_SIZE 256m Shared-memory size.
PID_UPDATE_MEMORY 4g Core/extension updater memory limit.
PID_SKIP_SELF_UPDATE 0 Set to 1 to skip Git launcher updates.
PID_SKIP_UPDATE 0 Set to 1 to skip Pi core and extension updates and Claude Code auto-updates.

Advanced volume names

These allow an independent set of persistent volumes. PID_STATE_VOLUME is ignored when PID_PI_HOME_DIR is set.

Variable Default
PID_STATE_VOLUME pid-pi-home
PID_CORE_VOLUME pid-pi-core
PID_CORE_CACHE_VOLUME pid-pi-core-cache
PID_EXT_CACHE_VOLUME pid-pi-extension-cache
PID_CLAUDE_STATE_VOLUME pid-claude-home

Terminal integration

The launcher forwards these terminal capability variables when set:

  • TERM, COLORTERM
  • TERM_PROGRAM, TERM_PROGRAM_VERSION
  • KITTY_WINDOW_ID, GHOSTTY_RESOURCES_DIR
  • TMUX, STY
  • PI_IMAGE_PROTOCOL, PI_TRUE_COLOR, PI_HYPERLINKS
  • NO_COLOR, FORCE_COLOR

This preserves Kitty/Ghostty and Pi terminal detection without exposing host GUI sockets. The launcher's status messages, help, and doctor report use a compact color UI on interactive terminals. Output stays free of ANSI escapes when redirected or when NO_COLOR is set; FORCE_COLOR=1 enables color explicitly.

Image contents

The image includes Node.js 24, Pi, Git/Git LFS, GitHub CLI (gh), Python 3 (with python also pointing to Python 3), compilers/build tools, ripgrep, fd, jq, ShellCheck, SQLite, network diagnostics, archive tools, and other common command-line development utilities.

Every prepared Pi home receives the repository's AGENTS.md at /pi-home/agent/AGENTS.md, where Pi loads it as global context. The launcher resynchronizes this managed copy whenever the source file changes, including for an existing Docker volume or host-backed Pi home.

The initial and post-rebuild package list is controlled by the image. Package changes made with pi install or pi remove persist during ordinary Pi and shell launches, until the next explicit rebuild. The image list contains:

  • pi-web-access
  • @juicesharp/rpiv-ask-user-question
  • @xynogen/pix-subagent
  • pi-codex-goal
  • pi-context-view
  • pi-token-speed
  • pi-zentui
  • @hk_net/pi-usage-bars
  • @vanillagreen/pi-tool-renderer
  • @paulpham157/apply-patch
  • @gotgenes/pi-anthropic-auth

pi-math and xdg-utils are intentionally not installed.

Sandbox outline

  • Rootless Docker only
  • Read-only image filesystem
  • All Linux capabilities dropped
  • no-new-privileges and Docker's seccomp profile
  • Memory, process, core-dump, temporary-filesystem, and shared-memory limits
  • The current workspace and explicitly configured Pi home, settings, models, and auth paths are bind-mounted from the host
  • Pi core is read-only in the interactive agent container
  • Persistent state uses either PID_PI_HOME_DIR or a dedicated named volume; caches use dedicated named volumes

The launcher passes --approve to Pi, so project-local Pi configuration and extensions in the mounted workspace are trusted automatically. Review projects before opening them with pid.