Files
pi-devbox/DOCKER_HUB.md
T
Joakim Persson 12f99c49e3
Lint / skill-floor (push) Successful in 7s
Lint / hadolint (push) Successful in 15s
Lint / actionlint (push) Successful in 17s
Lint / doc-drift (push) Successful in 14s
Publish Docker Image / lint-gate (push) Successful in 21s
Publish Docker Image / resolve-versions (push) Successful in 10s
Publish Docker Image / base-decide (push) Successful in 9s
Publish Docker Image / build-base (push) Successful in 55m32s
Publish Docker Image / smoke (push) Failing after 8m50s
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Publish Docker Image / smoke-studio (push) Failing after 9m55s
Publish Docker Image / build-variant-studio (push) Has been skipped
docs(v1.10.0): adopt pi's fullscreen default and document the way back
Decision: keep pi 1.0.0's default (tuiMode "fullscreen"). No tuiMode is baked,
so the image follows upstream rather than pinning the fleet to either mode -
but "we inherited a changed default" is only acceptable if the revert is
written down, so it now is.

README gains a "Terminal UI mode" section ahead of the pi-atelier section,
because the two interact. It gives all three scopes, taken from pi 1.0.0's own
docs/settings.md and docs/cli.md rather than from the changelog prose:

  - permanent: "tuiMode": "regular" in ~/.pi/agent/settings.json
  - one session: pi --tui-mode regular   (the flag is real: cli.md:221)
  - one project: the same key in .pi/settings.json, which overrides the agent
    directory

It also documents the four related settings (fullscreenExitOutput,
fullscreenScrollbar, fullscreenCopyOnSelect, fullscreenWheelScrollLines) and
two things specific to this image:

  - tmux: fullscreen uses the alternate screen, so tmux copy-mode shows the
    pane history AROUND pi, not pi's transcript. That is the concrete reason
    someone here would want "regular" back.
  - fullscreenWheelScrollLines "auto" behaves differently over SSH (caps fast
    wheel spins at 6 lines/event) than in a local macOS terminal (1 line) -
    worth naming because this container is normally driven over SSH.
  - pi-atelier works in BOTH modes (upstream has handled regular vs fullscreen
    renderers separately since pi 0.84), so reverting costs nothing. Stated so
    nobody assumes the sidebar is the price of the old scrollback.

The python3 merge snippet in that section was RUN before being documented:
against a realistic settings.json under an overridden HOME, twice, confirming
it is idempotent and preserves sibling keys including nested objects and the
_comment fields the seeded file uses. Documented code that has never been
executed is a guess.

DOCKER_HUB.md gets a short version of the same note, because CI reads it from
the TAG and POSTs it to Docker Hub as full_description - a behaviour change
this visible should not require reading the repo to undo.

Gates: doc-drift 23 OK / 0 DRIFT / 0 SKIP / 0 FAIL; base-hash, workflow-shell,
skill-floor, lint-shell all rc=0.
2026-10-02 09:00:51 +02:00

171 lines
11 KiB
Markdown

# pi-devbox
A self-contained Docker container for the [pi coding-agent](https://github.com/earendil-works/pi) — pi + companion repos + MemPalace + a curated set of dev tooling, ready to run.
> **Current `:latest` ships pi `{{PI_VERSION}}`** (resolved at build time; see [Versioning](#versioning)).
## Image variants
| Tag | Architectures | Size (compressed) | What you get |
|---|---|---|---|
| `joakimp/pi-devbox:latest` | amd64, arm64 | ~1.23 GB | Self-contained: base + pi `{{PI_VERSION}}` + companions |
| `joakimp/pi-devbox:vX.Y.Z` | amd64, arm64 | same | Pinned semver release |
| `joakimp/pi-devbox:latest-studio` | amd64, arm64 | ~1.25 GB | `latest` + [pi-studio](https://github.com/omaclaren/pi-studio): browser prompt editor, KaTeX/Mermaid preview, tmux-backed literate REPLs |
| `joakimp/pi-devbox:vX.Y.Z-studio` | amd64, arm64 | same | Pinned semver studio release |
| `joakimp/pi-devbox:base-latest` | amd64, arm64 | ~1.17 GB | Base layer alias (internal building block; pull `:latest` instead) |
| `joakimp/pi-devbox:base-<hash>` | amd64, arm64 | ~1.17 GB | Content-addressed base; immutable. Stable parent for variant rebuilds. |
> **pi-studio (`-studio` tags):** launch with `/studio --no-browser --port 8765` inside a pi session. The server binds `127.0.0.1` **inside the container**, so reach it via host networking or a loopback bridge (and `ssh -L` for a remote host; mosh needs a parallel `ssh -L`). Full recipe: [README → Using pi-studio](https://gitea.jordbo.se/joakimp/pi-devbox#using-pi-studio--studio-variant).
## Quick start
One-shot, no persistence:
```bash
docker run -it --rm \
-v "$PWD":/workspace \
-v "$HOME/.ssh":/home/developer/.ssh:ro \
-e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
joakimp/pi-devbox:latest pi
```
For a fully-configured environment with persistent settings, MemPalace memory, neovim plugins, and shell history surviving container recreation, use docker-compose. **You don't need to clone the repo** — just grab two template files:
```bash
mkdir -p ~/pi-devbox && cd ~/pi-devbox
curl -O https://gitea.jordbo.se/joakimp/pi-devbox/raw/branch/main/docker-compose.yml
curl -fsSL https://gitea.jordbo.se/joakimp/pi-devbox/raw/branch/main/.env.example -o .env
# Edit .env — set WORKSPACE_PATH, an LLM API key (ANTHROPIC_API_KEY,
# OPENAI_API_KEY, GEMINI_API_KEY, or AWS_*), and your git identity.
docker compose run --rm devbox pi
```
Full setup guide — authentication for each provider (Anthropic, OpenAI, Gemini, AWS Bedrock SSO + static), persistence model, configuration reference, build args, troubleshooting: **<https://gitea.jordbo.se/joakimp/pi-devbox#readme>**
## What's inside
### pi and companions
- **pi `{{PI_VERSION}}`** ([`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent)) — installed at `/usr/bin/pi`, pinned to an audited version (not npm `latest`)
- **pi-atelier** — TUI sidebar (ordered panels, split-pane, themes), vendored at `/opt/pi-atelier` and pinned to an audited tag; the exact tag is in the image labels (`se.jordbo.pi-devbox.pi-atelier-version`) and `/etc/pi-devbox/build-manifest.json`
- **[pi-toolkit](https://gitea.jordbo.se/joakimp/pi-toolkit)** — keybindings (mosh/tmux-friendly Shift+Enter, Ctrl+J, Alt+J newline bindings), AWS env loader, settings template
- **[pi-extensions](https://gitea.jordbo.se/joakimp/pi-extensions)** — 7 user-facing extensions: `ext-toggle`, `mcp-loader`, `todo`, `ssh-controlmaster`, `notify`, `git-checkpoint`, `confirm-destructive`
- **`fork`** ([pi-fork](https://github.com/elpapi42/pi-fork)) and **`recall`** ([pi-observational-memory](https://github.com/elpapi42/pi-observational-memory)) tools
- **mempalace bridge** — MCP extension auto-symlinked so pi reads/writes the host-mounted palace
- **image-baked agent skills** — skills under `/usr/local/share/pi-devbox/skills/` (e.g. `pi-devbox-environment`, which teaches agents the container's persistence/networking/DNS/tmux/REPL specifics) are symlinked into `~/.agents/skills/` on start, available with or without a mounted skillset repo
The entrypoint deploys/registers all of these on first container start. Re-running is idempotent and preserves user edits.
**Terminal UI mode — fullscreen by default.** pi 1.0.0 made the TUI fullscreen, and this image adopts upstream's default. Fullscreen uses the terminal's alternate screen, so the transcript no longer lands in your terminal's (or tmux's) native scrollback. To get the previous behaviour back, set `"tuiMode": "regular"` in `~/.pi/agent/settings.json`, or pass `pi --tui-mode regular` for a single session. The bundled pi-atelier sidebar works in both modes. See the README for the related `fullscreenExitOutput` / `fullscreenScrollbar` / `fullscreenCopyOnSelect` / `fullscreenWheelScrollLines` settings — the last one behaves differently over SSH, which is how this container is usually driven.
### MemPalace (persistent agent memory)
- **MemPalace** + MCP server — semantic search over conversation history, knowledge graph, diary; queryable via 29 `mempalace_*` tools inside pi
- ChromaDB ONNX embedding model pre-warmed at build time (`all-MiniLM-L6-v2`)
- Bind-mount your host's `~/.mempalace` and the host-pi and container-pi share one brain
### Document and image tooling
- **pandoc** — universal Markdown↔HTML/Org/RST/etc. conversion. Useful well beyond pi: agent-driven doc exports, format conversion, etc.
- **Typst** — markup-based typesetting, used as pandoc's `--pdf-engine`
- **graphviz** (`dot`) — diagram rendering pipelines
- **imagemagick** (`magick`) — image conversion / resizing
### Browser automation
- **agent-browser** — CLI for driving a real browser (open pages, click/fill/`eval`, snapshot the DOM, screenshots) so agents can verify front-end work instead of guessing
- **Playwright** + a headless **Chromium** are pre-installed and pinned together; `AGENT_BROWSER_EXECUTABLE_PATH` is preset to the baked browser, so `agent-browser open <url>` works out of the box with no setup
- **socat** — TCP bridge used to expose the pi-studio server outside the container's loopback
### Modern CLI tooling
- **Editor**: neovim (system-wide `termguicolors` default; bring your own config/plugins), tmux (configured for 0-indexed sessions)
- **Search/nav**: ripgrep, fd, fzf, zoxide
- **Display**: bat, eza, htop, tree
- **Data**: jq, yq, sqlite3, bc/dc, column
- **Help**: tldr (tealdeer — Rust port; run `tldr --update` once to populate cache)
- **Git**: git-lfs, git-crypt, gitleaks (for pre-commit secret scanning)
- **Build**: gcc, g++, make, patch
- **Misc**: gosu, age, rsync, less
### Language toolchains
- **Python**: system Python 3 + **uv** (preferred) for fast Python package management. Run any Python REPL/notebook stack on demand without bloating the image:
```bash
uv run --with ipython ipython
uv run --with jupyterlab jupyter lab --no-browser --port 8888
uv run --with marimo marimo edit
```
- **Node.js** v24 LTS + npm (used by pi itself)
- **Rust** — `rustup-init` is on PATH; install toolchains on demand
- **Go** — opt-in via `--build-arg INSTALL_GO=true` if rebuilding from source
### Cloud + secrets
- **AWS CLI v2** — for SSO + Bedrock auth (pi's preferred LLM provider for the maintainer's setup)
- **Gitea MCP** server — for Gitea API access from inside pi
- **age**, **git-crypt** — encryption tooling
### SSH and networking
- OpenSSH client with **ControlMaster auto** preconfigured on a writable socket path (`/tmp/sshcm/`). Mitigates ssh banner-exchange failures behind CGNAT-restricted residential ISPs (~4-flow caps). A read-only `~/.ssh` carrying a per-host `ControlPath` (common CGNAT configs) is handled too — redirected to a writable socket dir for both `pi --ssh` and `dssh`/`dscp`.
- A **LAN-access helper** that auto-configures ssh jump-via-host on VM-backed hosts (OrbStack / Docker Desktop on macOS) so the container can reach the host's directly-attached LAN peers (`dssh <peer>` alias; `DEVBOX_LAN_ACCESS` / `HOST_SSH_USER`).
## Versioning
From v1.0.0 onward, pi-devbox uses **semver**:
- **Major** — architectural changes. v1.0.0 is the first decoupled release, where pi-devbox got its own self-contained build chain (previously it was a thin re-brand of opencode-devbox's `pi-only` variant).
- **Minor** — new image variants, significant base additions.
- **Patch** — pi version bumps, smaller fixes.
The pi binary version inside any given release is shown in this description (currently **`{{PI_VERSION}}`** for `:latest`) and asserted by smoke tests to match what's documented — version drift is caught at CI time, not on user pull.
> **Pre-v1.0.0 history.** Tags v0.74.0…v0.79.0 followed the pi npm version directly (`v{pi_version}[letter]`). Those images remain on Hub but are deprecated in favor of `:latest` / `:v1.X.Y`. The legacy `:base-pi-only*` tags were CI artifacts of the old opencode-devbox-based build pipeline; they will be removed in a future opencode-devbox v2.0.0.
### Build pipeline
pi-devbox is built in two phases:
1. **Base** (`Dockerfile.base`) → `base-<hash>` tag, content-addressed over `Dockerfile.base` + `rootfs/` + `entrypoint*.sh`. Rebuilt only when those change.
2. **Variant** (`Dockerfile.variant`) → `:latest` and `:vX.Y.Z`. FROMs the base, adds the pi install + companions.
`base-latest` is an alias of the most recent base.
## Persistent state
User edits and pi-installed packages survive container recreation when you mount these named volumes. Use the included `docker-compose.yml` and they're set up automatically.
| Volume | Mount point | What it holds |
|---|---|---|
| `devbox-pi-config` | `/home/developer/.pi/` | pi settings, extension toggles, sessions, user-installed pi packages (`npm install -g`, `pi install npm:…`) |
| `devbox-shell-history` | `/home/developer/.cache/bash` | bash history |
| `devbox-zoxide` | `/home/developer/.local/share/zoxide` | zoxide directory jump database |
| `devbox-nvim-data` | `/home/developer/.local/share/nvim` | neovim plugin & Mason package state |
| `devbox-uv` | `/home/developer/.local/share/uv` | uv Python installs and tool cache |
| `devbox-ssh-local` | `/home/developer/.ssh-local` | LAN-jump key (one-time host authorization survives recreate) |
Optional volumes for MemPalace (commented out by default — uncomment in `docker-compose.yml` to persist conversation memory across restarts):
| Volume | Mount point | What it holds |
|---|---|---|
| `devbox-palace` | `/home/developer/.mempalace` | palace data (drawers, knowledge graph, embeddings) |
| `devbox-chroma-cache` | `/home/developer/.cache/chroma` | ChromaDB embedding model cache (~80 MB, can be rebuilt) |
## User-installed pi packages
`NPM_CONFIG_PREFIX` is set inside the container to `/home/developer/.pi/npm-global`. Anything you `pi install npm:<pkg>` or `npm install -g` lands on the `devbox-pi-config` named volume — survives container recreation **and** image rebuilds. A user-installed `pi` wins over the baked one via `PATH` order, so you can pin a different pi version without rebuilding the image.
## Source
- **This image**: https://gitea.jordbo.se/joakimp/pi-devbox
- **pi**: https://github.com/earendil-works/pi
- **pi-toolkit**: https://gitea.jordbo.se/joakimp/pi-toolkit
- **pi-extensions**: https://gitea.jordbo.se/joakimp/pi-extensions
- **MemPalace**: https://github.com/MemPalace/mempalace
## License
MIT (the image; pi and the bundled tools each carry their own licenses). See
`LICENSE` and `THIRD_PARTY.md` in the [source repo](https://gitea.jordbo.se/joakimp/pi-devbox).