banger/docs/kernel-catalog.md
Thales Maciel da4a6bf45b
Add lint targets, fix gofmt drift, broaden Makefile build inputs
Three small operational improvements.

1. Makefile build dependencies now cover everything under cmd/ and
   internal/, not just *.go. The previous GO_SOURCES find pattern
   missed embedded assets (catalog.json today, anything else added
   later), so editing a JSON manifest didn't trigger a rebuild and
   left the binary stale. New BUILD_INPUTS covers all files; go's own
   build cache absorbs any redundant invocations. GO_SOURCES is kept
   for fmt/lint targets which still want only Go files.

2. New `make lint` (default + lint-go + lint-shell):
   - lint-go: gofmt -l (fail if any output) and go vet ./...
   - lint-shell: shellcheck --severity=error on scripts/*.sh
   The shell floor is set at error-level for now; the legacy
   make-rootfs-*.sh / make-*-kernel.sh / customize.sh scripts have
   warning-level findings (sudo-cat redirects, heredoc quoting) that
   would block landing this if we tightened immediately. Documented
   as tech debt in docs/kernel-catalog.md alongside a note about
   eventually replacing the per-distro bash with a uniform Go tool.

3. gofmt drift fixed in internal/daemon/imagemgr/build.go,
   session/session.go, and vm_create_ops.go (trailing newline +
   gofmt's preferred function-definition wrapping). Now
   `make lint` passes cleanly; future drift will fail CI/local lint
   instead of accumulating.

AGENTS.md gains a one-line note on make lint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 16:49:17 -03:00

5.7 KiB

Kernel catalog

The kernel catalog ships pre-built Firecracker-ready kernel bundles so users don't have to compile anything. The catalog is embedded into the banger binary and updated each release.

End-user flow:

banger kernel list --available    # browse the catalog
banger kernel pull void-6.12      # download a bundle (no sudo, no make)
banger image register --name void --rootfs … --kernel-ref void-6.12

Architecture

Two parts:

  1. internal/kernelcat/catalog.json — a JSON manifest embedded into the banger binary via go:embed. Each entry carries a name, distro, arch, kernel version, tarball URL, and tarball SHA256. Updating the catalog means editing this file in the repo and rebuilding banger.

  2. Tarballs at https://kernels.thaloco.com/ — Cloudflare R2 bucket banger-kernels, fronted by a public custom domain. Each tarball is <name>-<arch>.tar.zst and contains vmlinux, optional initrd.img, and an optional modules/ tree at the archive root.

The banger kernel pull flow streams the tarball, verifies its SHA256 against the embedded catalog entry, decompresses it (zstd), extracts it into ~/.local/state/banger/kernels/<name>/, and writes a manifest. Path traversal entries and unsafe symlinks are rejected.

Adding or updating an entry

The repo has no CI for kernel publishing yet. Catalog updates are manual and infrequent (kernel version bumps every few weeks at most).

# 1. Build the kernel locally with the existing helper.
make void-kernel       # or: make alpine-kernel

# 2. Import it into the local catalog so the canonical layout exists.
banger kernel import void-6.12 \
  --from build/manual/void-kernel \
  --distro void \
  --arch x86_64

# 3. Package, upload, patch catalog.json.
scripts/publish-kernel.sh void-6.12 \
  --description "Void Linux 6.12 kernel for Firecracker microVMs"

# 4. Review and commit the catalog change.
git diff -- internal/kernelcat/catalog.json
git add internal/kernelcat/catalog.json
git commit -m 'kernel catalog: add/update void-6.12'

# 5. Rebuild so the new catalog is embedded.
make build

scripts/publish-kernel.sh reads the locally-imported entry under ~/.local/state/banger/kernels/<name>/, builds a tar+zstd archive, uploads it to R2 via rclone, HEAD-checks the public URL, and patches internal/kernelcat/catalog.json with the new URL, SHA256, and size.

Environment overrides if the defaults need to change: RCLONE_REMOTE, RCLONE_BUCKET, BASE_URL, BANGER_KERNELS_DIR.

Removing an entry

  1. Delete the line from internal/kernelcat/catalog.json and commit.
  2. Delete the tarball from R2: rclone delete r2:banger-kernels/<name>-<arch>.tar.zst.
  3. Rebuild banger.

Already-pulled local copies on user machines are not invalidated — they keep working until the user runs banger kernel rm <name>. That's intentional: pulling is idempotent, removing should not break anyone in the middle of a workflow.

Versioning conventions

  • Entry names: <distro>-<major.minor> (e.g. void-6.12, alpine-3.23). The major.minor is the kernel line, not the distro release. Patch-level bumps reuse the entry name and replace the tarball; minor bumps create a new entry (void-6.13).
  • Architecture: only x86_64 is published today. The arch field in the catalog schema is additive — adding arm64 later is a config change, not a schema change.
  • Tarball layout: contents at the archive root (no top-level versioned directory). vmlinux is required; initrd.img and modules/ are optional. Symlinks inside modules/ are allowed but must resolve within the archive.

Trust model

The embedded catalog.json carries the SHA256 of each tarball. banger kernel pull rejects any download whose hash doesn't match. This protects against transport corruption and against an attacker swapping a tarball on R2 without also pushing a banger release.

It does not protect against a compromise of the banger source repo itself — an attacker who can land a commit can change both the catalog SHA256 and the tarball. 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-kernels), served at the custom domain kernels.thaloco.com. The bucket is publicly readable; writes require the banger-kernels-publish API token (kept locally, never committed). R2's free tier covers the expected traffic comfortably (zero egress fees, generous storage).

If hosting ever moves, catalog entries can be migrated by reuploading the tarballs and editing the URLs in catalog.json — no other code changes required.

Tech debt: kernel-build scripts

scripts/make-void-kernel.sh and scripts/make-alpine-kernel.sh are procedural bash that fetches and patches per-distro kernel sources. Each new distro means a new bespoke script. They're "good enough" because catalog refreshes are infrequent and only the maintainer runs them, but they are the bottleneck if the catalog ever wants to grow beyond two distros.

A future iteration should:

  • Move kernel acquisition into a Go (or at least uniform) tool with a per-distro plugin/config rather than per-distro scripts.
  • Encode kernel config and required modules declaratively so a Debian or Fedora target is a config addition, not a new script.
  • Run unattended in CI once banger goes public — the manual scripts/publish-kernel.sh flow scales until then.

Until that happens, make lint-shell only runs at --severity=error. Tightening to --severity=warning would surface real issues in the legacy build scripts (mostly sudo cat > file redirects and heredoc-quoting concerns); fixing those is a prerequisite to bumping the lint floor.