Files
opencode-devbox/CHANGELOG.md
T
joakimp 6fde27c212
Validate / docs-check (push) Successful in 16s
Validate / validate-base (push) Successful in 12m9s
Validate / validate-omos (push) Successful in 16m45s
Validate / validate-with-pi (push) Successful in 13m30s
Validate / validate-omos-with-pi (push) Successful in 15m15s
Document the build pipeline architecture in .gitea/README.md
The split-base build architecture, the NPM_CONFIG_PREFIX gotcha, the
hash-driven base cache reuse mechanism, and the cutover plan from
docker-publish.yml to docker-publish-split.yml were previously
scattered across:
  - inline Dockerfile.base / Dockerfile.variant comments
  - CHANGELOG Unreleased entries
  - AGENTS.md mentions
  - docker-publish-split.yml header comment
  - my own session notes

Consolidate into .gitea/README.md as the canonical architectural doc.
Gitea (like GitHub) auto-renders this when navigating to .gitea/ in
the web UI, so anyone investigating 'why is CI shaped this way?'
finds it on the first click. Cross-referenced from AGENTS.md as the
first thing to read when touching CI.

Covers:
  - The two release pipelines and why both exist
  - Why split-base: cross-variant cache misses on layer-hash-divergence
  - The 6 phases of the split-base pipeline with an ASCII diagram
  - base-decide hash inputs and Docker Hub probe logic
  - NPM_CONFIG_PREFIX variant-override pattern (the volume-shadow trap)
  - Registry cache strategy (mode=max for cross-arch reuse)
  - Wall-clock estimates: version-bump vs base-touching releases
  - Validate workflow role
  - Runner expectations: catthehacker image, disk reclaim, concurrency,
    Gitea Actions @v4 artifact incompatibility
  - 4-step migration plan from docker-publish.yml to .split.yml
  - Cross-refs to related docs

Does not duplicate AGENTS.md content; links to it for domain facts and
release-day checklist.
2026-05-09 19:28:03 +02:00

39 KiB
Raw Blame History

Changelog

All notable changes to the opencode-devbox container image.

Tags follow v{opencode_version}[letter] — bare tag for the first build on a new opencode release, letter suffix (b, c, …) for container-level rebuilds on the same version. See AGENTS.md for details.


Unreleased

Docs:

  • New: .gitea/README.md — architectural overview of the build pipeline. Documents the production single-Dockerfile path vs the merged-but-unvalidated split-base path, hash-driven base cache reuse, wall-clock estimates, the NPM_CONFIG_PREFIX variant-override pattern, runner expectations (catthehacker container, disk reclaim, concurrency, gitea-actions @v4 artifact gotcha), and the cutover plan. Auto-renders when navigating to .gitea/ in the gitea web UI. Linked from AGENTS.md as the first thing to read when touching CI.

Build pipeline (merged to main as Dockerfile.base + Dockerfile.variant + .gitea/workflows/docker-publish-split.yml, NOT yet validated end-to-end — the workflow_dispatch test against :base-<hash> + :v0.0.0-split-test* aliases is still the gating step before this can take over on: push: tags: v*):

  • New: split-base build pipeline. Dockerfile.base (variant-independent layers — apt, locales, AWS CLI, Node.js, mempalace, gitea-mcp, user setup, chromadb prewarm, ENVs, entrypoints) builds once and is published as joakimp/opencode-devbox:base-<sha>. Dockerfile.variant FROMs that base and adds only opencode/omos/pi installs (or skips them per build-args). Companion workflow .gitea/workflows/docker-publish-split.yml runs as a workflow_dispatch-only pipeline alongside the existing docker-publish.yml so they don't conflict. Hash-driven base reuse: a content hash of Dockerfile.base + rootfs/ + entrypoint*.sh becomes the base tag; if the tag already exists on Docker Hub, the base build is skipped entirely. Estimated wall clock: version-bump-only release ~3040 min (vs ~165180 min today); base-touching release ~6070 min. Trade-off: two Dockerfiles to maintain, and npm install -g in the variant must override NPM_CONFIG_PREFIX=/usr per-RUN to keep baked binaries off the volume-shadowed path. Once 12 successful workflow_dispatch runs validate the output against the existing pipeline, the new workflow takes over on: push: tags: v* and the original is retired.

v1.14.42 — 2026-05-09

opencode 1.14.41 → 1.14.42 bump. Carries along all container-side changes accumulated since v1.14.41b: pi package rename to @earendil-works/*, npm-prefix-on-volume fix, Hub doc rewrite, README/AGENTS docs catchup.

Image changes:

  • Bump: opencode 1.14.41 → 1.14.42 (OPENCODE_VERSION build-arg default in both Dockerfile and Dockerfile.variant).
  • Rename: npm install -g @mariozechner/pi-coding-agent -> npm install -g @earendil-works/pi-coding-agent in the INSTALL_PI=true build path. Pi moved to its new home at earendil-works on 2026-05-07 (https://pi.dev/news/2026/5/7/pi-has-a-new-home); the old @mariozechner/* packages are deprecated on npm with the explicit message 'please use @earendil-works/pi-coding-agent instead going forward', and the version stream has moved on (old top-out 0.73.1; new currently 0.74.0). Anyone npm-installing the old name today gets a deprecation warning + a stale binary. Affects both Dockerfile (production single-Dockerfile path) and Dockerfile.variant (split-base path on main). README, AGENTS, and HUB_TEMPLATE URL refs updated from github.com/mariozechner/pi-coding-agent (which now 404s) to github.com/earendil-works/pi. Brew install references (brew install pi-coding-agent) left as-is: formula still works at 0.73.1 and a homebrew tap update is tracked upstream at earendil-works/pi#2755.
  • Fix: pi install npm:<pkg> (and any npm install -g) by the developer user no longer EACCES against the system npm prefix. NPM_CONFIG_PREFIX is now /home/developer/.pi/npm-global and the prefix's bin/ is prepended to PATH. The directory lives on the devbox-pi-config named volume, so user-installed pi packages (themes, skills, extensions) survive container recreation and image rebuilds. Build-time npm install -g calls (opencode, pi, oh-my-opencode-slim) are unaffected because the new ENVs are declared after those steps in the Dockerfile, so the baked binaries still install to /usr and are not shadowed by the volume mount.
  • Fix (smoke-test): scripts/smoke-test.sh oh-my-opencode-slim check now invokes npm ls -g with NPM_CONFIG_PREFIX=/usr so it queries the system prefix where the baked install lives. Latent regression from the npm-prefix fix above: default npm ls -g started querying the user prefix (/home/developer/.pi/npm-global, empty at build time) and missed the baked OMOS install — surfaced when validate.yml ran on main after the merge of feat/split-build.

Docs:

  • Docs: DOCKER_HUB.md Image Variants table now lists all four published variants (latest, latest-omos, latest-with-pi, latest-omos-with-pi) instead of only the first two. Generator (scripts/generate-dockerhub-md.py) HEADER updated to match.
  • Docs: DOCKER_HUB.md is now generated from a hand-maintained HUB_TEMPLATE constant in scripts/generate-dockerhub-md.py instead of a section-by-section transformation of README.md. Drops from 24 997 bytes (3 byte headroom) to ~5.5 kB (~78% headroom). The old derive-from-README mechanism (SECTION_RULES, TRIM_SUBSECTIONS, REPLACEMENTS, split_sections, trim_subsections) is gone — README and Hub doc are now independent surfaces, and most README edits no longer require regenerating DOCKER_HUB.md. Trade-off: image-variants table and quick-start flow are now coupled to HUB_TEMPLATE and need a manual edit when they change.
  • Docs: README pi section gains a ### Setup paragraph mentioning the prebuilt latest-with-pi and latest-omos-with-pi Docker Hub tags, mirroring the OMOS section's latest-omos mention. "What gets installed" updated to reflect the actual shipped state: 7 pi-extensions (was stale at 6 — mcp-loader was added in pi-extensions but not propagated here), each with a one-line description; mcp-loader gets a paragraph covering its dual-transport (local stdio + remote streamable-HTTP per MCP spec 2025-03-26) capability and the /mcp slash command. Clarified that the mempalace bridge is a separate MCP entry point that coexists with mcp-loader rather than being replaced by it.
  • Docs: AGENTS.md tag-scheme paragraph corrected from "four Docker Hub tags per release" to eight (the v1.14.41b CI matrix expansion). "Documentation coupling on release" rule updated — README edits no longer require regenerating DOCKER_HUB.md. Release-day checklist tightened.
  • README pi section: "What gets installed" sub-section updated to reflect the actual shipped state. Was stale: claimed 6 pi-extensions (actually 7 — mcp-loader was added in pi-extensions commit 141bf64 / 7eec49b / 37cc49e but never propagated here). Each extension now has a one-line description; mcp-loader gets a paragraph covering its dual-transport (local stdio + remote streamable-HTTP per MCP spec 2025-03-26) capability and the /mcp slash command. Clarified that the mempalace bridge is a separate MCP entry point that coexists with mcp-loader rather than being replaced by it. Added an explicit note that no MCP servers are baked in beyond mempalace — the loader is opt-in via settings.json edits.

v1.14.41b — 2026-05-08

Optional pi as second harness.

  • Feature: New INSTALL_PI=true build arg installs pi as an alternative or complementary harness alongside opencode. Both harnesses share the same mempalace install and palace path — wing/diary entries are mutually visible. Adds ~150 MB to the image. Pi version pinned by PI_VERSION (default: latest at build time); pi update inside the container does not persist across --rm containers — image rebuild is the upgrade path, same contract as OPENCODE_VERSION.
  • Feature: New INSTALL_OPENCODE=false build arg builds an image without opencode (e.g. for pi-only use). Default remains true. Existing builds and tags are unaffected.
  • Feature: New devbox-pi-config named volume mounted at ~/.pi/ persists pi user state (settings.json, /ext-disabled extensions) across container recreate. Mirrors the devbox-opencode-config pattern from v1.14.33.
  • Feature: Container clones pi-toolkit (keybindings, env loader, settings template) and pi-extensions (6 extensions including ext-toggle, todo, ssh-controlmaster, notify, git-checkpoint, confirm-destructive) into /opt/ at build time. New PI_TOOLKIT_REF and PI_EXTENSIONS_REF build args (default main) pin git refs. The mempalace pi-bridge mempalace.ts is symlinked from the existing /opt/mempalace-toolkit/ clone.
  • Behavior change: Default container CMD changed from ["opencode"] to ["bash", "-l"]. docker compose run --rm devbox (no command) now drops to a login shell so users can pick opencode or pi (or run aws sso login first). To preserve the old behavior, pass the harness explicitly: docker compose run --rm devbox opencode. docker compose exec workflows are unaffected (they bypass the entrypoint and CMD).
  • Performance: chromadb's all-MiniLM-L6-v2 ONNX embedding model (~80 MB) is now pre-warmed at image build time under ~/.cache/chroma/onnx_models/. Without this, mempalace's init step in entrypoint-user.sh would download the model silently on first container start (suppressed via >/dev/null 2>&1), stalling startup by minutes on a fresh image. Pre-warming runs as gosu developer so the cache lands at the right path and is owned by the runtime user.
  • Bugfix: entrypoint-user.sh now redirects stdin from /dev/null for the mempalace init --yes call. Without this, the interactive Mine this directory now? [Y/n] prompt at the end of init would silently block forever when the container was started with docker run -it (TTY keeps stdin open). EOF on stdin makes the prompt fall through to its default.
  • Smoke-test: New --variant with-pi (threshold 2700 MB) and --variant omos-with-pi (3400 MB). Pi-specific assertions verify pi binary, pi-toolkit clone, pi-extensions clone, deployed keybindings symlink, extension count ≥ 4, mempalace bridge symlink, and settings.json bootstrap. Pi state assertions use docker exec from the host (not run-inside-container) since the container has no docker CLI.
  • CI: .gitea/workflows/{validate,docker-publish}.yml extended with with-pi and omos-with-pi matrix entries. Each release now produces eight Docker Hub tags: vX.Y.Z[n], latest, vX.Y.Z[n]-omos, latest-omos, vX.Y.Z[n]-with-pi, latest-with-pi, vX.Y.Z[n]-omos-with-pi, latest-omos-with-pi.
  • Docs: README adds a "pi (alternative/complementary harness)" section. AGENTS.md codifies pi install contract, deploy ordering in entrypoint-user.sh, and rationale for not calling mempalace-toolkit's full install.sh from container.

v1.14.41 — 2026-05-08

Bump opencode to 1.14.41.

  • v1.14.41 (upstream): restored formatter output handling for stdout/stderr writes; warping a session to another workspace can now carry over uncommitted file changes; restored custom provider setup in /connect; macOS Settings menu entry added; desktop local server split into a separate utility process; ACP clients restore last model/mode/effort when loading sessions and can close sessions cleanly.

No container-level changes in this release. Dockerfile bump only.

v1.14.40 — 2026-05-07

Bump opencode to 1.14.40.

Rolls up upstream releases v1.14.34 → v1.14.40 (no v1.14.36). Highlights:

  • v1.14.40: support .well-known/opencode configs that point to a separate remote config file; assistant text preserved in signed reasoning blocks; CORS, network options, web terminal, and Cloudflare AI Gateway provider fixes; Mistral Medium 3.5 variants restored.
  • v1.14.39: desktop app respects HTTP_PROXY and friends; storage reads return null instead of failing when keys are missing.
  • v1.14.38: embedded UI requests work with arbitrary connect-src origins under the default CSP; desktop trusts system CA certificates for HTTPS.
  • v1.14.37: cancelling a task now cancels child subtask sessions; v2 session rendering improvements (cleaner tool states, better compaction summaries); new "warp a session into another workspace or back to local project" feature; Windows titlebar stable across zoom changes.
  • v1.14.35: preserve diff patch boundaries so session diffs render correctly when file contents themselves contain diff --git text.
  • v1.14.34: PTY connection tickets for authenticated terminal websockets; v2 session failure events for clients to detect failed runs; improved shell command handling for Bash/PowerShell/cmd; new debug info command; --username option for basic-auth server connections.

No container-level changes in this release. Dockerfile bump only.

v1.14.33 — 2026-05-03

Bump opencode to 1.14.33. Named volume for opencode config, skillset auto-deploy, Context7 MCP.

Rolls up the image-structure changes originally planned for v1.14.32b onto the current opencode release. v1.14.32 was built but never deployed (wrong deploy dir caught the tag mid-flight); skipped in favor of landing everything together on 1.14.33.

  • Breaking: ~/.config/opencode/ now uses a named volume (devbox-opencode-config) instead of a host bind mount. The container's config, skills, and instructions are independent from the host. Users who relied on the bind mount should either re-add it explicitly in their compose file (overriding the volume) or migrate hand-edits into the container.
  • Breaking: ~/.agents/skills/ is no longer bind-mounted from the host. The container manages its own skills directory — the entrypoint deploys skills from the skillset repo on each start.
  • Feature: Skillset auto-deploy on container start. The entrypoint runs deploy-skills.sh --bootstrap --prune-stale from the first skillset repo found at: $SKILLSET_CONTAINER_PATH~/skillset/workspace/skillset. Creates relative symlinks that resolve inside the container regardless of host path layout. Idempotent.
  • Feature: Context7 remote MCP server registered in auto-generated config. No local binary; provides up-to-date library documentation to LLMs. Config file is now opencode.jsonc (supports comments) with a note about the optional API key for higher rate limits. Existing-config check detects both .json and .jsonc.
  • Env: New SKILLSET_CONTAINER_PATH env var for specifying skillset repo location inside the container when it's not at /workspace/skillset.
  • Docs: README updated for named volume config, skillset auto-deploy, Context7 MCP server, opencode.jsonc references. AGENTS.md, DOCKER_HUB.md regenerated.

Upstream opencode 1.14.32 notes (shipped in this build since v1.14.32 was skipped): shell-mode input in the prompt is editable again (backspace, cursor keys); HTTP API workspace adapters no longer lose instance context, restoring workspace create/sync/routing; experimental workspace creation requests that omit extra are fixed; OpenAPI parameter schemas now match the public API so generated clients stop drifting; unsupported image formats fall back to text reads instead of being sent as image attachments; agents can use the global temp directory without extra permission prompts; Bedrock sessions that include reasoning content no longer break when switching models; session archive timestamps reject non-finite values to avoid invalid JSON. TUI: reduced startup theme flashing under the system theme, animated logo avoids subpixel rendering on terminals without truecolor support.

Upstream opencode 1.14.33 release notes: see https://github.com/sst/opencode/releases/tag/v1.14.33.

v1.14.31d — 2026-05-01

CI: collapse per-arch matrix back into single multi-arch push jobs.

  • Fix: v1.14.31c's per-arch matrix build jobs failed on Upload digest with GHESNotSupportedError: @actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES. Gitea Actions only implements the v3-compatible artifact API; @v4 uses a GitHub-Enterprise-specific backend. Separately, build-omos linux/arm64 hung silently for 12 minutes in "Set-up job" and then failed with no log output — likely catthehacker image-pull contention between concurrent matrix children on the same runner host.
    • Rather than downgrade to actions/{upload,download}-artifact@v3, collapsed the per-arch matrix entirely. docker/build-push-action@v7 with platforms: linux/amd64,linux/arm64 publishes a proper multi-arch manifest in a single job, so the whole artifact-passing and imagetools create merge dance existed only to support a matrix split we no longer need.
    • The original matrix split was designed around load: true disk exhaustion (v1.14.30b). With push-by-digest/push: true streaming straight to the registry — no local unpack — the peak disk story is fundamentally different. Validated in v1.14.31b that the reclaim step gives sufficient headroom for a single-job amd64 build; oracle-reviewed call that this should extend to the combined amd64+arm64 push case.
    • Workflow goes from 7 jobs to 5 (smoke-base, smoke-omos, build-base, build-omos, update-description). 263 → ~110 lines of YAML in docker-publish.yml.
  • Add: timeout-minutes: 90 on both build jobs so a hung arm64 build produces an explicit failure with logs rather than runner-default silent truncation.
  • Add: BUILDKIT_PROGRESS=plain at workflow level so arm64-under-QEMU build output is line-by-line (the default collapsed progress UI was obscuring earlier stalls).
  • Add: AGENTS.md §CI quirks documents the Gitea-specific traps encountered this week: upload-artifact@v3-only on Gitea, /bin/sh is dash, build-push-action@v7 does multi-arch natively with comma-separated platforms, reclaim step is mandatory on load: true jobs.
  • No image changes. Rebuild of v1.14.31 content only.

v1.14.31c — 2026-05-01

CI: fix bash-specific parameter expansion and bump omos size threshold.

  • Fix: Derive platform slug step in the per-arch matrix build jobs (build-base, build-omos) used ${PLATFORM_PAIR//\//-} which is a bash parameter-expansion. The runner container executes step scripts via /bin/sh (dash), which errored with Bad substitution. Rewrote using tr / - which is POSIX and behaves identically. Both build-base and build-omos matrix jobs were blocked on this on v1.14.31b.
  • Fix: smoke-test image-size threshold for the omos variant bumped from 3000 MB to 3200 MB. The mempalace-toolkit bake-in added ~100 MB to omos; measured 3107 MB on v1.14.31b. All functional smoke checks (opencode, node, mempalace CLIs, toolkit wrappers, oh-my-opencode-slim) pass — this is a guardrail recalibration, not a performance concession. The underlying image genuinely grew.
  • The runner-disk reclaim step from v1.14.31b did its job: smoke-base and validate-base now pass cleanly. Only smoke-omos was blocked this iteration, and only on the threshold.
  • No image changes beyond what shipped in v1.14.31. Rebuild of v1.14.31 content only.

v1.14.31b — 2026-05-01

CI: reclaim runner disk before load: true smoke builds.

  • Fix: v1.14.31's publish workflow and the validate workflow both hit No space left on device on the single-arch amd64 smoke/validate builds (/opt/uv-tools/mempalace/lib/python3.13/site-packages/hf_xet/hf_xet.abi3.so, /usr/local/bin/git-lfs). Root cause is not the build itself but the load: true step: peak disk during export equals tarball + unpacked image + buildx cache, and the image has crossed the ~3 GB threshold where this no longer fits in the ~12 GB of free space the runner container starts with. The v1.14.30c refactor split multi-arch into per-arch push-by-digest jobs (which don't load), but the smoke gates still do and still hit the wall.
    • Added a Reclaim runner disk step to all four load: true jobs (validate-base, validate-omos, smoke-base, smoke-omos). The step strips catthehacker/ubuntu:act-latest-resident toolchains we never use (hosted-tool-cache, dotnet, android, powershell, swift, ghc, jvm, microsoft, chromium, boost) and runs docker system prune -af --volumes + docker builder prune -af against the runner's dockerd before setup-buildx-action. Expected reclaim is 612 GB depending on what's resident.
    • Added workflow-level concurrency: { group: ..., cancel-in-progress: false } on docker-publish.yml so concurrent tag pushes can't race docker system prune in one job against an in-flight buildx cache in another.
    • Pruning is deliberately kept out of the per-arch matrix push-by-digest jobs (build-base/build-omos) — those don't need it (no load: true), and pruning in parallel jobs risks one job nuking another's cache.
  • Follow-up (not in this release): image-size reduction via a dedicated uv tool install mempalace build stage (strips uv's cache from the final image), pinning mempalace-toolkit to a commit SHA with --depth=1 --filter=blob:none, and auditing whether hf_xet is actually required by mempalace at runtime. These will ship in the next release that rebases on a new opencode version.
  • No image changes. Rebuild of v1.14.31 content only.

v1.14.31 — 2026-05-01

Bump opencode to 1.14.31.

CI infrastructure: split multi-arch publish across separate runners.

  • Fix: The publish workflow exhausted runner disk space on v1.14.30b and would have hit the same wall on any subsequent release. Both variants built both architectures on a single catthehacker/ubuntu:act-latest container with ~40 GB of shared overlay space, and the peak disk footprint during the nodejs dpkg unpack / git-lfs layer export pushed it over the edge (No space left on device). The mempalace-toolkit bake-in from v1.14.30b added the final straw; the underlying issue is that QEMU-emulated arm64 layers were stored alongside the amd64 build on the same runner.
    • docker-publish.yml refactored to the canonical push-by-digest + manifest-merge pattern: smoke test (amd64) runs on its own runner, each (variant × arch) push target runs on its own fresh runner with outputs: type=image,...,push-by-digest=true,push=true (no local image store), then a tiny merge job assembles the multi-arch manifest with docker buildx imagetools create from digest artifacts.
    • Per-runner disk peak is now roughly one-quarter of the old single-job peak. The four Docker Hub tags produced per release (vX.Y.Z[n], latest, vX.Y.Z[n]-omos, latest-omos) are unchanged.
    • Also parallelizes the amd64 and arm64 builds, so wall-clock time for a release should drop noticeably despite the added merge hop.

v1.14.30b — 2026-04-30

Bake mempalace-toolkit wrappers into the image.

  • Fix: The scheduler templates in mempalace-toolkit's contrib/ assume mempalace-session is available inside the container, but the image never actually installed it. Users following the *-devbox scheduler docs would silently lose the wrappers on every docker compose up --force-recreate, because the only way to get them was a post-hoc ./install.sh --yes inside the container — which lives in the ephemeral layer. The host-side systemd timer would then fire, docker exec in, and hit mempalace-session: command not found. Caught during runtime validation on 2026-04-30.
    • New Dockerfile block clones mempalace-toolkit at build time (depth-1) to /opt/mempalace-toolkit/, symlinks bin/mempalace-session and bin/mempalace-docs into /usr/local/bin/, and asserts both respond to --help before the layer succeeds.
    • Gated by ARG INSTALL_MEMPALACE_TOOLKIT=true (defaults on, depends on INSTALL_MEMPALACE=true).
    • Floated ref via ARG MEMPALACE_TOOLKIT_REF=main — override for reproducible builds once the toolkit starts tagging releases.
  • Tests: Smoke test gains three toolkit assertions (mempalace-session --help, mempalace-docs --help, symlink target check). The resolved-versions preamble now logs the toolkit git short-SHA alongside the other floated components.
  • Docs: README's MemPalace section gains a Scheduled mining (mempalace-toolkit) subsection covering the new wrappers and pointing at contrib/ for scheduling. New build-args table entry for INSTALL_MEMPALACE_TOOLKIT.

v1.14.30 — 2026-04-30

Bump opencode to 1.14.30.

v1.14.29c — 2026-04-29

Drop redundant mempalace-mcp-server wrapper, use the entry point mempalace ships.

  • Fix: MCP integration with mempalace was still broken for users with custom opencode.json files because they typically had ["python3", "-m", "mempalace.mcp_server"] from v1.14.28b and earlier. With the uv-tool install path, system python3 can't import mempalace and the MCP server subprocess exits immediately — opencode surfaced this as MCP error -32000: connection closed. Users should migrate to ["mempalace-mcp"]. The auto-generated config in new containers already emits the new form.
  • Cleanup: Remove the hand-rolled /usr/local/bin/mempalace-mcp-server wrapper. The mempalace Python package ships a mempalace-mcp console entry point; uv tool install places it on PATH as a shim whose shebang points at the isolated venv's Python. The wrapper was duplicating what uv installs for free. Removed rootfs/usr/local/bin/ and its COPY + chmod lines from the Dockerfile.
  • Docs: README's MemPalace section now shows ["mempalace-mcp"] and explicitly warns against ["python3", "-m", "mempalace.mcp_server"] with the observed failure mode.
  • Tests: Smoke test asserts /usr/local/bin/mempalace-mcp is executable and prints its symlink target, replacing the previous wrapper-present check.

v1.14.29b — 2026-04-29

Fix OMOS bunx detection + CI build reliability.

  • Fix: entrypoint-user.sh checked command -v bunx to gate the OMOS auto-install, but the OMOS image only ships the bun binary — upstream's bun installer never creates a bunx symlink and neither did our Dockerfile. The check always failed on a fresh OMOS image, so bun x oh-my-opencode-slim@latest install never ran and first-start OMOS setup would have printed ENABLE_OMOS=true but bun is not installed. even though bun was right there. Latent until now because the only exercised path had a persisted oh-my-opencode-slim.json from a prior install.
    • Changed the gate to command -v bun.
    • Changed both install invocations from bunx oh-my-opencode-slim@latest install ... to bun x oh-my-opencode-slim@latest install ....
    • Added ln -sf bun /usr/local/bin/bunx to the Dockerfile's OMOS block so interactive users can still type bunx by habit, and verified the symlink at build time (test -L /usr/local/bin/bunx).
    • Smoke test now asserts the bunx symlink is present on the OMOS variant.
  • Fix: CI build robustness against transient GitHub/Gitea CDN failures. The first attempt at building v1.14.29b tripped on a single HTTP 502 from GitHub's release CDN mid-download (zoxide-0.9.9-x86_64-unknown-linux-musl.tar.gz), failing the entire OMOS build with no retry. Fix applied to every tool-download curl in the Dockerfile:
    • curl --retry 5 --retry-delay 5 --retry-all-errors on both the -fsSL GET requests and the -sI HEAD requests used for /releases/latest redirect resolution. 5 attempts with 5 s back-off eats most transient CDN hiccups without failing the build.
    • Added [ -n "$V" ] assertion after each version-resolution step. If the HEAD redirect ever fails to produce a tag name, the build fails fast with an empty-version message rather than trying to download .../v//... and producing a confusing 404.
    • Same hardening applied to the optional Go install block (go.dev JSON feed + tarball download) and the nodesource apt-repo setup script.
  • Security: Added apt-get upgrade -y to the core-packages RUN step. Picks up any security/CVE fixes published between debian:trixie-slim base-image rebuilds. Paired with the existing update and install in the same layer so image history isn't bloated. Today this produced 0 upgraded (base image is current), but it future-proofs against the next CVE drop.

v1.14.29 — 2026-04-28

Opencode 1.14.29 + infrastructure and maintainability pass.

  • Bump opencode to 1.14.29.
  • Cleanup: Remove dead INSTALL_PYTHON build arg. Python 3 + pip + venv have been unconditionally installed in the base layer since mempalace was added; the flag was a no-op. Users should use uv (pre-installed) or uvx for Python tooling.
  • Fix: mempalace init in entrypoint-user.sh now uses --yes for non-interactive operation. Previously the command prompted the user (Your choice [enter/edit/add]:) on first container start, which either hung or printed prompts into the user's terminal. The init is still gated by [ ! -d "$PALACE_DIR/palace" ] so existing palace data from prior versions is preserved untouched on upgrade.
  • Feature: MemPalace is now installed via uv tool install into an isolated venv at /opt/uv-tools/mempalace/, reached through a new /usr/local/bin/mempalace-mcp-server wrapper. Replaces the previous pip install --break-system-packages approach — removes the PEP 668 workaround and keeps mempalace deps out of system Python site-packages. The wrapper is what generate-config.py now references in the auto-generated opencode.json. Users with custom opencode.json files should update their mempalace MCP command from ["python3", "-m", "mempalace.mcp_server"] to ["mempalace-mcp-server"].
  • Feature: New INSTALL_MEMPALACE build arg (default true). Rebuild with --build-arg INSTALL_MEMPALACE=false to shave ~300 MB off the image when local AI memory isn't needed.
  • Refactor: opencode.json generation extracted from entrypoint-user.sh into a standalone Python script at /usr/local/lib/opencode-devbox/generate-config.py. Easier to read, test, and extend with new providers. Default models are declared at the top of the script rather than hard-coded in bash heredocs. Reduces entrypoint-user.sh from 176 to 97 lines. Behavior is unchanged — the script preserves the critical guarantee of never overwriting an existing opencode.json.
  • Perf: Container startup avoids the recursive chown -R on named volumes that already have correct ownership. A .devbox-owner sentinel file written after a successful chown lets subsequent starts short-circuit via a single cat. On volumes with thousands of files (nvim plugins, palace data) this cuts multi-second startup costs to milliseconds. If USER_UID changes between runs, the sentinel mismatches and the full chown still runs.
  • CI: New validate workflow runs on every push to main and PR — single-arch amd64 build, smoke test, and DOCKER_HUB.md sync check. Catches broken Dockerfile changes without waiting for a tag push.
  • CI: docker-publish.yml now smoke-tests each variant on amd64 before the full multi-arch push. A failing smoke test blocks the release.
  • CI: Image size is tracked and fails the build if it exceeds thresholds (base: 2500 MB uncompressed, OMOS: 3000 MB). Makes bloat visible rather than silent.
  • Docs: DOCKER_HUB.md is now auto-generated from README.md via scripts/generate-dockerhub-md.py. Editing it directly is a mistake — the --check step in CI fails if the committed file is out of sync. Section inclusion is controlled by explicit rules (SECTION_RULES, TRIM_SUBSECTIONS); adding a new section to README forces an explicit keep/drop/replace decision. Keeps the 25 kB Docker Hub limit in sight and eliminates manual sync burden.
  • Tests: New scripts/smoke-test.sh asserts: (a) all core binaries are runnable and print a version, (b) opencode starts, (c) entrypoint correctly drops to the developer user, (d) generate-config.py produces valid JSON with the expected shape, (e) generate-config.py never overwrites an existing config, (f) bun is present only in the OMOS variant, (g) image size is under threshold. The smoke test also logs resolved versions of every component as its first step so CI output always records what got baked in.
  • Versioning: All GitHub/Gitea-hosted binaries (gosu, fzf, git-lfs, neovim, bat, eza, zoxide, uv, gitea-mcp) and the go.dev-hosted Go toolchain now default to latest at build time. Each *_VERSION ARG resolves the newest upstream release by reading the /releases/latest Location redirect (or the go.dev JSON feed). Previously these were hand-pinned to a specific version, which meant rebuilds didn't pick up upstream CVE fixes until someone remembered to bump the pin. Pinning is still supported — pass --build-arg NVIM_VERSION=0.12.1 etc. to lock a specific version. Intentionally still pinned: OPENCODE_VERSION (drives the image tag), NODE_VERSION=22 (major only), DEBIAN_VERSION=trixie-slim (OS base).

v1.14.28b — 2026-04-27

  • Feature: Add MemPalace local-first AI memory system to base image. Provides 29 MCP tools for semantic search over conversation history, knowledge graph queries, and agent diaries. Palace data persists via optional devbox-palace named volume, ChromaDB embedding model cache via devbox-chroma-cache. No API keys required.
  • Feature: Auto-register mempalace MCP server in generated opencode.json (when mempalace is installed and config is auto-generated from OPENCODE_PROVIDER).
  • Feature: Add official Gitea MCP server (gitea-mcp) to base image. Provides 50+ MCP tools for Gitea API (repos, issues, PRs, releases, Actions). Disabled by default — requires GITEA_ACCESS_TOKEN and GITEA_HOST env vars.

v1.14.28 — 2026-04-26

Bump opencode to 1.14.28.

v1.14.25 — 2026-04-25

Bump opencode to 1.14.25. Also includes container-level changes since v1.14.22b:

  • Add python3-pip and python3-venv to base image (fixes Mason LSP installs).
  • Add devbox-nvim-data named volume for neovim plugin/Mason persistence.
  • Add devbox-zoxide named volume for zoxide directory history persistence.
  • Bake devbox-shell bridge line into /etc/skel-devbox/.bash_aliases.
  • Add CHANGELOG.md with full release history.

v1.14.22b — 2026-04-23

Fix Mason LSP installs, persist nvim data, devbox-shell bridge.

  • Fix: Add python3-pip and python3-venv to base image. Mason creates a Python venv per LSP package and pip-installs into it; Debian trixie ships python3 without ensurepip, so venv creation failed and every Mason Python package (ruff, ansible-lint) errored on every nvim start.
  • Feature: Add devbox-nvim-data named volume at ~/.local/share/nvim — Lazy plugin cache and Mason LSP installs now persist across --force-recreate.
  • Feature: Add devbox-zoxide named volume at ~/.local/share/zoxide — zoxide directory history persists across recreates.
  • Feature: Bake the devbox-shell bridge line into /etc/skel-devbox/.bash_aliases — hosts using the ~/.config/devbox-shell/ directory-mount pattern get automatic sourcing without manual setup after recreate.

v1.14.22 — 2026-04-23

Bump opencode to 1.14.22.

v1.14.21 — 2026-04-23

Opencode 1.14.21 + zoxide persistence + multi-user fixes.

  • Bump opencode to 1.14.21.
  • Fix single-file bind-mount caveat: document the kernel-level inode issue (affects all platforms, not just Docker Desktop).
  • Pin project name in default docker-compose.yml — directory renames no longer orphan named volumes.
  • Fix volume collision in shared-machine compose: scope project name by SIGNUM.
  • Auto-detect OS username ($USER) for volume isolation in own-account mode.
  • Document the upgrade ritual for reconciling VM compose files.
  • Add multi-user setup pointer in DOCKER_HUB.md.

v1.14.20b — 2026-04-21

Fix [devbox] prompt marker lost on exec bash.

  • The PS1 prefix guard used an exported env var that survived exec bash, but PS1 itself doesn't — so the new shell skipped adding the prefix. Replaced with a substring check on PS1 itself.
  • Clarify tag-letter convention in AGENTS.md: suffix is the build ordinal, a is never used.

v1.14.20 — 2026-04-21

Opencode 1.14.20 + PROMPT_COMMAND/zoxide fix.

  • Bump opencode to 1.14.20.
  • Fix PROMPT_COMMAND collision with zoxide: history -a; followed by zoxide's ;__zoxide_hook produced ;; which bash rejected on every prompt. Moved history-flush after zoxide init, using newline separator.
  • Includes all v1.14.19c shell-defaults work (baked .bash_aliases/.inputrc via /etc/skel-devbox/, skel-copy on first run, devbox-shell-history named volume).

v1.14.19d — 2026-04-21

Superseded by v1.14.20 before building. Tagged but never built.

v1.14.19c — 2026-04-21

Bash history persistence, shell defaults, GID auto-detect.

  • Feature: Bash history persists across --force-recreate via devbox-shell-history named volume at ~/.cache/bash.
  • Feature: Quality-of-life shell defaults shipped in /etc/skel-devbox/ and copied to ~/ only if absent: prefix history search on Up/Down, 100k-entry timestamped dedup history, coloured case-insensitive tab completion, eza/bat aliases, zoxide/fzf integrations, [devbox] prompt marker.
  • Feature: Skel-copy pattern — host bind-mounts and in-container customizations are never overwritten on upgrade.
  • Fix: Entrypoint now detects workspace UID and GID independently. Hosts with UID 1000 but non-1000 GID (e.g. Debian's useradd default GID 1001) get correct group remapping.
  • Docs: SSH banner-timeout troubleshooting (CGNAT), shell defaults section, skel restore/diff commands.

v1.14.19b — 2026-04-20

Ownership fixes and config/docs refresh.

  • Fix: Root-owned parent dirs left behind by nested named-volume mounts. Entrypoint now chowns .local, .local/share, .local/state, .config before leaf mount points.
  • Fix: deploy/sync-to-vm.sh no longer preserves host GIDs (rsync -a-rlptDz).
  • Default model IDs refreshed (claude-sonnet-4-6, gpt-5.4, global Bedrock inference profile).
  • Documentation gates oh-my-opencode-slim references to the OMOS variant.

v1.14.19 — 2026-04-20

Bump opencode to 1.14.19.

v1.14.18 — 2026-04-19

Fix Bun download URL: remove non-existent LATEST file fetch.

v1.4.17 — 2026-04-19

Bump opencode to v1.4.17, add file utility to base image.

v1.4.12 — 2026-04-18

Bump opencode to v1.4.12.

v1.4.11 — 2026-04-18

Bump opencode to v1.4.11.

v1.4.7 — 2026-04-17

Bump opencode to v1.4.7.

v1.4.6 — 2026-04-15

Bump opencode to v1.4.6.

v1.4.3k — 2026-04-13

Fix Bedrock config: add AWS_PROFILE to generated config, add .agents/skills to volume ownership fix.

v1.4.3j — 2026-04-13

Upgrade base image from Debian bookworm to trixie (current stable). Bookworm EOL June 2026; trixie supported until 2028/LTS 2030.

v1.4.3i — 2026-04-12

Add rustup for on-demand Rust support, document JS/TS development.

v1.4.3h — 2026-04-12

Add uv package manager to base image for on-demand Python support.

v1.4.3g — 2026-04-12

Fix IPv6 connectivity failures: force IPv4 preference in CI builds.

v1.4.3f — 2026-04-11

Add error handling to Docker Hub description update step.

v1.4.3e — 2026-04-10

Fix CVEs: install git-lfs from GitHub (Go 1.25), document Go versions for gosu/fzf.

v1.4.3d — 2026-04-10

Fix CVEs: install gosu 1.19 and fzf 0.71.0 from GitHub releases instead of Debian packages.

v1.4.3c — 2026-04-10

Fix CVEs: install gosu from GitHub release instead of Debian package (Go 1.19.8 → current).

v1.4.3b — 2026-04-10

Fix entrypoint crash on read-only SSH mount.

v1.4.3 — 2026-04-10

Bump opencode to 1.4.3.

v1.4.2 — 2026-04-10

Initial release. Fix CI: use vars for username, secrets for token.