Prune stale firmware cache versions after download - #199
Conversation
Each go-microvm version bump creates a new firmware cache directory at ~/.cache/broodbox/firmware/<version>/, but only the pinned version is ever used. Old version dirs (tens of MB each) were never deleted, so the cache grew unbounded. After a successful fresh download (with the firmware lock held), scan sibling version dirs under the cache root and remove any that don't match the version just downloaded. Pruning runs only on a fresh download, not on a cache hit, and never on transient temp entries (.firmware.lock, firmware-*.tar.gz, firmware-extract-*). Failures are logged at debug level but never fail the download — the firmware is already cached. Also add a `task firmware-clean` target for manual cache removal. Fixes #12 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP887qH8BMW4PMUXBuEqGc
JAORMX
left a comment
There was a problem hiding this comment.
Panel review — 3 axes, 4 reviewers
Ran a three-axis panel review (Spec / Standards / Domain) with the Domain axis fanned out to secure-code-reviewer, software-architect, devops-expert, and library-reuse-reviewer. Findings are inline as line-anchored comments; summary below.
Spec — ✅ clean
All five implementation requirements in issue #12 are satisfied (list cacheRoot, skip lock/temp entries, remove non-current version dirs, debug log per prune, failures don't fail the download). No scope creep — the task firmware-clean target is explicitly in scope. Call site is fresh-download-only with the flock held, matching the spec.
Standards — ✅ clean
SPDX headers present, log/slog exclusively, correct DDD layer (internal/infra/vm/, no domain/sandbox/client leakage), table-driven tests alongside code, imperative commit message.
Domain — 0 ship-blockers, 1 mechanical, 1 cross-confirmed Info, 1 polish
- [Medium]
firmware-cleanresolves the cache root differently from the Go code on macOS → silently no-ops on a supported platform. See Taskfile.yaml comment. - [Info — cross-confirmed]
firmware-prefix guard is magic-string coupled to the download temp prefixes. Two reviewers independently concluded it's defense-in-depth, not load-bearing (failure mode is "leaves garbage," not "deletes something needed"). Optional shared constant. - [Info] Prune failures at
Debugmay hide a consistently-failing prune from default log output. ConsiderWarnfor unexpectedRemoveAllerrors.
Most important single issue: the Medium macOS path drift in firmware-clean — the only finding that changes actual behavior on a supported platform. The Go-side change (pruneStaleFirmwareVersions) is sound; secure-code-reviewer verified no symlink/TOCTOU/path-traversal exposure under the actual threat model (0700 owner-only cache root, lock held, stdlib RemoveAll doesn't follow symlinks).
🤖 Generated with Claude Code
- firmware-clean Taskfile target now mirrors xdg.CacheHome resolution (XDG_CACHE_HOME → Darwin /var/home/jaosorior/Library/Caches → /var/home/jaosorior/.cache) so macOS no longer removes the wrong path while firmware accumulates. - Hoist the transient-entry "firmware-" prefix into firmwareTempPrefix, referenced by both os.CreateTemp/os.MkdirTemp patterns and the prune skip-guard, so the coupling is explicit. - Bump unexpected os.RemoveAll failures in pruneStaleFirmwareVersions to Warn (only "not exist" stays at Debug) so a persistently-failing removal doesn't silently refill the cache disk.
Summary
Fixes #12 — the firmware cache grew unbounded across go-microvm version bumps.
Each version bump creates a new
~/.cache/broodbox/firmware/<version>/directory (~tens of MB), but only the version pinned ingo.modis ever used. Old version dirs were never deleted.Approach
In
downloadFirmware, after a successful fresh download (manifest written, firmware lock still held), callpruneStaleFirmwareVersions:.firmware.lock(a file),firmware-*.tar.gztemp archives (files), andfirmware-extract-*temp dirs (firmware-prefix guard).The file lock is held throughout, so there are no concurrency concerns.
Also adds a
task firmware-cleantarget to wipe the whole firmware cache manually (honorsXDG_CACHE_HOME), as suggested in the issue.Tests
Verified:
task lint(0 issues), fulltask testpasses (incl.-race).🤖 Generated with Claude Code