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>
4.6 KiB
Image catalog
The image catalog ships pre-built banger rootfs bundles so users don't
have to register or build anything. It's the fast path behind
banger vm run (auto-pull) and banger image pull <name>. The
catalog is embedded into the banger binary and updated each release.
End-user flow:
banger image pull debian-bookworm # explicit
banger vm run --name sandbox # implicit (auto-pulls)
Architecture
Two parts — the same shape as the kernel catalog:
-
internal/imagecat/catalog.json— JSON manifest embedded into the banger binary viago:embed. Each entry: name, distro, arch, kernel_ref (akernelcatentry name), tarball URL, tarball sha256, size. -
Tarballs at
https://images.thaloco.com/— Cloudflare R2 bucketbanger-images, fronted by a public custom domain. Each tarball is<name>-<arch>-<sha256-prefix>.tar.zst(content- addressed filename so CDN edge cache can never serve stale bytes for the URL the catalog points at). Contents at the archive root:rootfs.ext4(finalized: flattened + ownership-fixed + agent- injected at build time) andmanifest.json.
The banger image pull bundle path streams the tarball, verifies
sha256 against the catalog entry, extracts both files into a staging
dir, resolves the kernel via kernel_ref (auto-pulling from
kernelcat if the user hasn't pulled it yet), stages boot artifacts
alongside, and registers the result as a managed image.
The same image pull command transparently falls through to the
existing OCI-pull path when <name> doesn't match a catalog entry —
see docs/oci-import.md.
Adding or updating an entry
The repo has no CI for bundle publishing yet. Catalog updates are manual.
# 1. Build the bundle + upload + patch catalog.json in one shot.
scripts/publish-golden-image.sh
# 2. Review and commit the catalog change.
git diff -- internal/imagecat/catalog.json
git add internal/imagecat/catalog.json
git commit -m 'imagecat: publish debian-bookworm'
# 3. Rebuild so the new catalog is embedded.
make build
scripts/publish-golden-image.sh wraps scripts/make-golden-bundle.sh
(which runs docker build on images/golden/Dockerfile then pipes
docker export into banger internal make-bundle), computes the
bundle's sha256, uses the first 12 hex chars as a cache-busting
filename suffix, uploads via rclone to R2, HEAD-checks the public
URL, and patches internal/imagecat/catalog.json.
Environment overrides if the defaults need to change:
RCLONE_REMOTE, RCLONE_BUCKET, BASE_URL.
--skip-upload builds the bundle into dist/ and stops — useful for
local testing without touching R2 or the catalog.
Bundle format
A bundle is a tar+zstd archive with exactly two entries at the root:
rootfs.ext4 # finalized banger rootfs
manifest.json # {name, distro, arch, kernel_ref, description}
rootfs.ext4 is fully prepared at build time: ownership fixed via
debugfs sif, banger guest agents (vsock agent, network bootstrap,
first-boot unit) already injected and enabled in
multi-user.target.wants. The pull path only has to place the file
and register the image — no mkfs, no ownership pass, no injection on
the daemon host.
Removing an entry
- Remove the entry from
internal/imagecat/catalog.jsonand commit. - Delete the tarball from R2:
rclone delete banger-images:banger-images/<name>-<arch>-<hash>.tar.zst. - Rebuild banger.
Already-pulled local images are not invalidated — users keep using
them until they run banger image delete <name>.
Versioning conventions
- Entry names:
<distro>-<release>(e.g.debian-bookworm). Per-release names make it trivial to publishdebian-trixiealongside without collisions. - Content-addressed filenames: the
-<sha256-prefix>suffix is mandatory (set bypublish-golden-image.sh). Never reuse a URL for different bytes. - Architecture:
x86_64only today. Thearchfield is additive — addingarm64is a config change, not a schema change.
Trust model
Same as the kernel catalog: the embedded catalog.json carries each
bundle's sha256, and imagecat.Fetch rejects any download whose hash
doesn't match. This protects against transport corruption and against
an attacker swapping an R2 object without landing a commit in the
banger repo. GPG/sigstore signing is deferred until banger is public
and the threat model justifies the operational overhead.
Hosting
Tarballs live in Cloudflare R2 (bucket banger-images), served at
images.thaloco.com. The bucket is publicly readable; writes require
the R2 API token configured on the banger-images rclone remote.