v1.0.0: decouple from opencode-devbox
Publish Docker Image / resolve-versions (push) Successful in 5s
Publish Docker Image / base-decide (push) Successful in 12s
Publish Docker Image / build-base (push) Successful in 45m47s
Publish Docker Image / smoke (push) Successful in 8m18s
Publish Docker Image / build-variant (push) Successful in 22m41s
Publish Docker Image / update-description (push) Failing after 9s
Publish Docker Image / promote-base-latest (push) Successful in 14s
Publish Docker Image / resolve-versions (push) Successful in 5s
Publish Docker Image / base-decide (push) Successful in 12s
Publish Docker Image / build-base (push) Successful in 45m47s
Publish Docker Image / smoke (push) Successful in 8m18s
Publish Docker Image / build-variant (push) Successful in 22m41s
Publish Docker Image / update-description (push) Failing after 9s
Publish Docker Image / promote-base-latest (push) Successful in 14s
Self-contained build chain — own Dockerfile.base + Dockerfile.variant
+ entrypoint scripts + rootfs + CI pipeline. Previously v0.79.0 and
earlier were thin re-brands of opencode-devbox's pi-only variant
(joakimp/pi-devbox:base-pi-only built by opencode-devbox CI).
Architectural changes:
- Replace 5-line Dockerfile shim with full base+variant pair.
- Adapt CI workflow from opencode-devbox/docker-publish-split.yml,
simplified to a single variant. Includes content-addressed base hash,
PI_VERSION concrete-resolution to defeat registry-buildcache footgun,
crane-based base-latest promotion, and the c6f9d11 smoke-test gate.
- pi-devbox releases no longer require rebuilding opencode-devbox first.
Base image additions:
- pandoc, graphviz, imagemagick, yq — broadly useful, ~260 MB total.
- tldr (tealdeer) — Rust port replaces Node tldr global, saves 135 MB.
- /etc/tmux.conf with base-index 0 + pane-base-index 0 — required for
the planned :latest-studio variant; pi-studio hard-codes :0.0 target.
Smoke test:
- New checks for pandoc, graphviz, imagemagick, yq, tldr, tmux config,
/tmp/sshcm directory.
- Image-size measurement now sums docker history layers (the prior
inspect --format='{{.Size}}' returned only the variant-unique layer
with the new base/variant split, understating by 2+ GB).
- Threshold 2850 → 3500 MB to absorb base additions + arch margin.
Image size:
- Local arm64 build: 3.20 GB. ~390 MB up from prior pi-only equivalent.
- Will tighten threshold once amd64 actuals settle in CI.
Pre-1.0 history preserved at tag pre-v1.0.0-decouple-backup.
Future work:
- v1.1.0: :latest-studio variant (adds pi-studio).
- v1.2.0: :latest-studio-tex variant (adds texlive-xetex for PDF).
- opencode-devbox v2.0.0 will retire INSTALL_PI / pi-only paths.
This commit is contained in:
@@ -1,277 +1,395 @@
|
||||
# pi-devbox
|
||||
|
||||
A Docker container with [pi coding-agent](https://github.com/earendil-works/pi) pre-installed, built on the [opencode-devbox](https://gitea.jordbo.se/joakimp/opencode-devbox) base image. Persistent state, full dev toolchain, MemPalace memory, and provider-agnostic LLM auth — in one `docker compose run`.
|
||||
A self-contained Docker image for running [pi](https://pi.dev) — the pi
|
||||
coding-agent — in an isolated, reproducible Linux environment with a
|
||||
curated set of developer tooling, AI memory, and shell improvements.
|
||||
|
||||
> **Hub:** [`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox) · multi-arch (amd64 + arm64)
|
||||
> **Source:** [gitea.jordbo.se/joakimp/pi-devbox](https://gitea.jordbo.se/joakimp/pi-devbox)
|
||||
|
||||
---
|
||||
pi-devbox is opinionated about what's inside but unopinionated about how
|
||||
you use it: a single `docker compose up` gives you an interactive
|
||||
container with pi, a stack of modern CLI tools, MemPalace for persistent
|
||||
agent memory across sessions, and a UID-aligned `/workspace` mount so
|
||||
files you edit inside the container appear with your normal ownership
|
||||
on the host.
|
||||
|
||||
## What's inside
|
||||
|
||||
pi-devbox is a thin re-brand of the **`pi-only` build** — it `FROM`s
|
||||
`joakimp/pi-devbox:base-pi-only` and adds no layers of its own. That base build
|
||||
is produced by opencode-devbox's CI (from `opencode-devbox/Dockerfile.variant`
|
||||
with `INSTALL_OPENCODE=false`, the single source of truth for the pi install +
|
||||
companions) but is published **into this repo** as the internal building-block
|
||||
tag `base-pi-only` — *not* under opencode-devbox, so an "opencode-devbox" tag
|
||||
never ships without opencode. Everything below is inherited from that build,
|
||||
which is lean and pi-focused — no opencode.
|
||||
### The pi coding-agent
|
||||
|
||||
Base tooling:
|
||||
- `pi` — the pi-coding-agent CLI (`@earendil-works/pi-coding-agent`)
|
||||
- `pi-toolkit` — keybindings, AWS env loader, settings template
|
||||
- `pi-extensions` — TypeScript extensions for pi (preview, MCP bridges,
|
||||
mempalace integration, etc.)
|
||||
- `pi-fork` — the `fork` tool for spawning sub-agents
|
||||
- `pi-observational-memory` — the `recall` tool for session compaction
|
||||
|
||||
- **Debian trixie** (current stable)
|
||||
- **Node.js** (LTS), **uv** (Python), **rustup** (Rust on-demand)
|
||||
- **AWS CLI v2** (with Bedrock support)
|
||||
- **MemPalace** + MCP server — persistent agent memory across sessions; queryable via `mempalace_*` tools inside pi
|
||||
- **Gitea MCP** server
|
||||
- **Dev tools**: neovim (LazyVim), tmux, bat, eza, fzf, zoxide, ripgrep, jq, git-lfs, make
|
||||
- **Shell**: bash with history tuning, prefix-search, fzf/zoxide integration
|
||||
- **Host-OS-agnostic LAN access** — on VM-backed hosts (macOS OrbStack / Docker Desktop) the entrypoint sets up the host as an SSH jump so you can reach LAN peers (`dssh` alias; `DEVBOX_LAN_ACCESS` / `HOST_SSH_USER` / `DEVBOX_LAN_AUTOJUMP_PRIVATE` env; host-owned `~/.config/devbox-shell/ssh-lan.conf` for named-peer jumps). No-op on native Linux.
|
||||
### MemPalace (AI memory)
|
||||
|
||||
pi and companions:
|
||||
- `mempalace` — local-first agent memory system (29 MCP tools)
|
||||
- `mempalace-toolkit` — bash wrappers for session/docs mining
|
||||
- ChromaDB embedding model pre-warmed at build time (`all-MiniLM-L6-v2`)
|
||||
|
||||
- **pi** ([`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent)) — baked at `/usr/bin/pi`, version pinned by the pi-only base build
|
||||
- **[pi-toolkit](https://gitea.jordbo.se/joakimp/pi-toolkit)** — mosh/tmux-friendly keybindings (Shift+Enter, Ctrl+J, Alt+J newline), AWS env loader, settings template
|
||||
- **[pi-extensions](https://gitea.jordbo.se/joakimp/pi-extensions)** — 7 extensions: `ext-toggle`, `mcp-loader`, `todo`, `ssh-controlmaster`, `notify`, `git-checkpoint`, `confirm-destructive`
|
||||
- **`fork` tool** ([pi-fork](https://github.com/elpapi42/pi-fork)) and **`recall` tool** ([pi-observational-memory](https://github.com/elpapi42/pi-observational-memory)) — baked into `/opt` and registered at runtime
|
||||
- **mempalace bridge** — auto-symlinked MCP extension so pi reads/writes the same palace as opencode-devbox's palace
|
||||
The host-mounted palace at `~/.mempalace` is shared across the host and
|
||||
this container so all your agents share one brain.
|
||||
|
||||
(opencode itself is **not** included — that's the difference from `opencode-devbox:latest-with-pi`. If you want both opencode and pi in one image, use that variant instead.)
|
||||
### Modern CLI tooling
|
||||
|
||||
The entrypoint deploys/registers all of these on first container start. Idempotent and preserves user edits.
|
||||
| Tool | Purpose |
|
||||
|---|---|
|
||||
| `nvim` | Neovim text editor |
|
||||
| `tmux` | Terminal multiplexer (configured for 0-indexed sessions) |
|
||||
| `ripgrep`, `fd` | Fast file content / filename search |
|
||||
| `fzf` | Fuzzy finder |
|
||||
| `bat` | Syntax-highlighted `cat` |
|
||||
| `eza` | Modern `ls` |
|
||||
| `zoxide` | Smart `cd` |
|
||||
| `jq`, `yq` | JSON / YAML query and transformation |
|
||||
| `tldr` (tealdeer) | Quick command examples |
|
||||
| `git`, `git-lfs`, `git-crypt` | Git + extensions |
|
||||
| `gitleaks` | Secret scanning pre-commit hook |
|
||||
| `gosu` | Privilege de-escalation in entrypoint |
|
||||
| `htop`, `tree`, `less` | Inspection utilities |
|
||||
|
||||
---
|
||||
### Document and image tooling
|
||||
|
||||
## Quick start (no git clone)
|
||||
- `pandoc` — universal Markdown↔HTML/Org/RST/etc. converter
|
||||
- `graphviz` — `dot` rendering for diagram pipelines
|
||||
- `imagemagick` — image conversion / resizing (invoked as `magick`)
|
||||
|
||||
If you just want to run pi-devbox and don't plan to modify the source, grab the two template files and go:
|
||||
### Language toolchains
|
||||
|
||||
```bash
|
||||
mkdir -p ~/pi-devbox && cd ~/pi-devbox
|
||||
- `python3` + `python3-venv` + `python3-pip` (system Python)
|
||||
- `uv` + `uvx` — fast Python package manager (preferred over pip/venv)
|
||||
- `nodejs` (v22) + `npm`
|
||||
- `gcc`, `g++`, `make` — C/C++ build tools
|
||||
- `rustup-init` — Rust toolchain installer (toolchains opt-in at runtime)
|
||||
- Optional `INSTALL_GO=true` build arg for Go
|
||||
|
||||
# Pull the docker-compose.yml and .env template
|
||||
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
|
||||
For Python REPLs and notebooks beyond the system interpreter, see the
|
||||
[uv-driven REPL recipes](#uv-driven-repl-recipes) section.
|
||||
|
||||
# Edit .env — at minimum set WORKSPACE_PATH, an LLM API key, and your git identity
|
||||
$EDITOR .env
|
||||
### Cloud + secrets
|
||||
|
||||
# Pull and run pi
|
||||
docker compose run --rm devbox pi
|
||||
```
|
||||
- AWS CLI v2 — for SSO + Bedrock auth
|
||||
- `gitea-mcp` — MCP server for Gitea API
|
||||
- `age`, `git-crypt` — encryption tooling
|
||||
|
||||
`docker compose run --rm devbox` (no command) drops you into bash; you can then run `pi`, `aws sso login`, etc. manually.
|
||||
### SSH and networking
|
||||
|
||||
To attach a second terminal to the same container (e.g. shell while pi is running):
|
||||
- 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) by
|
||||
multiplexing many ssh calls over one TCP flow.
|
||||
- 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.
|
||||
|
||||
```bash
|
||||
docker compose exec -u developer devbox bash
|
||||
```
|
||||
## Quickstart
|
||||
|
||||
---
|
||||
### Prerequisites
|
||||
|
||||
## Quick start (with git clone)
|
||||
- Docker or OrbStack (recommended on macOS)
|
||||
- Optional: AWS credentials configured on the host if you'll use the
|
||||
Bedrock LLM provider
|
||||
|
||||
If you want to follow upstream changes, run a customized fork, or rebuild the image yourself:
|
||||
### Pull and run
|
||||
|
||||
```bash
|
||||
git clone https://gitea.jordbo.se/joakimp/pi-devbox
|
||||
cd pi-devbox
|
||||
cp .env.example .env
|
||||
$EDITOR .env
|
||||
docker compose run --rm devbox pi
|
||||
cp .env.example .env # edit if needed
|
||||
docker compose up -d
|
||||
docker compose exec devbox bash
|
||||
```
|
||||
|
||||
---
|
||||
You're now in the container as user `developer` with `pi` on PATH and
|
||||
your host workspace mounted at `/workspace`.
|
||||
|
||||
## Authentication
|
||||
To start pi:
|
||||
|
||||
pi reads provider credentials from environment variables, which the container picks up from `.env` automatically.
|
||||
|
||||
### Anthropic (Claude)
|
||||
|
||||
```ini
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
```bash
|
||||
pi
|
||||
```
|
||||
|
||||
Generate a key at <https://console.anthropic.com/settings/keys>.
|
||||
First-run pi-toolkit and pi-extensions install steps run automatically
|
||||
on container start; symlinks are written to `~/.pi/agent/` on the
|
||||
named volume (so they persist across container recreations).
|
||||
|
||||
### OpenAI
|
||||
### Stop / recreate / update
|
||||
|
||||
```ini
|
||||
OPENAI_API_KEY=sk-...
|
||||
```bash
|
||||
docker compose down # stop, keep volumes
|
||||
docker compose down -v # stop, wipe per-container volumes (palace data is bind-mounted, so unaffected)
|
||||
docker compose pull # fetch latest image
|
||||
docker compose up -d --force-recreate
|
||||
```
|
||||
|
||||
### Google Gemini
|
||||
## Image variants
|
||||
|
||||
```ini
|
||||
GEMINI_API_KEY=...
|
||||
```
|
||||
Currently published:
|
||||
|
||||
### AWS Bedrock (e.g. Claude on Bedrock)
|
||||
| Tag | Includes | Size (approx.) |
|
||||
|---|---|---|
|
||||
| `joakimp/pi-devbox:latest` | base + pi + tooling | ~3.2 GB |
|
||||
| `joakimp/pi-devbox:vX.Y.Z` | pinned-version equivalent | ~3.2 GB |
|
||||
|
||||
Two paths — pick one:
|
||||
Planned for upcoming minor releases:
|
||||
|
||||
**A) Static credentials** (simplest, lower-trust environments only):
|
||||
- `joakimp/pi-devbox:latest-studio` — adds [pi-studio](https://github.com/omaclaren/pi-studio)
|
||||
for browser-based prompt editing, KaTeX/Mermaid preview, and
|
||||
literate REPLs (Shell / Python / IPython / Julia / R / GHCi /
|
||||
Clojure). Adds ~50 MB.
|
||||
- `joakimp/pi-devbox:latest-studio-tex` — also adds `texlive-xetex`
|
||||
for PDF export from Studio. Adds ~600 MB on top of `-studio`.
|
||||
|
||||
```ini
|
||||
AWS_REGION=eu-west-1
|
||||
AWS_ACCESS_KEY_ID=AKIA...
|
||||
AWS_SECRET_ACCESS_KEY=...
|
||||
```
|
||||
|
||||
**B) AWS SSO** (recommended for corporate AWS, requires mounting `~/.aws`):
|
||||
|
||||
```ini
|
||||
AWS_REGION=eu-west-1
|
||||
AWS_PROFILE=your-profile
|
||||
```
|
||||
|
||||
Then in your `docker-compose.yml`, uncomment the `~/.aws` bind-mount:
|
||||
## docker-compose.yml — basic shape
|
||||
|
||||
```yaml
|
||||
name: pi-devbox
|
||||
|
||||
services:
|
||||
devbox:
|
||||
image: joakimp/pi-devbox:latest
|
||||
container_name: pi-devbox
|
||||
stdin_open: true
|
||||
tty: true
|
||||
env_file: .env
|
||||
environment:
|
||||
- TZ=${TZ:-Europe/Stockholm}
|
||||
- TERM=xterm-256color
|
||||
- AWS_PROFILE=${AWS_PROFILE:-}
|
||||
- AWS_REGION=${AWS_REGION:-eu-west-1}
|
||||
volumes:
|
||||
# Workspace: your host source tree, read-write
|
||||
- ${HOST_WORKSPACE:-./workspace}:/workspace:rw
|
||||
# SSH keys: read-only from host
|
||||
- ${HOME}/.ssh:/home/developer/.ssh:ro
|
||||
# AWS config: read-only from host
|
||||
- ${HOME}/.aws:/home/developer/.aws:ro
|
||||
# MemPalace: bind-mounted so host pi and container pi share a brain
|
||||
- ${HOME}/.mempalace:/home/developer/.mempalace:rw
|
||||
# Per-container persistent state
|
||||
- devbox-pi-config:/home/developer/.pi
|
||||
- devbox-bash-history:/home/developer/.cache/bash
|
||||
- devbox-nvim-data:/home/developer/.local/share/nvim
|
||||
- devbox-uv-tools:/opt/uv-tools
|
||||
- devbox-chroma-cache:/home/developer/.cache/chroma
|
||||
|
||||
volumes:
|
||||
- ~/.aws:/home/developer/.aws
|
||||
devbox-pi-config:
|
||||
devbox-bash-history:
|
||||
devbox-nvim-data:
|
||||
devbox-uv-tools:
|
||||
devbox-chroma-cache:
|
||||
```
|
||||
|
||||
Inside the container, run `aws sso login` once per session. The token cache lives on the bind-mount, so subsequent `pi` invocations pick it up automatically. The pi-toolkit's `pi-env.zsh` (deployed to `~/.config/pi/`) auto-sources `AWS_PROFILE`/`AWS_REGION` whenever a shell starts.
|
||||
See `.env.example` in the repo for available environment variables.
|
||||
|
||||
### First-run pi configuration
|
||||
## uv-driven REPL recipes
|
||||
|
||||
On first start, pi reads `~/.pi/agent/settings.json` (auto-bootstrapped from the pi-toolkit template). Edit it inside the container to pick a default provider/model:
|
||||
uv is installed in the base image and is the recommended way to run
|
||||
Python interpreters and notebooks without bloating the image:
|
||||
|
||||
| Goal | One-liner |
|
||||
|---|---|
|
||||
| IPython REPL | `uv run --with ipython ipython` |
|
||||
| IPython + scientific stack | `uv run --with ipython --with numpy --with matplotlib --with pandas ipython` |
|
||||
| JupyterLab (browser, port-forward needed) | `uv run --with jupyterlab jupyter lab --no-browser --port 8888` |
|
||||
| Marimo (modern alternative) | `uv run --with marimo marimo edit --port 8889` |
|
||||
|
||||
For long-lived environments, prefer a project venv:
|
||||
|
||||
```bash
|
||||
docker compose exec -u developer devbox bash
|
||||
$EDITOR ~/.pi/agent/settings.json
|
||||
cd /workspace/myproj
|
||||
uv init && uv add ipython numpy matplotlib
|
||||
# then:
|
||||
uv run ipython
|
||||
```
|
||||
|
||||
The file is rewritten by pi at runtime (e.g. `lastChangelogVersion`), so it lives on the `devbox-pi-config` named volume — your edits persist across container recreation.
|
||||
`pyproject.toml` + `uv.lock` then capture the dependency state and
|
||||
travel with the project in git.
|
||||
|
||||
For pi's full configuration model (provider list, model overrides, MCP integration, themes, extensions): <https://github.com/earendil-works/pi#configuration>.
|
||||
uv only manages Python. For other languages:
|
||||
|
||||
---
|
||||
| Toolchain | How to add |
|
||||
|---|---|
|
||||
| R | `sudo apt-get install r-base-core` (~200 MB) |
|
||||
| GHCi (Haskell) | `sudo apt-get install ghc` (~700 MB) |
|
||||
| Clojure | `sudo apt-get install clojure` (~150 MB + JVM) |
|
||||
| Julia | `juliaup` is planned for an upcoming release |
|
||||
|
||||
## Persistent state
|
||||
These are runtime opt-ins and persist only in the container's writable
|
||||
layer — they don't survive `docker compose down -v` or image updates.
|
||||
|
||||
Persistent state is what makes the difference between "use this once" and "make it my long-term coding environment". Everything important survives `docker compose down` and image upgrades; only `docker compose down -v` wipes the volumes.
|
||||
## tldr — first-run cache
|
||||
|
||||
| Volume | Mount point | What survives | Notes |
|
||||
|---|---|---|---|
|
||||
| `devbox-pi-config` | `/home/developer/.pi/` | pi settings.json, extension toggles, sessions, user-installed pi packages | `NPM_CONFIG_PREFIX` set inside the container so `pi install npm:…` and `npm install -g` lands here automatically |
|
||||
| `devbox-ssh-local` | `/home/developer/.ssh-local` | generated LAN-jump keypair + known_hosts | Authorize the jump key on the host **once per machine**; persisting it avoids re-authorizing after every update (see opencode-devbox README → *Reaching your LAN*) |
|
||||
| `devbox-shell-history` | `/home/developer/.cache/bash` | bash history | Across container recreate |
|
||||
| `devbox-zoxide` | `/home/developer/.local/share/zoxide` | zoxide directory jump history | The `z`/`zi` shortcuts remember where you've been |
|
||||
| `devbox-nvim-data` | `/home/developer/.local/share/nvim` | neovim plugin & Mason package state | LazyVim plugins persist |
|
||||
| `devbox-uv` | `/home/developer/.local/share/uv` | uv-managed Python installs and tool cache | `uv tool install` results live here |
|
||||
|
||||
### Optional persistent volumes
|
||||
|
||||
These are commented out in `docker-compose.yml` by default. Uncomment them if you want the corresponding state to persist:
|
||||
|
||||
| Volume | Mount point | What survives |
|
||||
|---|---|---|
|
||||
| `devbox-palace` | `/home/developer/.mempalace` | MemPalace data — drawers, knowledge graph, embeddings. Treat as primary storage if you rely on agent memory. |
|
||||
| `devbox-chroma-cache` | `/home/developer/.cache/chroma` | ChromaDB embedding model cache (~80 MB; disposable, re-downloads in seconds) |
|
||||
|
||||
### Workspace bind mount
|
||||
|
||||
`/workspace` is bind-mounted from `WORKSPACE_PATH` on the host (default `~/projects`). Source code never lives inside the container — your editor on the host and pi inside the container see the same files.
|
||||
|
||||
### SSH keys (read-only)
|
||||
|
||||
`~/.ssh` is mounted read-only at `/home/developer/.ssh` for git push/pull. The container does **not** write to it.
|
||||
|
||||
---
|
||||
|
||||
## Configuration reference
|
||||
|
||||
All config flows through `.env`. The full list (with annotations) is in [`.env.example`](https://gitea.jordbo.se/joakimp/pi-devbox/src/branch/main/.env.example). Here's the most relevant subset:
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `WORKSPACE_PATH` | `~/projects` | Host path mounted as `/workspace` |
|
||||
| `SSH_KEY_PATH` | `~/.ssh` | Host path for SSH keys (read-only) |
|
||||
| `GIT_USER_NAME` | (empty) | Sets `git config --global user.name` inside container |
|
||||
| `GIT_USER_EMAIL` | (empty) | Sets `git config --global user.email` inside container |
|
||||
| `ANTHROPIC_API_KEY` | (unset) | Anthropic provider auth |
|
||||
| `OPENAI_API_KEY` | (unset) | OpenAI provider auth |
|
||||
| `GEMINI_API_KEY` | (unset) | Google Gemini auth |
|
||||
| `AWS_PROFILE` / `AWS_REGION` | (unset) | AWS Bedrock SSO flow |
|
||||
| `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | (unset) | AWS Bedrock static creds |
|
||||
| `GITEA_ACCESS_TOKEN` / `GITEA_HOST` | (unset) | Gitea MCP server (optional) |
|
||||
| `GITHUB_PERSONAL_ACCESS_TOKEN` | (unset) | GitHub MCP server / git ops over HTTPS |
|
||||
| `DEVBOX_LAN_ACCESS` | `auto` | LAN-access mode: `auto` (jump only on VM-backed hosts), `jump`, `off` |
|
||||
| `HOST_SSH_USER` | (unset) | Host username for the LAN SSH jump (see opencode-devbox README) |
|
||||
| `DEVBOX_LAN_AUTOJUMP_PRIVATE` | `0` | `1` = ProxyJump any private (RFC1918) IP through the host (roaming-friendly; see opencode-devbox README) |
|
||||
| `LANG` / `LANGUAGE` / `LC_ALL` | `en_US.UTF-8` | Locale override |
|
||||
|
||||
---
|
||||
|
||||
## Versioning
|
||||
|
||||
Tags follow the pi npm package version: `v0.74.0`, `v0.75.0`, … `latest` always points at the most recent successful release.
|
||||
|
||||
Container-level rebuilds on the same pi version (security updates, base bumps, fixes) get a letter suffix: `v0.74.0b`, `v0.74.0c`, …
|
||||
|
||||
The pi binary is inherited from `joakimp/pi-devbox:base-pi-only`, so a release of this image must be preceded by an opencode-devbox release that bakes the target pi version into `base-pi-only`. The smoke test enforces this (it asserts `pi --version` matches the tag).
|
||||
|
||||
---
|
||||
|
||||
## Building from source
|
||||
|
||||
This image is a thin re-brand of the pi-only variant, so building it just pulls
|
||||
the base. To pin a specific pi-only build or hack on it:
|
||||
The `tldr` command (provided by tealdeer) shows a "Page cache not
|
||||
found" message on first invocation. To populate the cache:
|
||||
|
||||
```bash
|
||||
git clone https://gitea.jordbo.se/joakimp/pi-devbox
|
||||
cd pi-devbox
|
||||
|
||||
# Default tracks base-pi-only; override BASE_IMAGE to pin a build:
|
||||
docker compose build \
|
||||
--build-arg BASE_IMAGE=joakimp/pi-devbox:base-pi-only-v1.15.13c
|
||||
|
||||
docker compose up -d
|
||||
tldr --update
|
||||
```
|
||||
|
||||
To change the pi version, the pi extensions, or the install logic, edit
|
||||
`opencode-devbox/Dockerfile.variant` (the single source of truth) and release
|
||||
opencode-devbox — not this repo.
|
||||
This fetches ~1500 command pages from the [tldr-pages](https://tldr.sh)
|
||||
project and caches them in `~/.cache/tealdeer/`. After that, `tldr ls`,
|
||||
`tldr docker`, etc. work instantly. Re-run `tldr --update` periodically
|
||||
to refresh.
|
||||
|
||||
Build args supported:
|
||||
## Volumes and persistence
|
||||
|
||||
| Arg | Default | Effect |
|
||||
| Path inside container | Volume | What survives |
|
||||
|---|---|---|
|
||||
| `BASE_IMAGE` | `joakimp/pi-devbox:base-pi-only` | Parent image (internal building-block tag) — set to a `:base-pi-only-vX.Y.Z` tag or a digest for reproducible builds |
|
||||
| `/workspace` | host bind-mount | host filesystem |
|
||||
| `~/.ssh` | host bind-mount (read-only) | host filesystem |
|
||||
| `~/.aws` | host bind-mount (read-only) | host filesystem |
|
||||
| `~/.mempalace` | host bind-mount | host filesystem |
|
||||
| `~/.pi` | named volume `devbox-pi-config` | `down -v` wipes |
|
||||
| `~/.cache/bash` | named volume | `down -v` wipes |
|
||||
| `~/.local/share/nvim` | named volume | `down -v` wipes |
|
||||
| `/opt/uv-tools` | named volume | `down -v` wipes |
|
||||
| `~/.cache/chroma` | named volume | `down -v` wipes |
|
||||
|
||||
---
|
||||
Anything not on a volume is on the writable layer and is lost on
|
||||
container recreate.
|
||||
|
||||
## MemPalace integration
|
||||
|
||||
MemPalace is installed in the base image and pre-warmed with the
|
||||
ChromaDB ONNX embedding model so first-time semantic search is
|
||||
instant.
|
||||
|
||||
The palace data lives at `~/.mempalace/palace` on the host
|
||||
(bind-mounted into the container). This means:
|
||||
|
||||
- A pi running on the host and a pi running inside this container see
|
||||
the same palace.
|
||||
- SQLite's WAL mode handles concurrent reads + single writer cleanly,
|
||||
so simultaneous use is safe in practice.
|
||||
|
||||
`mempalace-session` and `mempalace-docs` are on PATH for one-off
|
||||
session/docs mining; the 29 MCP tools (search, kg-query, drawer-add,
|
||||
diary-write, etc.) are wired into pi automatically by the pi-extensions
|
||||
mempalace bridge.
|
||||
|
||||
## SSH and ControlMaster
|
||||
|
||||
The base image preconfigures `Host *` ssh defaults:
|
||||
|
||||
```
|
||||
ControlMaster auto
|
||||
ControlPath /tmp/sshcm/%r@%h:%p
|
||||
ControlPersist 10m
|
||||
```
|
||||
|
||||
The socket directory `/tmp/sshcm/` is created mode 700 on every
|
||||
container start (per-container, tmpfs-friendly). Multiple ssh calls
|
||||
to the same host within 10 minutes reuse the master TCP flow —
|
||||
important on residential ISPs with CGNAT per-destination flow caps
|
||||
(~4 flows on most European broadband; symptoms are
|
||||
`kex_exchange_identification: Connection closed by remote host` on
|
||||
the 5th+ concurrent ssh).
|
||||
|
||||
User-level overrides in `~/.ssh/config` win because Debian's
|
||||
`/etc/ssh/ssh_config` includes `/etc/ssh/ssh_config.d/*.conf` before
|
||||
the `Host *` block.
|
||||
|
||||
## tmux and 0-indexed sessions
|
||||
|
||||
The image installs `/etc/tmux.conf` with:
|
||||
|
||||
```
|
||||
set -g base-index 0
|
||||
set -g pane-base-index 0
|
||||
```
|
||||
|
||||
This is the default tmux indexing. It's baked here because `pi-studio`
|
||||
(planned for `:latest-studio`) hard-codes its tmux send target to
|
||||
`<session>:0.0`. If you override `base-index` to 1 in a personal
|
||||
`~/.tmux.conf`, pi-studio will fail with "can't find window: 0".
|
||||
|
||||
## AWS Bedrock auth
|
||||
|
||||
If you use Bedrock as pi's LLM provider:
|
||||
|
||||
1. Configure SSO on the host: `aws configure sso`
|
||||
2. Bind-mount `~/.aws:/home/developer/.aws:ro`
|
||||
3. Set `AWS_PROFILE` and `AWS_REGION` in `.env`
|
||||
4. Inside the container: `aws sso login` if needed; pi picks up the
|
||||
profile via the env vars.
|
||||
|
||||
The pi-toolkit AWS env loader (in `~/.pi/agent/`) prepares Bedrock
|
||||
inference-profile model IDs (with `eu.` / `us.` prefixes) automatically.
|
||||
|
||||
## Build pipeline
|
||||
|
||||
pi-devbox is built from this repo's CI in two phases:
|
||||
|
||||
1. **Base** (`Dockerfile.base`) — produces `joakimp/pi-devbox:base-<hash>`
|
||||
where `<hash>` is content-addressed over `Dockerfile.base`,
|
||||
`rootfs/`, and `entrypoint*.sh`. Rebuilt only when these change.
|
||||
2. **Variant** (`Dockerfile.variant`) — `FROM ${BASE_IMAGE}` and adds
|
||||
the pi install. The `:latest` and `vX.Y.Z` tags are produced from
|
||||
this layer; future Studio variants will extend further.
|
||||
|
||||
Tag naming:
|
||||
|
||||
| Tag | Stage |
|
||||
|---|---|
|
||||
| `base-<hash>` | base image — internal building block |
|
||||
| `base-latest` | promoted alias of the most recent base |
|
||||
| `latest`, `vX.Y.Z` | variant: base + pi |
|
||||
|
||||
CI resolves `PI_VERSION` to a concrete version string before building
|
||||
to defeat a registry-buildcache hit on `npm install -g
|
||||
pi-coding-agent@latest` (the build-arg string would otherwise be
|
||||
byte-identical across releases and the layer would silently reuse the
|
||||
previous version's bytes).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**`pi --version` works but `pi` exits immediately.** First-run config probably hasn't been done. `docker compose exec -u developer devbox bash`, edit `~/.pi/agent/settings.json`, ensure a provider is set and the matching API key is exported.
|
||||
### Image grew unexpectedly
|
||||
|
||||
**AWS SSO token expired.** `aws sso login` from inside the container. The token cache is on the `~/.aws` bind-mount, so it persists; expiration is the issue.
|
||||
`docker history joakimp/pi-devbox:latest` shows per-layer sizes. The
|
||||
biggest layers are typically the apt block (~600 MB), pi npm install
|
||||
(~330 MB), MemPalace + ChromaDB (~315 MB), AWS CLI (~270 MB), Node.js
|
||||
(~200 MB).
|
||||
|
||||
**Anthropic 401 / OpenAI 401.** Check the `.env` value made it in: `docker compose exec devbox env | grep ANTHROPIC` (etc).
|
||||
### pi can't reach LAN peers on macOS
|
||||
|
||||
**`pi` prompts for an extension/MCP server you don't recognize.** Either toggle it off via `/ext` inside pi, or rename the file: `mv ~/.pi/agent/extensions/<name>.ts{,.off}`.
|
||||
The LAN-access helper (`/usr/local/lib/pi-devbox/setup-lan-access.sh`)
|
||||
auto-runs on container start and writes `~/.ssh-local/config` with a
|
||||
ssh-jump-via-host configuration. Set `DEVBOX_LAN_ACCESS=jump` and
|
||||
`HOST_SSH_USER=<your-mac-user>` in `.env` if auto-detection fails.
|
||||
|
||||
**Container won't start, error about `/workspace`.** `WORKSPACE_PATH` in `.env` doesn't exist on the host. Create the directory or fix the path.
|
||||
### Smoke-testing a local build
|
||||
|
||||
**Pi-toolkit symlinks lost after `docker compose down -v`.** That's expected — `-v` wipes named volumes. Don't do it unless you mean it. Container recreation without `-v` (the default) preserves all state.
|
||||
```bash
|
||||
./scripts/smoke-test.sh joakimp/pi-devbox:latest
|
||||
```
|
||||
|
||||
---
|
||||
## Versioning and release
|
||||
|
||||
## Related
|
||||
pi-devbox follows semver-ish:
|
||||
|
||||
- **[opencode-devbox](https://gitea.jordbo.se/joakimp/opencode-devbox)** — the base image. Use this if you want both opencode and pi (it has a `latest-with-pi` variant) or just opencode.
|
||||
- **[pi-toolkit](https://gitea.jordbo.se/joakimp/pi-toolkit)** — keybindings, env loader, settings template. Cloned into `/opt/pi-toolkit` at image build time and `install.sh` runs on container start.
|
||||
- **[pi-extensions](https://gitea.jordbo.se/joakimp/pi-extensions)** — extension source. Same install pattern.
|
||||
- **[mempalace-toolkit](https://gitea.jordbo.se/joakimp/mempalace-toolkit)** — MemPalace bring-up. The `mempalace.ts` extension symlinked into `~/.pi/agent/extensions/` comes from here.
|
||||
- **[pi (upstream)](https://github.com/earendil-works/pi)** — the coding-agent itself.
|
||||
- **Major** — architectural changes. `v1.0.0` is the first decoupled
|
||||
release (independent of opencode-devbox).
|
||||
- **Minor** — new variants, significant base additions.
|
||||
- **Patch** — pi version bumps, smaller fixes.
|
||||
|
||||
---
|
||||
The `pi --version` inside the image is asserted by smoke tests to
|
||||
match the release tag's pi component, so version drift between the
|
||||
image and the tag is caught at CI time.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
pi-devbox was originally a thin re-brand of the `pi-only` variant of
|
||||
[opencode-devbox](https://gitea.jordbo.se/joakimp/opencode-devbox).
|
||||
It was decoupled at `v1.0.0` so it could evolve at its own pace, with
|
||||
self-contained docs and a focused, pi-centric image. Significant base
|
||||
infrastructure (the SSH ControlMaster setup, MemPalace integration,
|
||||
the entrypoint UID/GID dance) was adopted from there.
|
||||
|
||||
The pi coding-agent itself is [@earendil-works/pi-coding-agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent).
|
||||
|
||||
## License
|
||||
|
||||
MIT (this image and its source). Pi and the bundled tools each carry their own licenses.
|
||||
MIT
|
||||
|
||||
Reference in New Issue
Block a user