Repository navigation
feat(sandbox): offer the newer guest image, and keep the guest's home - #272
Closed
quickbeard wants to merge 2 commits into
Closed
quickbeard wants to merge 2 commits into
quickbeard wants to merge 2 commits into
Conversation
Reset already re-provisioned from the current manifest, so an image upgrade existed — but nothing ever said one was available, so guests stayed on the image they were created with until somebody happened to reset, which throws away everything inside the sandbox on the way. Detection is a marker comparison the providers already had the pieces for: each provisioned sandbox records the asset it was built from, and the new SandboxProvider.imageStatus compares its digest with the published one. Digests, not versions — the same version string has been republished with different bytes. A sandbox built from a custom image file reports "custom" and is never offered anything: the manifest is not its source. The controller sweeps every sandbox (skipping any with a job in flight) and the desktop app asks shortly after launch, every six hours, and whenever Settings → Servers opens, sharing one memoized manifest fetch. A check that cannot reach the storage host leaves the previous verdict alone rather than reporting "up to date" about a question nobody answered. Taking the update keeps the guest's home — its sessions, its config, the files the user made — which is what separates it from Reset, still a wipe. What that takes is entirely per-backend, which is why it is a method rather than a flag on reset: - bubblewrap: the home is a host directory, so it is simply not deleted. Only the rootfs is replaced, wholesale rather than unpacked over the old one. - WSL: a distro cannot be imported over itself, so /home/codev is tarred out to the host before the old one is unregistered — the export runs while nothing has been destroyed yet, so failing it costs nothing — and tarred back in after. Both directions go over stdio, which keeps the archive binary-safe and Windows paths off the command line. - vz: the home lives in the disk being replaced, so the outgoing disk is set aside and attached read-only to the new guest, whose init copies the home across before starting the server; start drops it once the server answers, since a healthy guest is proof the copy finished. codev-vmm learns to attach extra disks for it. The code that reads that disk lives in the image being updated *to*, so images now declare `home-migration` and the host knows before downloading anything whether this update can keep the home — and says so in the confirmation rather than finding out afterwards. It takes effect once an image built from this pipeline is published. Both backends that stage the home outside the sandbox refuse to overwrite one an interrupted update left behind: that archive, or that disk, holds the home the sandbox is still trying to keep, and the current guest's — freshly imported by the run that failed — cannot be worth more. The offer appears where sandboxes are used: a line on the Work sidebar, and a tag plus an Update item on the Settings → Servers row, both behind a confirmation that states what survives. Nothing downloads on its own; it is over a gigabyte and the sandbox restarts to take it. A successful provision now also prunes staged images no sandbox's marker names any more. Nothing else ever removed them, so updating would otherwise have left its predecessor on disk forever. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
quickbeard
force-pushed
the
feat/sandbox-image-updates
branch
from
August 19, 2026 06:46
ed0d065 to
8a53340
Compare
Pruning ran after a successful provision, which covered the update that supersedes an image but not the delete that abandons one: destroy removed the sandbox's own directory and left the image it was built from — over a gigabyte — in the cache, where nothing would ever have collected it. Deleting is the one moment a user expects the disk back. Each backend prunes after its destroy, with the same rule the provision path uses: an image any remaining sandbox's marker still names is kept, so deleting one of two sandboxes on the same image costs the other nothing. The deleted sandbox's marker went with its directory, so no keep list is needed. On WSL it sits in destroy rather than in the discard() that update() also calls. Pruning between an update's discard and its re-provision would drop an image an interrupted earlier attempt had already staged and charge the user another download for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reset already re-provisioned from the current manifest, so an image upgrade existed — but nothing ever said one was available, so guests stayed on the image they were created with until somebody happened to reset, which throws away everything inside the sandbox on the way. This is the README's one listed known gap.
Detection
Every provisioned sandbox already recorded the asset it was built from. The new
SandboxProvider.imageStatuscompares that marker's digest with the published one — digests, not versions, since the same version string has been republished with different bytes. A sandbox built from a custom image file reportscustomand is never offered anything: the manifest is not its source.The controller sweeps every sandbox, skipping any with a job in flight, and the desktop app asks shortly after launch, every six hours, and whenever Settings → Servers opens, sharing one memoized manifest fetch. A check that cannot reach the storage host leaves the previous verdict alone rather than reporting "up to date" about a question nobody answered.
Keeping the home
Taking the update keeps the guest's home — its sessions, its config, the files the user made — which is what separates it from Reset, still a wipe. What that takes is entirely per-backend, which is why it is a method rather than a flag on
reset:/home/codevis tarred out to the host before the old one is unregistered (the export runs while nothing has been destroyed yet, so failing it costs nothing) and tarred back in after. Both directions go over stdio, which keeps the archive binary-safe and Windows paths off the command line.startdrops it once the server answers, since a healthy guest is proof the copy finished.codev-vmmlearns to attach extra disks for it.Both backends that stage the home outside the sandbox refuse to overwrite one an interrupted update left behind: that archive, or that disk, holds the home the sandbox is still trying to keep, and the current guest's — freshly imported by the run that failed — cannot be worth more.
The macOS caveat, made explicit
The code that reads the outgoing disk lives in the image being updated to, so images now declare
home-migrationand the host knows before downloading anything whether an update can keep the home — the confirmation says so, rather than finding out afterwards. It takes effect once an image built from this pipeline is published; an update to today's1.18.4-8is a fresh start and says as much. The WSL and bubblewrap halves work against the images already published.Elsewhere
The offer appears where sandboxes are used: a line on the Work sidebar, and a tag plus an Update sandbox item on the Settings → Servers row, both behind a confirmation that states what survives. Nothing downloads on its own — it is over a gigabyte and the sandbox restarts to take it. A successful provision now also prunes staged images no sandbox's marker names any more; nothing else ever removed them, so updating would otherwise have left its predecessor on disk forever.
Verification
bun testinpackages/codev-sandbox: 189 pass / 2 skip (+31 tests). Typechecks clean incodev-sandbox,appanddesktop.script/e2e-vz.ts --migrate(new flag): create 7.3s, start 2.4s, health/path/broker OK, migration disk attached and dropped after a healthy start. Theextra mount: FAILEDline in that run comes from an old cached image predatingmounts.jsonand reproduces without the flag.🤖 Generated with Claude Code