banger/docs/oci-import.md
Thales Maciel d0997fd3b5
model,cli,docs: medium-effort polish for v0.1.0
* 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>
2026-04-28 17:36:03 -03:00

5.2 KiB
Raw Permalink Blame History

OCI import (banger image pull)

banger image pull has two paths. The primary one — catalog bundle — is documented in docs/image-catalog.md. This doc covers the fallthrough: OCI-registry pull for arbitrary container images.

When to use it

Use the OCI path when you need a distro or image that isn't in the catalog. The catalog covers the common happy path (debian-bookworm); anything else (alpine, fedora, ubuntu, custom corporate images) goes through OCI pull.

banger image pull docker.io/library/alpine:3.20 --kernel-ref generic-6.12
banger image pull ghcr.io/myorg/devimg:v2        --kernel-ref generic-6.12

banger image pull dispatches based on the reference:

  • banger image pull debian-bookworm → catalog (fast path).
  • banger image pull docker.io/library/foo:bar → OCI (anything not in the catalog).

What works

  • Any public OCI image that exposes a linux/amd64 manifest.
  • Correct layer replay with whiteout semantics (.wh.* deletes, .wh..wh..opq opaque-dir markers).
  • Path-traversal, debugfs-hostile filename, and relative-symlink-escape protection.
  • Content-aware default sizing (content × 1.5, floor 1 GiB).
  • Layer caching on disk, keyed by blob sha256.
  • Ownership preservation — tar-header uid/gid/mode captured during flatten, applied to the ext4 via a debugfs pass, so setuid binaries (sudo, passwd) and root-owned config (/etc/shadow, /etc/sudoers) end up correctly owned.
  • Pre-injected banger agents — the pulled ext4 ships with banger-vsock-agent, banger-network.service, and the banger-first-boot unit already enabled.
  • First-boot sshd install — a one-shot systemd service installs openssh-server via the guest's package manager on first boot. Dispatches on /etc/os-releaseapt-get / apk / dnf / pacman / zypper. Subsequent boots skip the install.

What doesn't yet work

  • Private registries. Anonymous pulls only. Docker Hub, GHCR (public), quay.io (public) all work. Adding auth via authn.DefaultKeychain (from go-containerregistry) is a cheap follow-up when someone needs it.
  • Non-linux/amd64. The kernel catalog is x86_64-only, so pulled rootfses match. arm64 is additive in the schema.
  • Non-systemd rootfses. The injected units assume systemd as PID 1. Alpine ≥3.20 ships systemd; older alpine + void + busybox- init images won't honour the banger-* units.
  • First boot needs network access. The first-boot sshd install reaches out to the distro's package repo. VMs without NAT or without the bridge reaching the internet time out. The marker file stays in place so a later restart retries.

Architecture

Implementation details live in docs/oci-import-internals.md.

Guest-side boot sequence

On first boot of a pulled image:

  1. banger-network.service — brings the guest interface up with the IP assigned by banger's VM-create lifecycle.
  2. banger-first-boot.service (first boot only) — reads /etc/os-release, dispatches to the native package manager, installs openssh-server, enables ssh.service.
  3. banger-vsock-agent.service — the health-check daemon banger uses to confirm the VM is alive.

Subsequent boots skip step 2.

Adding distro support to first-boot

internal/imagepull/assets/first-boot.sh is the POSIX-sh dispatch. Add a new ID= branch and its install command, then rebuild banger (the asset is go:embed-ed).

Supported ID values today: debian, ubuntu, kali, raspbian, linuxmint, pop, alpine, fedora, rhel, centos, rocky, almalinux, arch, archlinux, manjaro, opensuse*, suse. Unknown distros fall back to ID_LIKE, then error cleanly.

Paths

What Where
Layer blob cache ~/.cache/banger/oci/blobs/sha256/<hex>
Staging dir ~/.local/state/banger/images/<id>.staging/
Extraction scratch $TMPDIR/banger-pull-<rand>/
Published image ~/.local/state/banger/images/<id>/rootfs.ext4

Cache lifecycle

OCI layer blobs accumulate as you pull images. Banger flattens every pull into a self-contained ext4, so the cache is purely a re-pull avoidance — losing it only costs network round-trips on the next pull of the same image. Reclaim disk with:

banger image cache prune --dry-run   # report size only
banger image cache prune             # remove every cached blob

Run with the daemon idle; an in-flight pull racing against prune may fail and need a retry.

Tech debt

  • Auth. When we add private-registry support, the natural path is authn.DefaultKeychain, which honours ~/.docker/config.json and the standard credential helpers.
  • Non-systemd rootfses. The guest agents assume systemd. Adding openrc / s6 / busybox-init variants means keeping parallel unit trees keyed on /etc/os-release.

Trust model

image pull (OCI path) delegates trust to the registry the user selected. go-containerregistry verifies layer digests against the manifest during download, so a tampered mirror can't ship modified layers without breaking the sha256 chain. Banger does not verify OCI image signatures (cosign/sigstore) — users who care should verify references out-of-band.