You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The control-plane update transaction itself — pull by digest, snapshot the brain's SQLite, write the declaration, recreate only what changed, health-check, and revert both images on failure of either. This is the expensive half of the update machinery and it is shared by the appliance and hosted profiles (UPDATES.md # 8: "we only build it once"), so it ships as a synchronous library with no trigger and no cloud in it.
New package internal/hostagent/cpupdate, driven by a consumer-side Docker interface (extend the seam brainlaunch.Docker already establishes rather than opening a second Docker client):
Pull by digest. Both refs are pinned digests, not tags (BUILD.md # 6). A pull failure aborts before anything on the box has changed.
Snapshot the brain's SQLite to /var/lib/malmo/brain-snapshots/<old-ref>.db — only when the brain ref moved. Take it with the brain stopped so the copy is consistent; a hot copy of a file another process is writing is not a backup.
Recreate only what moved. UI-only → docker compose up the control-plane project. Brain-only → stop, remove, and re-run the brain from the brainlaunch run spec so the new container is identical to a first-boot one except for the image. Both → one transaction.
Revert both on failure of either: write the previous pair back to ledger + compose, recreate, restore the SQLite snapshot if the brain moved. Report which mode failed (pull / recreate / health check) so the caller can surface it.
Fake-Docker tests cover all five paths — UI only, brain only, both, health-check failure → both reverted and the snapshot restored, pull failure → nothing on the box changed — and each asserts the declaration was written before the first recreate call, not after. The package has no HTTP surface and no knowledge of where the target came from.
Size
L
Area
backend
Depends on
#378, #379
Summary
The control-plane update transaction itself — pull by digest, snapshot the brain's SQLite, write the declaration, recreate only what changed, health-check, and revert both images on failure of either. This is the expensive half of the update machinery and it is shared by the appliance and hosted profiles (
UPDATES.md# 8: "we only build it once"), so it ships as a synchronous library with no trigger and no cloud in it.Spec / source of truth
docs/specs/UPDATES.md# 3 (update mechanics, rollback) and # 8.3 / # 8.4 (one actor, staged-compose handoff)Do
New package
internal/hostagent/cpupdate, driven by a consumer-side Docker interface (extend the seambrainlaunch.Dockeralready establishes rather than opening a second Docker client):BUILD.md# 6). A pull failure aborts before anything on the box has changed./var/lib/malmo/brain-snapshots/<old-ref>.db— only when the brain ref moved. Take it with the brain stopped so the copy is consistent; a hot copy of a file another process is writing is not a backup.docker compose upthe control-plane project. Brain-only → stop, remove, and re-run the brain from thebrainlaunchrun spec so the new container is identical to a first-boot one except for the image. Both → one transaction.GET /healthz(Brain serves GET /healthz so the updater can probe it #378) and the UI with a plain HTTP probe, bounded at 60s perUPDATES.md# 3 step 3d.Structured logging with the standard fields (
image,step,err); add any new recurring field to CLAUDE.md.Touch
internal/hostagent/cpupdate/(new),internal/hostagent/brainlaunch/,docs/specs/UPDATES.md,docs/progress/Done when
Fake-Docker tests cover all five paths — UI only, brain only, both, health-check failure → both reverted and the snapshot restored, pull failure → nothing on the box changed — and each asserts the declaration was written before the first recreate call, not after. The package has no HTTP surface and no knowledge of where the target came from.