f1e879ca6c
The bind-mounted ~/.ssh/config is read before the baked Host * default and SSH uses the first ControlPath it sees. A per-host block pointing ControlPath under ~/.ssh/ (CGNAT-multiplexing pattern) wins but fails in-container because ~/.ssh is read-only, silently breaking pi --ssh <host> (falls back to local tools). Documented the host-side fix: drop the override or repoint at the writable /tmp/sshcm/. README + CHANGELOG only, no image change.
863 lines
91 KiB
Markdown
863 lines
91 KiB
Markdown
# 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](AGENTS.md#versioning-scheme) for details.
|
||
|
||
---
|
||
|
||
## Unreleased
|
||
|
||
### Docs: per-host `ControlPath` overrides break `pi --ssh` (read-only `~/.ssh`)
|
||
|
||
Documented a gotcha in the README "Reaching your LAN" section: the bind-mounted
|
||
`~/.ssh/config` is read before the baked `Host *` default, and SSH uses the
|
||
first `ControlPath` it sees. A per-host block that sets `ControlPath` under
|
||
`~/.ssh/` (a common CGNAT-multiplexing pattern, e.g. `~/.ssh/cm/%r@%h:%p`) wins
|
||
but then fails inside the container because `~/.ssh` is mounted read-only — the
|
||
master socket can't bind. This silently breaks `pi --ssh <host>`: the SSH layer
|
||
fails and pi falls back to running its tools locally in the container. Fix is
|
||
host-side — drop the per-host `ControlPath` or repoint it at the writable
|
||
`/tmp/sshcm/%r@%h:%p` (works on both host and container, preserves multiplexing).
|
||
No image change; documentation only.
|
||
|
||
### Fixed: validate.yml false-negative on fork/recall registration checks
|
||
|
||
The push-to-main `validate.yml` builds variants FROM the published `base-latest`
|
||
image, which lags the entrypoint in the current commit until a release tag
|
||
rebuilds the base. The fork/recall *registration* smoke checks depend on the
|
||
base entrypoint running `pi install /opt/<pkg>`, so a stale `base-latest` reded
|
||
those runs with a false negative even when the variant layer was correct.
|
||
`smoke-test.sh` now gates the two registration assertions behind
|
||
`STRICT_REGISTRATION` (warn-only when unset). `validate.yml` leaves it unset;
|
||
the release pipeline (`docker-publish-split.yml`), which builds the base fresh
|
||
in the same run, sets `STRICT_REGISTRATION=1` on the pi-bearing smoke jobs to
|
||
enforce them. The build-time `/opt` + `node_modules` checks stay hard in both
|
||
paths.
|
||
|
||
### Added: persist the LAN-jump key + one-line authorize hint (authorize once per machine)
|
||
|
||
The jump keypair (`~/.ssh-local/devbox_jump_ed25519`) was stored on the
|
||
container's ephemeral overlay, so `docker compose up --force-recreate` (every
|
||
image update) regenerated it — forcing you to re-authorize the new key on the
|
||
host each time. The compose files now persist `~/.ssh-local` via a named volume
|
||
(`devbox-ssh-local`), matching the pattern already used for `.pi`, shell
|
||
history, etc. The key is generated **once** and reused across updates, so you
|
||
authorize it on the host **once per machine**.
|
||
|
||
`setup-lan-access.sh` now also prints a ready-to-paste authorize line whenever
|
||
it generates a **new** key (not just when `HOST_SSH_USER` is unset), e.g.
|
||
`echo 'ssh-ed25519 …' >> ~/.ssh/authorized_keys` — no helper file to locate, no
|
||
workspace path to guess. It stays silent once the key is persisted.
|
||
|
||
### Fixed: chown the `devbox-ssh-local` volume so the jump key can be generated
|
||
|
||
The previous change persisted `~/.ssh-local` via a named volume, but the
|
||
entrypoint's volume-ownership loop was never updated to include it. Docker
|
||
creates named volumes as `root:root`, so on a fresh volume `~/.ssh-local`
|
||
stayed root-owned while `setup-lan-access.sh` runs as `developer` — both its
|
||
`mkdir cm` and `ssh-keygen` failed silently (`|| true` / `|| exit 0`), leaving
|
||
**no jump key and no config**, breaking LAN access on the first recreate after
|
||
the persistence change. `entrypoint.sh` now chowns `~/.ssh-local` to the
|
||
developer user alongside the other named-volume mount points.
|
||
|
||
_(no other changes since v1.15.13d)_
|
||
|
||
## v1.15.13d — 2026-06-04
|
||
|
||
LAN-access fixes + ergonomics. Letter-suffix rebuild on opencode `1.15.13`
|
||
(version unchanged). Touches `setup-lan-access.sh`, which is in the base hash,
|
||
so `base-latest` / `base-pi-only` advance and the fix propagates to `pi-devbox`.
|
||
|
||
### Fixed: LAN-access `Include` was scoped to the `host`/`mac` block (named peers ignored)
|
||
|
||
The generated `~/.ssh-local/config` placed `Include ~/.ssh/config` *inside* the
|
||
`Host host mac` block. Because SSH scopes an `Include` to the enclosing
|
||
`Host`/`Match` block, the user's `~/.ssh/config` was only consulted when
|
||
targeting `host`/`mac` — so `dssh pve` / `dssh <peer>` by name silently fell
|
||
back to SSH defaults (wrong user, unresolved hostname) and never applied the
|
||
peer's settings or any `ProxyJump`. Fixed by emitting a bare `Host *` scope
|
||
reset before every `Include`.
|
||
|
||
### Fixed: read-only `~/.ssh/cm` ControlPath broke multiplexed hosts
|
||
|
||
The bind-mounted `~/.ssh/config` commonly sets `ControlPath ~/.ssh/cm/...`
|
||
(CGNAT flow-cap multiplexing), but `~/.ssh` is read-only in the container, so
|
||
every `ControlMaster`-enabled host (e.g. `pmx-jh`, `proxmox*`, `synlig`) failed
|
||
with `cannot bind to path … Read-only file system`. The generated config now
|
||
sets `ControlPath ~/.ssh-local/cm/%r@%h:%p` in the top `Host *` block
|
||
(first-value-wins) so master sockets land in the writable sidecar.
|
||
|
||
### Added: host-owned `ssh-lan.conf` for named-peer jump overrides
|
||
|
||
When the host bind-mounts `~/.config/devbox-shell/ssh-lan.conf`, the generated
|
||
config now Includes it *before* `~/.ssh/config`. Put `ProxyJump host` overrides
|
||
there (first-value-wins inherits HostName/User/IdentityFile from `~/.ssh/config`)
|
||
instead of editing the shared `~/.ssh/config` — which would break the host's own
|
||
direct access to those peers and is read-only from the container anyway. New
|
||
[`ssh-lan.conf.example`](ssh-lan.conf.example).
|
||
|
||
### Added: `DEVBOX_LAN_AUTOJUMP_PRIVATE=1` opt-in RFC1918 auto-jump
|
||
|
||
Emits a catch-all that ProxyJumps any private (RFC1918) IP through the host, so
|
||
bare `dssh user@<ip>` reaches whatever LAN the (roaming) host is currently on,
|
||
without naming peers. Matches the typed address (not the resolved HostName), so
|
||
named hosts carrying their own ProxyJump are unaffected; public IPs stay direct.
|
||
|
||
All three land in `rootfs/usr/local/lib/opencode-devbox/setup-lan-access.sh`,
|
||
which is counted in the base hash → advances `base-latest` and propagates to
|
||
`pi-devbox` (built `FROM` the base).
|
||
|
||
## v1.15.13c — 2026-06-03
|
||
|
||
Follow-up to v1.15.13b: relocates the pi-only build out of the `opencode-devbox`
|
||
repo (Option B) and fixes the base size threshold that blocked `promote-base-latest`.
|
||
|
||
### Changed: `pi-only` build now publishes to the `joakimp/pi-devbox` repo (not `opencode-devbox`)
|
||
|
||
The `pi-only` variant (added in v1.15.13b) was published under `opencode-devbox`
|
||
as `latest-pi-only` / `vX.Y.Z-pi-only` — an "opencode-devbox" tag that contains
|
||
**no opencode**, which confused users browsing the tag list.
|
||
|
||
- The `build-variant-pi-only` CI job now pushes the artifact into the
|
||
**`joakimp/pi-devbox`** repo as `base-pi-only-vX.Y.Z` (+ floating `base-pi-only`
|
||
on tag builds) instead of `opencode-devbox:*-pi-only`. New `PI_IMAGE` workflow env.
|
||
- It is still built from the same `Dockerfile.variant` (single source of truth)
|
||
and still smoke-tested by `smoke-pi-only` / `validate-pi-only` before publish.
|
||
- `opencode-devbox` now publishes **eight** tags per release (four opencode-bearing
|
||
variants) plus `base-latest`; the pi-only pair lives in the pi-devbox repo.
|
||
- De-advertised the pi-only tag from the README, `DOCKER_HUB.md` (HUB_TEMPLATE),
|
||
and AGENTS docs.
|
||
- The old `opencode-devbox:latest-pi-only` / `vX.Y.Z-pi-only` tags from v1.15.13b
|
||
are superseded and should be deleted from Docker Hub.
|
||
|
||
### Fixed: base image size threshold (unblocks `promote-base-latest`)
|
||
|
||
- Bumped the `base` variant smoke size threshold 2500 → 2600 MB. In the v1.15.13b
|
||
run the base crept to 2506 MB (LAN-access script + updated entrypoint + apt
|
||
drift) and tripped the deliberately zero-headroom 2500 ceiling, which failed
|
||
`smoke-base` and cascaded into skipping `build-variant-base` **and**
|
||
`promote-base-latest` — so `base-latest` never advanced. (`base-<hash>` and the
|
||
omos/with-pi/omos-with-pi/pi-only variants did publish on the fresh base.)
|
||
|
||
## v1.15.13b — 2026-06-03
|
||
|
||
Container-level rebuild on opencode `1.15.13` (unchanged) and pi `0.78.0` (unchanged) — adds host-OS-agnostic LAN access, the `fork`/`recall` pi extensions, and a new `pi-only` variant. Letter-suffix release per the `v{opencode_version}[letter]` scheme since no upstream version moved.
|
||
|
||
### Added: host-OS-agnostic LAN access (base image)
|
||
|
||
The container can now reach LAN peers that the **host** can reach, regardless of host OS — addressing the macOS/Docker-Desktop limitation where a container in the Linux VM cannot see the host's directly-attached LAN.
|
||
|
||
- New `rootfs/usr/local/lib/opencode-devbox/setup-lan-access.sh`, invoked (non-fatally) by `entrypoint-user.sh` on every start.
|
||
- **Detection:** on VM-backed hosts (macOS OrbStack / Docker Desktop, Windows Docker Desktop — detected via `host.docker.internal` resolution) it generates a writable `~/.ssh-local/config` that uses the host as an SSH **jump**. On native Linux Docker (LAN reachable directly) it is a **no-op**.
|
||
- **Mechanism, not policy:** ships a generic `host` (alias `mac`) jump entry + a generated jump key in the writable `~/.ssh-local/` sidecar (necessary because `~/.ssh` is bind-mounted read-only). Your own targets stay in your bind-mounted `~/.ssh/config` (add `ProxyJump host`), pulled in via `Include ~/.ssh/config`.
|
||
- New env knobs: `DEVBOX_LAN_ACCESS` (`auto`|`jump`|`off`, default `auto`), `HOST_SSH_USER`, `DEVBOX_HOST_ALIAS`. When `HOST_SSH_USER` is unset the entrypoint prints the public key to authorize on the host.
|
||
- New `dssh` / `dscp` aliases in `.bash_aliases` (wrap `ssh -F ~/.ssh-local/config`), guarded so they only appear when the jump config was generated.
|
||
- Because this touches `Dockerfile.base` inputs (`rootfs/`, `entrypoint-user.sh`), the base image rebuilds and `base-latest` advances.
|
||
|
||
### Added: pi-fork (`fork`) + pi-observational-memory (`recall`) in pi variants
|
||
|
||
The `with-pi` and `omos-with-pi` variants now bake in two pi extensions from `github.com/elpapi42`:
|
||
|
||
- `Dockerfile.variant` clones both repos to `/opt/pi-fork` and `/opt/pi-observational-memory` and runs `npm install` there at **build** time (a local-path `pi install` does not npm-install, so deps must be present for the extension to load).
|
||
- `entrypoint-user.sh` registers them at runtime via `pi install /opt/<pkg>` (instant, in-place, idempotent; `fork`/`recall` tools bind on the next pi start).
|
||
- CI (`resolve-versions`) resolves the `master` HEAD of each repo to a concrete commit SHA and passes it as `PI_FORK_REF` / `PI_OBSMEM_REF` build-args — same registry-buildcache cache-hit guard used for `PI_VERSION` / `OMOS_VERSION`.
|
||
- New build-args: `PI_FORK_REPO`, `PI_FORK_REF`, `PI_OBSMEM_REPO`, `PI_OBSMEM_REF`.
|
||
- Smoke test asserts the `/opt` clones + baked `node_modules` exist and that both packages register in `settings.json`. Size thresholds bumped: `with-pi` 2700→2900 MB, `omos-with-pi` 3700→3900 MB (fork's `@earendil-works` peer deps add ~150 MB).
|
||
|
||
### Added: `pi-only` variant (basis for `pi-devbox`)
|
||
|
||
New fifth published variant built with `INSTALL_OPENCODE=false`, `INSTALL_PI=true` — pi + companions (toolkit, extensions, `fork`, `recall`) and all base tooling, but **without** opencode (~145 MB lighter than `with-pi`).
|
||
|
||
- Published as `latest-pi-only` / `vX.Y.Z-pi-only` (multi-arch). New CI jobs `smoke-pi-only` and `build-variant-pi-only`; wired into `promote-base-latest` / `update-description` needs.
|
||
- This is the **single source of truth** for the separate [`joakimp/pi-devbox`](https://gitea.jordbo.se/joakimp/pi-devbox) image, which now `FROM`s `latest-pi-only` instead of duplicating the pi-install logic. Lets pi-devbox stay lean and pi-focused while the install logic lives in one place.
|
||
- Smoke size threshold: 2750 MB (`with-pi` minus opencode).
|
||
|
||
_Versions unchanged: opencode-ai `1.15.13`, pi `0.78.0` (both still latest at time of writing)._
|
||
|
||
## v1.15.13 — 2026-05-29
|
||
|
||
First container build on `opencode-ai@1.15.13` upstream release (published 2026-05-29). Also picks up pi `0.77.0` → `0.78.0` (resolved from npm at build time).
|
||
|
||
### Bumped: opencode-ai 1.15.12 → 1.15.13
|
||
|
||
**Core**
|
||
- Gateway Anthropic Opus 4.7+ adaptive reasoning now keeps summarized thinking instead of returning empty thinking blocks (bugfix).
|
||
- Sessions can now store custom metadata through the API and SDK ([@shantur](https://github.com/shantur)).
|
||
- Config now loads from the opened location upward, so directory-specific settings and provider policies apply more predictably.
|
||
|
||
**TUI**
|
||
- Wrapped inline tool rows now stay aligned, and failed inline tools can expand their error details in place (bugfix).
|
||
|
||
### Bumped: pi 0.77.0 → 0.78.0 (resolved from npm at build time)
|
||
|
||
See [pi-devbox v0.78.0](https://github.com/joakimp/pi-devbox/releases/tag/v0.78.0) for full pi release notes.
|
||
|
||
## v1.15.12 — 2026-05-29
|
||
|
||
First container build on the genuine `opencode-ai@1.15.12` upstream release (published 2026-05-28). Also bumps pi `0.76.0` → `0.77.0`.
|
||
|
||
> **Note on the `v1.15.12` git tag:** an earlier `v1.15.12` git tag existed at commit `be2a168` as a historical artifact from the 2026-05-28 versioning slip (re-cut as `v1.15.11c` once the slip was caught). The corresponding Hub `v1.15.12*` images were manually deleted at the time. Now that opencode upstream has actually released 1.15.12, the tag is being re-used at HEAD per the `v{opencode_version}[letter]` scheme — the old tag was force-overwritten locally and on origin. Commit `be2a168` and the v1.15.11c CHANGELOG block (which references the slip) remain in history.
|
||
|
||
### Bumped: opencode-ai 1.15.11 → 1.15.12
|
||
|
||
Notable upstream changes (from the [anomalyco/opencode v1.15.12 release](https://github.com/anomalyco/opencode/releases/tag/v1.15.12)):
|
||
|
||
- **Core** — ACP integrations can send prompts/slash-commands/usage updates through `acp-next`; experimental WebSocket transport for OpenAI Responses (`OPENCODE_EXPERIMENTAL_WEBSOCKETS=true`); adaptive reasoning enabled for Anthropic Opus 4.7+.
|
||
- **Bugfixes** — colons allowed in passwords; faster warm `acp-next` model/config switches; OpenAI WebSocket response timeouts kept active with retries before fallback; `acp-next` permission prompts handled correctly; persisted session directory used for existing-session requests; remote workspace request bodies forwarded correctly; custom base URLs supported for OpenAI WebSocket Responses.
|
||
- **TUI** — workspace management dialog; session navigation works while prompt modes are open; thinking spinner restored; subagent retry status surfaced; opening editors from non-Git project paths fixed.
|
||
- **Desktop** — tab-layout setting; home empty state and V2 font usage improved; tab close buttons showing reliably.
|
||
|
||
### Bumped: pi 0.76.0 → 0.77.0
|
||
|
||
Notable upstream changes (from pi's CHANGELOG):
|
||
|
||
- **Claude Opus 4.8 support** — model metadata + adaptive-thinking coverage updated.
|
||
- **Selective tool disablement** — `--exclude-tools` / `-xt` disables specific built-in, extension, or custom tools while keeping the rest available.
|
||
- **Headless Codex subscription login** — `/login` can use device-code auth for ChatGPT Plus/Pro Codex subscriptions.
|
||
- **Streaming-aware extension input** — `InputEvent.streamingBehavior` lets extensions distinguish idle prompts, mid-stream steers, and queued follow-ups.
|
||
- **Bugfixes** — startup timing output excludes `createAgentSessionRuntime`; OpenRouter DeepSeek V4 `xhigh` reasoning preserved; SIGTERM/SIGHUP run extension `session_shutdown` cleanly; keyboard protocol negotiation ignores delayed terminal responses; Windows MSYS2 ucrt64 startup crash fixed; API-key/header config resolution treats plain strings as literals with `$ENV_VAR` interpolation; session disposal aborts in-flight work; numerous provider-specific reasoning/metadata fixes (Codex Responses replay, OpenAI/OpenRouter GPT-5.5 Pro, Kimi K2.6, Xiaomi Token Plan).
|
||
|
||
### Inheritance from base
|
||
|
||
No base change — `base-latest` is reused unchanged from v1.15.11c (`base-decide` short-circuits at the Hub-probe step). SSH ControlMaster on a writable socket path, gitleaks, and git-crypt continue to ride along from the base.
|
||
|
||
### Workflow status
|
||
|
||
This is the first opencode-version-bump publish exercising the afternoon-of-2026-05-28 workflow changes (cache-export removal + 3-attempt retry wrapper) end-to-end on a real upstream release. v1.15.11c proved the publish path mechanically; v1.15.12 is the first one with both an opencode bump and a pi bump driving fresh variant layers.
|
||
|
||
## v1.15.11c — 2026-05-28
|
||
|
||
**Re-cut of v1.15.12 to fix a versioning-scheme violation.** The morning's v1.15.12 release was tagged in error: `opencode-ai` stayed at `1.15.11` upstream (no 1.15.12 exists on npm), so per the project's `v{opencode_version}[letter]` scheme this should have been the third container build on opencode 1.15.11 — `v1.15.11c` — not a new minor version bump. The `v1.15.12` git tag and the eight `v1.15.12*` / `latest*` Docker Hub images remain as historical artifacts but are superseded by this release. Future builds on opencode 1.15.11 continue the letter sequence as `v1.15.11d`, `v1.15.11e`, … — v1.15.12 will only be reused if and when opencode upstream actually releases 1.15.12.
|
||
|
||
Content inherited from v1.15.12 (see that block below for the full diagnostic chain on the v4.0.0 pin disproof and the manual host-side publish):
|
||
|
||
- pi `0.75.5` → `0.76.0`.
|
||
- `setup-buildx-action` pin reverted from `@v4.0.0` back to `@v4` (the v1.15.11b regression hypothesis was disproven).
|
||
- Inheritance from base: SSH ControlMaster on a writable socket path, gitleaks, git-crypt.
|
||
- Cache-hit silent same-bytes regression fix carried forward from v0.75.5b's pattern.
|
||
|
||
Additional changes since v1.15.12 (afternoon 2026-05-28 followup work):
|
||
|
||
### Hub-push regression — root cause identified, CI fixed
|
||
|
||
The `400 Bad request` from `registry-1.docker.io` that broke CI publishing across runs #332/333/334/336 (and forced v1.15.12 to ship via manual host-side push) is **buildkit's registry cache-export with `mode=max`**, not the image push itself.
|
||
|
||
**Diagnostic that nailed it:** the manual v1.15.12 publish from an Orbstack host reproduced the exact same 400 — but only on the cache-export step. Image layers pushed cleanly (911s for the base, all variants succeeded). Dropping `--cache-to` from the manual script let the publish complete. Running the same buildx version against the same Hub account from the same network, the only differential was cache export vs. image export.
|
||
|
||
This explains every observation:
|
||
|
||
- Failure shape stable across attempts (`Offset:0`, HTML body, CDN-tier rejection): cache-export protocol-level mismatch, not transient network or per-blob corruption.
|
||
- Repo-specific (`joakimp/opencode-devbox` only): we're the only Hub repo currently writing a `:base-buildcache` tag with `mode=max`.
|
||
- Started ~2026-05-23: lines up with buildx 0.34.x rolling out and bundling moby/buildkit v0.30.0, which changed the `_state` token format on resumable cache uploads.
|
||
- Image push works fine: cache-export is a separate codepath using a different manifest/layer scheme.
|
||
- Action-pin to `setup-buildx-action@v4.0.0` didn't help: that pin pulls older actions-toolkit, but the bundled buildkit was still 0.34.x via Buildx CLI on the runner image. Pin was correctly disproven by run #336.
|
||
|
||
### Workflow change
|
||
|
||
- **`.gitea/workflows/docker-publish-split.yml`** — registry cache (`cache-from`/`cache-to`) removed from the `build-base` step. Comment in place documenting the regression and the re-enable condition. Variants don't use registry cache so they're untouched. The base tag is content-addressed (`base-<hash>` derived from Dockerfile.base + rootfs/* + entrypoint*.sh) so unchanged bases short-circuit at the Hub-probe step in `base-decide` and never re-build anyway — the lost cache only affects the rare case of a Dockerfile.base change, where we now pay the full ~3 min build instead of pulling cached layers. Acceptable trade-off vs. broken publishes.
|
||
|
||
Next tag push (e.g. v1.15.13) is expected to publish cleanly via Gitea CI again. validate.yml on this main push will be the first real-time test of the smoke side; full publish path will be tested on the next opencode bump or by a deliberate letter-suffix re-tag.
|
||
|
||
### Status of earlier suspects
|
||
|
||
- ~~`setup-buildx-action@v4.1.0`~~ — disproven by v1.15.11b CI run #336 with v4.0.0 pin failing identically. Pin reverted in v1.15.12. Not the regressor.
|
||
- ~~`@docker/actions-toolkit 0.79.0 → 0.90.0`~~ — rolled back via the action pin; same failure. Not the regressor.
|
||
- ~~Account / repo / Hub-CDN globally~~ — local pushes from developer host succeed. Always was healthy.
|
||
- ~~`catthehacker/ubuntu:act-latest`~~ / ~~act-runner egress~~ — manual publish from host reproduced the same 400, ruling out runner-side network. Not the cause.
|
||
- **Confirmed:** buildkit cache-export protocol (mode=max) hitting Hub-CDN edge rejection. Workaround: don't export cache to registry. Long-term: track moby/buildkit upstream for protocol fix or switch to GHA cache (not portable to Gitea Actions).
|
||
|
||
### Docs: manual host-publish runbook + script archive
|
||
|
||
- `docs/manual-host-publish.sh` — the literal script that shipped v1.15.12 from a developer Mac via Orbstack, preserved as-is.
|
||
- `docs/manual-host-publish.md` — runbook explaining when to reach for the escape hatch, the four constants to edit (`RELEASE_TAG`, `BASE_HASH`, `PI_VERSION`, `OMOS_VERSION`), three sources for `BASE_HASH` (CI's `base-decide` log = canonical, Hub `base-latest` probe, local recompute matching CI's exact recipe including `__pycache__`/`.DS_Store`/`._*` junk filters), and adaptations for pi-devbox / letter-suffix rebuilds / partial-failure single-variant recovery.
|
||
- `AGENTS.md` — new Critical conventions bullet documenting that `cache-from`/`cache-to` is currently disabled, why, and the re-enable condition.
|
||
|
||
### CI: workflow-level retry around `docker buildx build --push`
|
||
|
||
All five push steps in `.gitea/workflows/docker-publish-split.yml` (1 base + 4 variants) are now wrapped in a 3-attempt retry loop with backoff (15s, 30s) as belt-and-braces against transient `registry-1.docker.io` blips. Replaces the `docker/build-push-action@v7` invocations with `shell: bash` steps that run `docker buildx build --push` directly so the loop is visible and tweakable. Smoke-test build steps (`load: true`, no push) are unchanged — they don't suffer from registry-side flakiness.
|
||
|
||
Does **not** mask deterministic failures: a true regression (e.g. the cache-export 400 documented above) will fail all 3 attempts identically and the job still fails by design. Belt-and-braces with the workflow-level retry-on-failure rerun heuristic in the `ci-release-watcher` skill, which catches transient-shaped runner-side failures separately. No image-side change.
|
||
|
||
### AGENTS.md addition: pre-flight scheme check
|
||
|
||
New "Versioning scheme" subsection documenting the **mandatory `npm view opencode-ai version` pre-flight check** before cutting any non-letter-suffixed tag, with this slip cited as the cautionary example.
|
||
|
||
---
|
||
|
||
## v1.15.12 — 2026-05-28
|
||
|
||
> **Note (2026-05-28 PM):** this tag violates the project's `v{opencode_version}[letter]` versioning scheme — there is no `opencode-ai@1.15.12` on npm; OPENCODE_VERSION stayed at 1.15.11 across this build. Re-cut as `v1.15.11c` at HEAD per the scheme. The git tag and Hub images for `v1.15.12*` remain as historical artifacts but are superseded by `v1.15.11c`. See the `v1.15.11c` block above for the corrected release notes.
|
||
|
||
Manual-published release. Reverts the `setup-buildx-action@v4.0.0` pin from v1.15.11b (hypothesis was disproven — see below) and bumps the bundled `pi-coding-agent` to 0.76.0 via the floating `PI_VERSION=latest` resolution.
|
||
|
||
### Why "manual-published"
|
||
|
||
v1.15.11b reproduced the exact same Hub `400 Bad request` regression as v1.15.11 (CI run #336, build-base failed twice including a Gitea auto-rerun), confirming `setup-buildx-action@v4.1.0` is **not** the regressor. After four consecutive identical CI failures across two days, the SSH-CM and gitleaks fixes were shipped by hand from a developer host's Orbstack/Docker-Desktop — a path we already knew worked in ~25s for the same multi-arch build to the same Hub account.
|
||
|
||
This release ships the same content the runner-side build would have shipped; it just bypasses the broken runner-network → Hub-CDN combo. CI auto-publishing remains broken pending separate runner-side investigation (see [AGENTS.md — known issues](AGENTS.md)).
|
||
|
||
### Workflow change
|
||
|
||
- **`.gitea/workflows/docker-publish-split.yml`** — all nine `setup-buildx-action@v4.0.0` pins reverted to `@v4`. The pin added no value (failure reproduced) and was holding us off action improvements.
|
||
|
||
### Bumped: pi-coding-agent (latest → 0.76.0)
|
||
|
||
`PI_VERSION=latest` in `Dockerfile.variant` resolves at build time. 0.76.0 was published 2026-05-27 20:03 UTC. No Dockerfile edit needed; floating-`latest` is intentional so each opencode-devbox release pulls the freshest pi without a manual bump.
|
||
|
||
### Hub-push regression — ruled out / still suspect
|
||
|
||
**Ruled out:**
|
||
- `setup-buildx-action@v4.1.0` — v4.0.0 reproduces the failure identically.
|
||
- `@docker/actions-toolkit 0.79.0 → 0.90.0` — rolled back via the action pin; same failure.
|
||
- Account / repo / Hub-CDN globally — local pushes from a developer host succeed.
|
||
- Multi-arch as such — pi-devbox v0.75.5b pushed multi-arch on 2026-05-23.
|
||
|
||
**Still suspect:**
|
||
- `catthehacker/ubuntu:act-latest` runner image (floating, not pinned in workflows).
|
||
- act-runner host network egress from `runner-2` (sustained CDN-edge rejection from this specific source IP).
|
||
- buildx 0.34.x's signed `_state` token format hitting a Hub-edge WAF/length rule that didn't apply to 0.33.x.
|
||
- Hub-side per-repo state for `joakimp/opencode-devbox` specifically (other Hub repos from the same account work).
|
||
|
||
Four failing runs share the exact failure shape: HTTP 400 with HTML body (CDN-tier, not registry backend) on the very first PUT (`Offset:0`) of the resumable layer-blob upload. UUIDs and `_state` signatures differ across attempts — only the failure pattern is stable.
|
||
|
||
---
|
||
|
||
## v1.15.11b — 2026-05-27
|
||
|
||
Container-level rebuild of v1.15.11. The original v1.15.11 release-day publish failed three times in a row (CI runs #332/333/334) with identical `400 Bad request` responses from `registry-1.docker.io` on the buildx layer-blob PUT. Build itself succeeded 30/30 each time; only the multi-arch push failed. Triaged on 2026-05-27 evening:
|
||
|
||
- **Local multi-arch buildx push from a developer host succeeds in ~25s** — same Hub account, same multi-arch path. Account, repo, and Hub-CDN are all healthy.
|
||
- **Last known-good Gitea Actions Hub push: 2026-05-23 ~20:26 UTC** (`pi-devbox v0.75.5b`). All Gitea-runner-driven pushes since 2026-05-24 have failed identically.
|
||
- **Smoking gun candidate:** `docker/setup-buildx-action@v4` floats to `v4.1.0` (published 2026-05-22 16:00 UTC). Action-resolver caches on the runner appear to have rolled forward to v4.1.0 sometime between the May 23 success and the first May 24 failure. v4.1.0 ships a newer bundled buildx/buildkit which may be using a different push protocol that trips Hub's CDN URI-length cap (the failing `_state` query string is ~1.4 KB).
|
||
|
||
### Workflow change
|
||
|
||
- **`.gitea/workflows/docker-publish-split.yml`** — all nine `docker/setup-buildx-action@v4` uses pinned to `@v4.0.0`. `setup-qemu-action@v3` left floating since QEMU wasn't in the suspected blast radius and was working on May 23. If v4.0.0 publishes cleanly we keep the pin and file an upstream buildkit/buildx issue documenting the regression.
|
||
|
||
No other source changes — same `OPENCODE_VERSION=1.15.11`, same `Dockerfile.base` and `Dockerfile.variant`, same SSH-CM bake, same gitleaks. v1.15.11 (the original tag) is preserved in the repo as a historical marker of the first publish attempt; v1.15.11b is the canonical release.
|
||
|
||
### v1.15.11
|
||
|
||
First release on opencode 1.15.11. Also bakes in four devbox-side fixes accumulated since v1.15.10 (SSH ControlMaster on a writable path, gitleaks added to base, CI resolve-versions hardening, CI cache-hit regression fix). Downstream pi-devbox inherits all of these on its next build against `base-latest`.
|
||
|
||
### Bumped: opencode 1.15.10 → 1.15.11
|
||
|
||
`OPENCODE_VERSION` ARG bumped in `Dockerfile.variant`. Highlights from the upstream release (full notes: <https://github.com/anomalyco/opencode/releases/tag/v1.15.11>):
|
||
|
||
- **Core / Improvements** — new `headerTimeout` config for provider requests (10s default for default OpenAI setups); experimental background agents now push updates without polling; remote-backed projects resolve a stable project identity; `modalities.input` / `modalities.output` can be set independently.
|
||
- **Core / Bugfixes** — dynamically added MCP servers now disconnect cleanly on removal; Google tool calling fixed after upstream tool-ID regression; resumed sessions no longer continue orphaned interrupted tools; OpenAI reasoning summaries render as separate blocks; the `shell` tool now advertises its configured timeout to the model; config loading falls back cleanly when user info is unavailable.
|
||
- **TUI** — prompt resizes with terminal width (new prompt-size config); accelerated diff-viewer scrolling; external editors open from the worktree directory when available.
|
||
- **Desktop** — refined v2 home screen, prompt, status popover, and session controls; fixed V2 titlebar errors when a session sync cache was deleted; web deployments no longer run desktop health checks; duplicate server connections are merged.
|
||
- **Extensions** — new `dispose` hook for plugins; Codex plugin now sends the expected session-ID header.
|
||
|
||
No `opencode-devbox`-side changes were required to consume 1.15.11 — pure version bump.
|
||
|
||
### Base: SSH ControlMaster default on a writable socket path
|
||
|
||
Devboxes typically mount `~/.ssh` from the host as **read-only** (security: keys remain readable but agents can't tamper with config / known_hosts / authorized_keys / plant a malicious ProxyCommand). OpenSSH's default `ControlPath` lands inside `~/.ssh/cm/`, which is unwritable on such mounts — so any attempt to use `ControlMaster auto` (or anything that wants to multiplex) fails with:
|
||
|
||
```
|
||
unix_listener: cannot bind to path /home/.../.ssh/cm/...: Read-only file system
|
||
kex_exchange_identification: Connection closed by remote host
|
||
```
|
||
|
||
The second line is downstream: when ControlMaster fails the ssh client falls back to a fresh TCP connection, and on residential CGNAT (most European ISPs) the per-(src,dst) concurrent-flow cap (~4) silently drops further SYNs once exceeded — manifesting as banner-exchange timeouts that look like a remote problem.
|
||
|
||
- **`Dockerfile.base`** — new section right after the apt block bakes `/etc/ssh/ssh_config.d/00-devbox-controlmaster.conf` with `Host *` defaults: `ControlMaster auto`, `ControlPath /tmp/sshcm/%r@%h:%p`, `ControlPersist 10m`, plus `ServerAliveInterval 30` / `ServerAliveCountMax 6` for resilience to mid-stream NAT timeouts. `/tmp` is per-container and always writable, so the read-only `~/.ssh` mount is left untouched. Debian's stock `/etc/ssh/ssh_config` includes `ssh_config.d/*.conf` *before* its own `Host *` block, so user `~/.ssh/config` overrides still win.
|
||
- **`entrypoint-user.sh`** — creates `/tmp/sshcm` mode 700 on every container start. `/tmp` is per-container so the dir doesn't survive recreation; baking it into a Dockerfile layer would be wrong. Mode 700 is required — OpenSSH refuses to use a `ControlPath` directory others can write to.
|
||
- **`scripts/smoke-test.sh`** — two new assertions: (a) the conf file exists at the expected path; (b) `ssh -G example.invalid` reports a `controlpath` rooted at `/tmp/sshcm/`. The second catches the silent regression where something later in the SSH config chain shadows the bake-in.
|
||
- **No size/threshold impact:** the conf file is ~250 bytes.
|
||
|
||
Downstream pi-devbox and any other variant inherits this on its next build against `base-latest`. Discovered while running a recon-shell from inside pi-devbox to a Proxmox node — fresh ssh hit banner timeout, debug output pointed at the read-only socket dir.
|
||
|
||
_(Originally landed on `main` 2026-05-24 as commit `668592d`; first ships in v1.15.11.)_
|
||
|
||
### Base: gitleaks added; git-crypt confirmed already installed
|
||
|
||
`gitleaks` is now baked into `Dockerfile.base` (Go-compiled binary fetched from GitHub releases, same `/releases/latest` redirect-resolution pattern as gosu/fzf/git-lfs/etc.). It pairs with `git-crypt`, which has been installed via apt all along but wasn't asserted by smoke or called out in user-facing docs. Several of the user's repos use both as part of their secret-management setup (gitleaks pre-commit hook + git-crypt for selectively-encrypted canonical config); having them in the devbox means `pi install`-style hooks fire correctly inside the container instead of warning that gitleaks is missing.
|
||
|
||
- **`Dockerfile.base`** — new `GITLEAKS_VERSION=latest` ARG + install RUN block right after `git-lfs`. Arch suffix is `x64` (not `x86_64` or `amd64`) on this project; comment in the Dockerfile flags the deviation. Adds ~21 MB to the base layer.
|
||
- **`scripts/smoke-test.sh`** — adds `git-crypt` and `gitleaks` to the "Resolved component versions" table and to the "Core binaries" assertion list. Now fails fast if either binary disappears from the base.
|
||
- **`README.md`** — "What's in the image" tree updated to name `gitleaks` alongside `git-crypt` in the dev-tools line.
|
||
- **No threshold bumps:** 21 MB on a 2500–3700 MB envelope is noise; existing variant thresholds keep their headroom.
|
||
|
||
This is a base-layer change — `base-decide` will compute a fresh `base-<hash>`, `build-base` will run on the next release (no cache hit), and all four variants will rebuild against the new base. **Downstream pi-devbox** picks up gitleaks automatically on its next release that resolves `joakimp/opencode-devbox:base-latest` to the new digest — no Dockerfile change needed there.
|
||
|
||
### CI: preventative fix for PI_VERSION/OMOS_VERSION cache-hit silent regression
|
||
|
||
Mirrors the pi-devbox v0.75.5b fix (2026-05-23) onto the four-variant pipeline here. The `with-pi`, `omos`, and `omos-with-pi` variants all install upstream npm packages (`@earendil-works/pi-coding-agent`, `oh-my-opencode-slim`) whose `*_VERSION` build-args defaulted to `latest`. When the build-arg string is byte-identical across builds, the resulting layer-hash is identical, and the registry buildcache (`base-buildcache` / variant cache-from chain) silently reuses the layer from whatever upstream version was current when the cache was first populated — the same mechanism that caused pi-devbox v0.74.0 through v0.75.5 to ship the same image bytes.
|
||
|
||
Currently masked here because `OPENCODE_VERSION` is a hard-coded ARG that bumps every release — changing a parent layer invalidates the downstream cache key for the pi/omos install layers. Masking would fail the moment we cut a `vN.N.Nb` opencode-version-unchanged release that only bumps pi or omos. Filed as a parked followup that bedtime; fixing it preventatively now.
|
||
|
||
- **`.gitea/workflows/docker-publish-split.yml`** — new `resolve-versions` job runs `npm view @earendil-works/pi-coding-agent version` and `npm view oh-my-opencode-slim version`, exposing concrete strings as job outputs. All six affected jobs (`smoke-omos`, `smoke-with-pi`, `smoke-omos-with-pi`, `build-variant-omos`, `build-variant-with-pi`, `build-variant-omos-with-pi`) now `needs:` it and pass the concrete versions as `PI_VERSION` / `OMOS_VERSION` build-args. `smoke-base` and `build-variant-base` are unaffected (no pi or omos).
|
||
- **`scripts/smoke-test.sh`** — new `run_expect` helper asserts an expected substring in command output. The pi-version check uses `EXPECTED_PI_VERSION` when set; the omos check uses `EXPECTED_OMOS_VERSION` against `npm ls -g`. Both env vars are wired from `resolve-versions` outputs in the smoke jobs. Catches the regression on the next release rather than four releases later.
|
||
- **`Dockerfile.variant`** — comment block above each affected `ARG` (`OPENCODE_VERSION`, `PI_VERSION`, `OMOS_VERSION`) documenting the cache-hit footgun + which ones are CI-resolved vs source-pinned.
|
||
- **`AGENTS.md`** — new convention bullet explaining the cache-hit class of bug and naming the resolve-versions job + EXPECTED_*_VERSION wiring as the contract to keep in lockstep.
|
||
|
||
No image-content change expected on the next release vs what `latest` would have resolved to anyway — this is purely about making sure the cache invalidates correctly going forward.
|
||
|
||
## v1.15.10 — 2026-05-23
|
||
|
||
opencode 1.15.6 → 1.15.10 bump (four upstream patch releases over two days). Plus implicit pi 0.75.4 → 0.75.5 in the `with-pi` and `omos-with-pi` variants since `PI_VERSION=latest` resolves at build time.
|
||
|
||
No image-content changes beyond the version bumps; cache hit expected on `base-35ee5fe7861a` (no `Dockerfile.base` or `rootfs/` edits since v1.14.50b).
|
||
|
||
### Notable upstream opencode changes
|
||
|
||
Sourced from <https://github.com/anomalyco/opencode/releases> (the upstream this devbox tracks).
|
||
|
||
**v1.15.7** — Grok OAuth (SuperGrok) sign-in including device-code login (@Jaaneek). V2 session APIs gain safe error responses with reference IDs (UnknownError, SessionNotFoundError, ServiceUnavailableError) so generic 500s no longer leak config details. Codex OAuth refreshes deduped to avoid repeated refresh failures (@cooper-oai). Native OpenAI OAuth requests restored. Tool schema failures now surface as friendly tool errors. PDF attachment support for Grok. Restored OpenAI reasoning streams. TUI: clearer collapsed-thinking punctuation, new sessions default to local project, single-select question checkmarks no longer collide with labels. Desktop: pinch zoom, new home view + session entry flow + titlebar, log export.
|
||
|
||
**v1.15.8** — Upstream release body empty; assumed internal/no user-visible changes.
|
||
|
||
**v1.15.9** — Redesigned diff viewer with file tree, **enabled by default**. MCP OAuth configs can set callback port and include configured scopes in client metadata (@sebin). Vertex Anthropic provider uses working `.rep.googleapis.com` endpoints for US/EU multi-region (@JPFrancoia). Many "show clearer error" improvements (default model invalid, missing PTY session, skill invocation failure, installation upgrade failure, project not found via HTTP API, MCP server not found, session busy). Native reasoning continuation metadata preserved across turns. TUI: copy worktree path from command palette, refined diff viewer shortcuts, spinner color aligned with active agent (@OpeOginni). Desktop: tab navigation in titlebar, session status in titlebar, multi-colon callback URL fix (@OpeOginni), debounced VCS refreshes.
|
||
|
||
**v1.15.10** — Single fix: restored the legacy production desktop flows for opening projects and starting sessions.
|
||
|
||
### Devbox-side notes
|
||
|
||
- **Bump:** opencode 1.15.6 → 1.15.10 (`OPENCODE_VERSION` in `Dockerfile.variant`).
|
||
- **Implicit pi bump:** `with-pi` and `omos-with-pi` variants pick up pi 0.75.5 (one patch release with cleaner read-tool cards, async file tools, more reliable package updates, Bedrock token cap fix, etc.). See [pi-devbox v0.75.5 CHANGELOG](https://gitea.jordbo.se/joakimp/pi-devbox/src/branch/main/CHANGELOG.md) for the full list.
|
||
- **Smoke threshold check:** `omos-with-pi` threshold remains at 3700 MB (set v1.15.4b 2026-05-18). Four opencode patches plus one pi patch typically add only a few MB across both; not expected to trip. If it does, recovery is the well-worn letter-suffix pattern (v1.15.10b with threshold bump).
|
||
- Built on the same CI path as v1.15.6 (pinned-crane install on real-base-rebuild, skip-promote-on-cache-hit, update-description-always-on-base-success) — all expected to remain quiet on this cache-hit run.
|
||
|
||
### Note on this CHANGELOG vs the v1.15.10 tag snapshot
|
||
|
||
The v1.15.10 tag itself was pushed before the upstream release notes were located (originally I checked `sst/opencode` which is a fork; the canonical upstream is `anomalyco/opencode`). The image content under the tag is correct, but the CHANGELOG snapshot at the tag was thinner. This expanded version is on `main` going forward; the tag's snapshot will not be retroactively rewritten.
|
||
|
||
## v1.15.6 — 2026-05-21
|
||
|
||
opencode 1.15.4 → 1.15.6 bump (two upstream patch releases) plus two workflow improvements that landed on `main` between v1.15.4b and now. No image-content changes beyond the version bump; cache hit expected on `base-35ee5fe7861a` (no `Dockerfile.base` or `rootfs/` edits).
|
||
|
||
- **Bump:** opencode 1.15.4 → 1.15.6 (`OPENCODE_VERSION` in `Dockerfile.variant`). The `with-pi` and `omos-with-pi` variants will also implicitly pick up pi 0.75.3 → 0.75.4 since `PI_VERSION=latest` resolves at build time.
|
||
- **CI: defensive `__pycache__` and macOS-metadata filter in `base-decide` hash compute.** `find rootfs -type f` previously included gitignored junk like `rootfs/__pycache__/*.pyc`, `.DS_Store`, and `._AppleDouble` files — which CI's clean checkout never sees. This bit us during v1.15.4 debugging when a stale `generate-config.cpython-314.pyc` on the local rootfs/ produced `base-3605aa6b6ab1` while CI computed `base-35ee5fe7861a`. The filter is a no-op on a clean tree (verified to still produce `35ee5fe7861a` post-filter), but defends against future stale-pyc / Finder-touched-rootfs hash mismatches. `.gitea/README.md` updated in lockstep. (commit `b6e4d89`)
|
||
- **AGENTS.md: documentation drift sweep as explicit pre-commit workflow step.** Codifies the rule that non-release commits must also grep docs for stale claims about behaviour they change, with concrete repo-specific drift hotspots. Companion clause added across the wider repo set (cloud-init, ansible, pi-devbox, pi-extensions, pi-toolkit, cli_utils, proxmox) the same day. (commit `90e5a1f`)
|
||
- **First release that exercises both the pinned-crane install (T14, v1.15.3) and the skip-promote-on-cache-hit guard (T15, v1.15.4) on this CI run path** — still cache-hit on base, so `promote-base-latest` should remain skipped via T15 and the pinned crane install will only fire when a real base rebuild happens.
|
||
|
||
## v1.15.4b — 2026-05-18
|
||
|
||
Recovery release for v1.15.4 — the `omos-with-pi` variant landed at >3500 MB and tripped the smoke threshold, so `smoke-omos-with-pi` and `build-variant-omos-with-pi` were skipped. The other three variants (base, omos, with-pi) published cleanly. Plus a latent workflow bug fix exposed by the partial publish.
|
||
|
||
- **Smoke threshold bump:** `omos-with-pi` 3500 → 3700 MB. Compounded growth: opencode 1.15.0 → 1.15.4 (4 patch versions) plus pi 0.74.0 → 0.75.3 (minor + 3 patches) both added a few MB each, and they sum in the omos-with-pi variant. Same pattern as previous threshold bumps (v1.14.31c, v1.15.0b); restores ~150 MB headroom.
|
||
- **Workflow fix — `update-description` no longer skips on partial publish.** Pre-existing latent bug: `update-description.needs` includes all four `build-variant-*` jobs, and gitea Actions' default behavior is "skipped need ⇒ skip dependent". When `build-variant-omos-with-pi` got skipped (because its smoke failed), `update-description` cascaded into a skip even though the job's `if:` condition (`tag pushed`) was true. Result: Hub description wasn't refreshed on v1.15.4 despite three variants publishing. Fix: wrap the `if:` in `always() && needs.build-variant-base.result == 'success' && ...` so the job runs as long as the base variant published, regardless of what other variants did.
|
||
- **Same fix applied to `promote-base-latest`** — had the identical latent bug. Currently masked by the cache-hit skip, but would have surfaced on a real-base-rebuild release with a single failed variant.
|
||
- No image-side changes from v1.15.4. Cache hit on the same base hash (`base-35ee5fe7861a`).
|
||
|
||
## v1.15.4 — 2026-05-18
|
||
|
||
opencode 1.15.3 → 1.15.4 bump (one upstream patch release), bundled with the CI hardening that landed on main between v1.15.3 and now.
|
||
|
||
- **Bump:** opencode 1.15.3 → 1.15.4 (`OPENCODE_VERSION` in `Dockerfile.variant`).
|
||
- **CI: pinned crane install in `promote-base-latest`.** Replaced `imjasonh/setup-crane@v0.4` with a direct `curl + tar` install pinned to crane v0.21.6. The action's bootstrap script calls `api.github.com/.../releases/latest` to discover what crane version to install. That call periodically rate-limits and produces `tag=null` → the action downloads `releases/download/null/...` → 404 → `gzip: unexpected end of file` → exit 2. We hit this on v1.15.3 (cosmetic failure since base-latest was already correct from cache hit). Pinned install removes the runtime GitHub API dependency entirely. Bump `CRANE_VERSION` deliberately when wanting updates, same pattern as the other GitHub-sourced binaries in the Dockerfile layer.
|
||
- **CI: skip `promote-base-latest` on cache-hit base builds.** When the base layer hash hasn't changed (cache-hit on the existing `base-<hash>` from a previous run), `base-latest` already points at the correct digest, so the retag is a tautology. Job now skipped entirely when `needs.base-decide.outputs.need_build == 'false'`. Manual `workflow_dispatch` with `promote_latest: true` overrides the gate as an escape hatch for hand-recovery scenarios.
|
||
- No image-side changes from the v1.15.3 baseline beyond the opencode npm version. Smoke thresholds unchanged.
|
||
|
||
## v1.15.3 — 2026-05-16
|
||
|
||
opencode 1.15.0 → 1.15.3 bump (three upstream patch releases).
|
||
|
||
- **Bump:** opencode 1.15.0 → 1.15.3 (`OPENCODE_VERSION` in `Dockerfile.variant`).
|
||
- No container-side changes. Smoke thresholds from v1.15.0b unchanged.
|
||
|
||
## v1.15.0b — 2026-05-15
|
||
|
||
Rebuild of v1.15.0 with one fix — v1.15.0's `omos` variant landed at 3206 MB, 6 MB over the 3200 MB smoke threshold, so `smoke-omos` failed and `build-variant-omos` was skipped. opencode 1.15.0 grew slightly vs 1.14.50, leaving zero headroom on the existing threshold.
|
||
|
||
- **Smoke threshold bump:** `omos` 3200 → 3300 MB, `omos-with-pi` 3400 → 3500 MB. Restores ~100 MB headroom for routine apt-get upgrade drift between releases. Documented inline in `scripts/smoke-test.sh`. No image-side changes — cache hits across the board, just a re-publish on the bumped threshold.
|
||
|
||
## v1.15.0 — 2026-05-15
|
||
|
||
opencode 1.14.50 → 1.15.0 bump (upstream minor release).
|
||
|
||
- **Bump:** opencode 1.14.50 → 1.15.0 (`OPENCODE_VERSION` in `Dockerfile.variant`).
|
||
- **Resilience:** `git clone` for pi-toolkit and pi-extensions in `Dockerfile.variant` is now wrapped in a 5-attempt retry loop with linear backoff (5s, 10s, 15s, 20s, 25s = up to ~75s total). gitea.jordbo.se occasionally returns transient HTTP 500s on the first request after idle, which previously broke the with-pi and omos-with-pi variant builds. Same pattern landed in pi-devbox repo concurrently.
|
||
- **Docs:** `DOCKER_HUB.md` mentions `joakimp/pi-devbox` as a sibling image — the pi-only build that uses this image's base layer as its parent. Generator template (`scripts/generate-dockerhub-md.py`) updated and regenerated. Hub size: 5905 bytes (well under the 25 kB limit).
|
||
- **Recovery from v1.14.50c partial publish:** the `latest-omos`, `v1.14.50c-omos` Hub gap is closed by this release — `latest-omos` will move forward to v1.15.0 once all four variants publish cleanly. Users on the floating tag were unaffected (still pointing at v1.14.41b until now).
|
||
|
||
## v1.14.50c — 2026-05-14
|
||
|
||
Recovery release for v1.14.50b's missing variants. v1.14.50b shipped only the `base` variant; `omos`, `with-pi`, and `omos-with-pi` were lost to a runner-fleet incident (see postmortem below).
|
||
|
||
No container-side changes. This is a tag-only retag to re-run the build on a now-healthy runner fleet. Same `base-35ee5fe7861a` from v1.14.50b is reused via hash-cache hit; only the four variant deltas are rebuilt and published.
|
||
|
||
### Postmortem: v1.14.50 / v1.14.50b runner-fleet incident
|
||
|
||
Two orthogonal runner-host issues compounded across runs 285–291:
|
||
|
||
1. **AVX-less runner shadowing the new fleet.** A pre-migration `act_runner` container on `nyvaken` (Sandy Bridge E3-12xx, has AVX but no AVX2; 4 weeks old, name `act_runner-runner-1`) collided with the orchestrator's freshly deployed `runner-1` VM (Broadwell-EP host, fully AVX2-capable). Gitea scheduled jobs to both. Jobs landing on the nyvaken container `npm install -g opencode-ai@1.14.50` succeeded, then ran `opencode --version` postinstall → the bundled Bun (v1.3.13 baseline) emitted `CPU lacks AVX support`, panicked, and SIGILLed (exit code 132).
|
||
2. **Containerd shared-state race at `capacity: 2`.** The new VM-based runners initially ran `act_runner` with `capacity: 2`, scheduling two concurrent jobs on a single host. Both jobs would invoke `docker/setup-buildx-action@v4`, which pulls `moby/buildkit:buildx-stable-1`. Containerd's content store raced on identical sha256 ingestion, surfacing as `commit failed: rename .../ingest/.../data .../blobs/sha256/...: no such file or directory` or `failed to extract layer: failed to Lchown ...`.
|
||
|
||
A secondary issue surfaced: **Proxmox VM `cpu:` field defaults mask AVX**. The newly-cloned runner VMs had no explicit `cpu:` line in `qm config` and inherited Proxmox's recent default `x86-64-v2-AES`, which excludes AVX even though the Broadwell-EP host silicon has full `avx2`. Fix: `qm set <vmid> --cpu x86-64-v3` (or `host` for full passthrough), then `qm shutdown` + `qm start` (live reboot is not enough). Verified inside guest with `grep -m1 -oE 'avx[2]?' /proc/cpuinfo`.
|
||
|
||
Additionally, when `promote-base-latest`'s `needs:` graph requires *all four* `build-variant-*` jobs to succeed, partial publishes leave the `base-latest` Hub alias never advancing. Workaround used during recovery: manually re-tag the new base hash via Docker Hub registry manifest API (`PUT /v2/<repo>/manifests/base-latest` with the body of `GET /v2/<repo>/manifests/base-<sha>`) using a granular Hub PAT. No blob copy needed since blobs are content-addressed.
|
||
|
||
### Recovery actions taken (orchestrator + this repo)
|
||
|
||
- Orchestrator (cloud-init + ansible repos): set explicit `cpu_type: x86-64-v3` in all runner host yaml files; provision.sh now applies `qm set --cpu` after clone; added runner-3 on proxmox003 for anti-affinity (one runner per Proxmox node); dropped `capacity: 2 → 1` on all runners; bumped `act_runner` 0.3.1 → 0.6.1 across the fleet; documented the CPU-type gotcha as gotcha #9 in cloud-init AGENTS.md and a section in proxmox-guide.md.
|
||
- User: retired the legacy `act_runner-runner-1` container on nyvaken; cleaned up stale runner registrations in Gitea Site Admin → Actions → Runners.
|
||
- This repo: no changes needed in Dockerfile.base / Dockerfile.variant; v1.14.50c is a tag-only retag.
|
||
|
||
### Fleet state at v1.14.50c
|
||
|
||
3 runners (runner-1@proxmox001, runner-2@proxmox002, runner-3@proxmox003), all `act_runner` v0.6.1, all `capacity: 1`, all expose AVX + AVX2 to the guest. No name collisions. Estimated wall clock for v1.14.50c (cache-hit base, 4 variant deltas across 3 runners with capacity:1): ~40–50 min.
|
||
|
||
## v1.14.50b — 2026-05-14
|
||
|
||
Rebuild of v1.14.50 with two fixes — the v1.14.50 release was incomplete (smokes failed under containerd contention; build-variant jobs skipped; base-latest never promoted to Docker Hub).
|
||
|
||
- **Force fresh base rebuild.** Added a `BASE_REBUILD_DATE` comment header to `Dockerfile.base` to invalidate the content hash and trigger a full base rebuild. Picks up ~5 days of Debian trixie security updates and other apt-tracked packages. The comment also documents the pattern for future intentional base-rebuilds without other code changes (recommended cadence: once per release).
|
||
- **First publish of `base-latest` alias.** `promote-base-latest` runs unconditionally on tag push (`PROMOTE_LATEST=true`), so this release is the first to put `joakimp/opencode-devbox:base-latest` on Docker Hub. Required before pi-devbox (and any other downstream image FROMing the base) can build.
|
||
|
||
## v1.14.50 — 2026-05-14
|
||
|
||
opencode 1.14.44 → 1.14.50 bump. First release on the split-base build pipeline.
|
||
|
||
- **Bump:** opencode 1.14.44 → 1.14.50 (`OPENCODE_VERSION` in `Dockerfile.variant`).
|
||
- **Infrastructure: split-base pipeline cutover.** `Dockerfile.base` + `Dockerfile.variant` replace the single `Dockerfile`. `docker-publish-split.yml` (now renamed to `docker-publish.yml` in spirit — triggers on `push: tags: v*`) replaces the old `docker-publish.yml`. The original `Dockerfile` and `docker-publish.yml` are deleted. Hash-driven base reuse: version-bump-only releases skip the base build entirely (~40–80 min wall clock with 4 runners vs ~165–180 min previously). Validated across two `workflow_dispatch` test runs (`:v0.0.0-split-test` tags on Docker Hub).
|
||
- **Fix:** `echo -e` heredoc replaced with POSIX-compatible brace-block for multiline `$GITHUB_OUTPUT` writes in the four `build-variant-*` jobs. `echo -e` does not interpret `\n` in `/bin/sh` (dash), causing `steps.tags.outputs.tags` to be empty and buildx to fail with "tag is needed when pushing to registry".
|
||
- **Docs:** New `.gitea/README.md` — architectural overview of the split-base pipeline, hash logic, wall-clock estimates, runner expectations, and the migration plan.
|
||
|
||
## v1.14.44 — 2026-05-09
|
||
|
||
opencode 1.14.42 → 1.14.44 bump (1.14.43 skipped upstream). Also completes the matrix coverage that v1.14.42 missed: `build-omos-with-pi` failed mid-publish on v1.14.42 due to an upstream npm CDN propagation race — `oh-my-opencode-slim@1.0.7` had been published declaring a dependency on `@opencode-ai/sdk@1.14.44`, and our build hit the registry within ~2 minutes of that SDK version landing, before the tarball had propagated across npm's CDN. The build returned 404 on the SDK fetch even though the manifest's `dist-tags.latest` already pointed at 1.14.44. Tarball is now fully fetchable; v1.14.44 builds cleanly across all four variants.
|
||
|
||
- **Bump:** opencode 1.14.42 → 1.14.44 (`OPENCODE_VERSION` build-arg default in both `Dockerfile` and `Dockerfile.variant`).
|
||
|
||
Known gap: `joakimp/opencode-devbox:v1.14.42-omos-with-pi` and the corresponding `latest-omos-with-pi` alias were NOT published in the v1.14.42 release (`build-omos-with-pi` job failed for the reason above). `latest-omos-with-pi` continued pointing at v1.14.41b until v1.14.44 published. Users on the `latest-omos-with-pi` floating tag were unaffected; users pulling explicit `:v1.14.42-omos-with-pi` would get a 404 from Hub. Closed by v1.14.44.
|
||
|
||
## v1.14.42 — 2026-05-09
|
||
|
||
**Note:** Of the 4 multi-arch variants, 3 published cleanly (`v1.14.42`, `v1.14.42-omos`, `v1.14.42-with-pi`, plus their `latest*` aliases). `build-omos-with-pi` failed during the publish step due to an upstream npm CDN propagation race (see v1.14.44 entry above for detail). Re-running the failed job would have required another full ~3h matrix rerun in gitea Actions; we chose to bump opencode to 1.14.44 instead and let the next tag close the gap.
|
||
|
||
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](https://github.com/earendil-works/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](https://gitea.jordbo.se/joakimp/pi-toolkit) (keybindings, env loader, settings template) and [pi-extensions](https://gitea.jordbo.se/joakimp/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 6–12 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/`](https://gitea.jordbo.se/joakimp/mempalace-toolkit/src/branch/main/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.
|