# 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 `. The catalog is embedded into the banger binary and updated each release. End-user flow: ```bash banger image pull debian-bookworm # explicit banger vm run --name sandbox # implicit (auto-pulls) ``` ## Architecture Two parts — the same shape as the kernel catalog: 1. **`internal/imagecat/catalog.json`** — JSON manifest embedded into the banger binary via `go:embed`. Each entry: name, distro, arch, kernel_ref (a `kernelcat` entry name), tarball URL, tarball sha256, size. 2. **Tarballs at `https://images.thaloco.com/`** — Cloudflare R2 bucket `banger-images`, fronted by a public custom domain. Each tarball is `--.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) and `manifest.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 `` doesn't match a catalog entry — see [`docs/oci-import.md`](oci-import.md). ## Adding or updating an entry The repo has no CI for bundle publishing yet. Catalog updates are manual. ```bash # 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 1. Remove the entry from `internal/imagecat/catalog.json` and commit. 2. Delete the tarball from R2: `rclone delete banger-images:banger-images/--.tar.zst`. 3. Rebuild banger. Already-pulled local images are not invalidated — users keep using them until they run `banger image delete `. ## Versioning conventions - **Entry names**: `-` (e.g. `debian-bookworm`). Per-release names make it trivial to publish `debian-trixie` alongside without collisions. - **Content-addressed filenames**: the `-` suffix is mandatory (set by `publish-golden-image.sh`). Never reuse a URL for different bytes. - **Architecture**: `x86_64` only today. The `arch` field is additive — adding `arm64` is 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.