* model.ParseSize / FormatSizeBytes: pinned with table tests in
internal/model/types_test.go (TestParseSize 22 cases,
TestFormatSizeBytes 11 cases, TestParseSizeFormatRoundTrip 7
boundaries). Fixed the long-suffix regression: "4GiB", "512MiB",
"4KiB" now parse correctly (parser strips trailing IB before
inspecting the unit byte). Pinned current behaviour for
no-suffix input ("1024" treated as MiB) and FormatSizeBytes(0).
commands_image.go --size flag-help updated to show 4GiB now
that the parser accepts it.
* vm ports --json: matches the JSON-vs-table inconsistency between
vm stats (always JSON) and vm ports (always table). --json on
vm ports flips to the same printJSON path as vm stats. Default
table output unchanged. Other vm subcommands (show, stats,
logs, health, ping) didn't fit the identical pattern; left
alone.
* docs/oci-import.md architecture section moved to a new
docs/oci-import-internals.md (precedent: internal/daemon/
ARCHITECTURE.md). User-facing oci-import.md keeps a one-line
pointer for advanced reading.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OCI layer blobs accumulate forever — every pull writes layers to
~/.cache/banger/oci/blobs/sha256/<hex> via go-containerregistry's
filesystem cache, and nothing ever evicts them. The cache is purely
a re-pull-avoidance (every flattened image is independent of the
blobs that sourced it), so it's a perfect candidate for an opt-in
operator-driven prune.
New surface:
* api: ImageCachePruneParams{DryRun}, ImageCachePruneResult
{BytesFreed, BlobsFreed, DryRun, CacheDir}.
* daemon: ImageService.PruneOCICache walks layout.OCICacheDir for
a (bytes, blobs) tally, then — outside dry-run — atomically
renames the cache aside, recreates it empty, and rm -rf's the
aside dir. The rename-then-rm avoids leaving the cache in a
half-removed state if a pull starts mid-prune (the in-flight
pull's open files survive the rename via standard Linux
semantics; it just sees a fresh empty cache afterwards). Missing
cache dir is treated as zero — fresh installs that have never
pulled an OCI image don't error.
* dispatch: image.cache.prune RPC (paramHandler-wrapped, mirroring
every other image RPC). Documented-methods test list updated.
* cli: `banger image cache` group with a `prune` subcommand
(--dry-run flag). Output is a single line: "freed 1.2 GiB
across 47 blob(s) in /var/cache/banger/oci" or "would free …".
formatBytes helper for the size pretty-print.
docs/oci-import.md: replaced the "Tech debt: cache eviction" bullet
with a "Cache lifecycle" section describing the new command and
the in-flight-pull caveat.
Tests: PruneOCICache covers the happy path (real prune empties the
cache, recreates an empty dir, doesn't leak the .pruning- aside),
the dry-run path (returns size, leaves blobs intact), and the
fresh-install path (cache dir absent → zero result, no error).
Smoke at JOBS=4 still green; live exercise against an empty cache
on a system install prints the expected zero summary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Preserve cleanup after daemon restarts and harden OCI and tar imports
against filenames that debugfs cannot encode safely.
Mirror tap, loop, and dm teardown identity onto VM.Runtime, teach
cleanup and reconcile to fall back to those persisted fields when
handles.json is missing or corrupt, and clear the recovery state on
stop, error, and delete paths.
Reject debugfs-hostile entry names during flattening and in
ApplyOwnership itself, then add regression coverage for corrupt
handles.json recovery and unsafe import paths.
Verified with targeted go tests, make lint-go, make lint-shell, and
make build.
The `image build` flow spun up a transient Firecracker VM, SSHed in,
and ran a large bash provisioning script to derive a new managed
image from an existing one. It overlapped heavily with the golden-
image Dockerfile flow (same mise/docker/tmux/opencode install logic
duplicated in Go as `imagemgr.BuildProvisionScript`) and had far more
machinery: async op state, RPC begin/status/cancel, webui form +
operation page, preflight checks, API types, tests. For custom
images, writing a Dockerfile is simpler and more reproducible.
Removed end-to-end:
- CLI `image build` subcommand + `absolutizeImageBuildPaths`.
- Daemon: BuildImage method, imagebuild.go (transient-VM orchestration),
image_build_ops.go (async begin/status/cancel), imagemgr/build.go
(the 247-line provisioning script generator and all its append*
helpers), validateImageBuildPrereqs + addImageBuildPrereqs.
- RPC dispatches for image.build / .begin / .status / .cancel.
- opstate registry `imageBuildOps`, daemon seam `imageBuild`,
background pruner call.
- API types: ImageBuildParams, ImageBuildOperation, ImageBuildBeginResult,
ImageBuildStatusParams, ImageBuildStatusResult; model type
ImageBuildRequest.
- Web UI: Backend interface methods, handlers, form, routes, template
branches (images.html build form, operation.html build branch,
dashboard.html Build button).
- Tests that directly exercised BuildImage.
Doctor polish (task C):
- Drop the "image build" preflight section entirely (its raison d'être
is gone).
- Default-image check now accepts "not local but in imagecat" as OK:
vm create auto-pulls on first use. Only flag when the image is
neither locally registered nor in the catalog.
Net: 24 files touched, 1,373 lines deleted, 25 added.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lead the README with `banger vm run` (one command, auto-pull default
image + kernel from the catalogs), move `image register` / `image
build` / OCI-pull to a "power-user flows" section. Golden-image
content from customize.sh moves to the golden-image Dockerfile story.
New `docs/image-catalog.md` mirrors `docs/kernel-catalog.md` — the
bundle format, content-addressed filenames, publish flow, trust
model, R2 hosting. Cross-links with oci-import.md.
`docs/oci-import.md` refactored to document the OCI-pull path as the
fallthrough for arbitrary registry refs (it's the secondary path now
that the catalog covers the headline debian-bookworm case). Phase A
caveats removed — ownership fixup, agent injection, and first-boot
sshd install all landed.
AGENTS.md: promotes `vm run` as the smoke-test primitive, notes the
default-image auto-pull behaviour, and points at both catalog docs.
README shrinks 330 → 198 lines, mostly by removing the experimental
void/alpine sections (those flows still work as advanced scripts but
the README no longer advertises them).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs/oci-import.md: removed the "Phase A acquisition-only" framing
and the bootability-gap warnings. Expanded architecture section
with ApplyOwnership + InjectGuestAgents. Added a "guest-side boot
sequence" diagram-in-prose showing network → first-boot → vsock-
agent unit ordering. Added a "how to add distro support" section
pointing at the ID-case dispatch in first-boot.sh.
README.md: replaced the experimental-caveat block with an honest
"boots as a banger VM directly, no image build step required"
description. Pointer to the docs for distro support details.
Tech-debt list trimmed — ownership fixup and first-boot install
are no longer planned work, they shipped. What remains: private-
registry auth (authn.DefaultKeychain), cache eviction, first-boot
timeout UX (retry still works but could be smoother with a
FirstBootPending flag), non-systemd distros.
All 20 packages green. make lint clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New docs/oci-import.md covers the full Phase A story:
- end-user flow (kernel pull + image pull + image list)
- what works now (layer replay + whiteouts, path-traversal
hardening, content-aware sizing, layer caching, composition
with image build)
- what does not work yet (direct boot due to ownership
caveat, private registries, non-amd64 platforms)
- architecture of internal/imagepull + the daemon orchestrator
- path layout (OCI cache, staging, published)
- tech debt: the three plausible ownership-fixup approaches
(debugfs, hcsshim/tar2ext4, user namespaces) with honest
trade-offs for Phase B to choose from later
- trust model (digest chain covers transport; signature
verification out of scope)
README.md gains an image pull example alongside image register
+ --kernel-ref, with a pointer to the docs and an honest "pulled
images are a base for image build, not yet directly bootable"
warning.
AGENTS.md gets the one-line note pointing at the new doc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>