banger/README.md
Thales Maciel ea72ea26fe
Add Go daemon-driven VM control plane
Replace the shell-only user workflow with `banger` and `bangerd`: Cobra commands, XDG/SQLite-backed state, managed VM and image lifecycle, and a Bubble Tea TUI for browsing and operating VMs.\n\nKeep Firecracker orchestration behind the daemon so VM specs become persistent objects, and add repo entrypoints for building, installing, and documenting the new flow while still delegating rootfs customization to the existing shell tooling.\n\nHarden the control plane around real usage by reclaiming Firecracker API sockets for the user, restarting stale daemons after rebuilds, and returning the correct `vm.create` payload so the CLI and TUI creation flow work reliably.\n\nValidation: `go test ./...`, `make build`, and a host-side smoke test with `./banger vm create --name codex-smoke`.
2026-03-16 12:52:54 -03:00

5.9 KiB

banger

Minimal Firecracker launcher.

Requirements

  • Linux host with KVM (/dev/kvm access)
  • sudo, ip, curl, ssh, jq
  • dmsetup, losetup, blockdev (device-mapper snapshot for rootfs)
  • e2cp, e2rm (writes hostname and resolv.conf into rootfs snapshot)

Files

  • firecracker: Firecracker binary
  • wtf/root/boot/vmlinux-6.8.0-94-generic: guest kernel
  • wtf/root/boot/initrd.img-6.8.0-94-generic: guest initrd
  • wtf/root/lib/modules/6.8.0-94-generic/: guest kernel modules
  • rootfs.ext4: guest root filesystem (base image if present)
  • rootfs-docker.ext4: docker-ready guest rootfs (built via make-rootfs.sh)
  • packages.apt: apt packages baked into rebuilt guest images
  • id_ed25519: SSH key for root
  • mapdns: local DNS mapping CLI used to publish <vm-name>.vm → guest IP records

Run

./run.sh

Experimental Go Control Plane

There is now an XDG-based Go daemon + CLI prototype alongside the shell scripts. It keeps persistent VM/image state in SQLite under your XDG state directory and talks over a Unix socket under your XDG runtime directory.

Build it with:

make build

Or directly with Go:

go build -o ./banger ./cmd/banger
go build -o ./bangerd ./cmd/bangerd

Basic usage:

./banger daemon status
./banger tui
./banger vm list
./banger vm create --name calm-otter --disk-size 16G
./banger vm set calm-otter --memory 2048 --vcpu 4
./banger image list

Notes:

  • banger auto-starts the per-user daemon when needed.
  • banger tui launches a terminal UI for browsing, creating, editing, and operating VMs.
  • VM configs are persistent by default.
  • RAM, vCPU, and work-disk size edits are stopped-only.
  • The Go image build path currently delegates guest customization to customize.sh.

Run Options

./run.sh --name calm-otter --vcpu 4 --ram 2048 --overlay-size 12G
  • --name: must be unique and match [a-z0-9][a-z0-9-]{0,63}.
  • --vcpu: defaults to 2, max 16.
  • --ram: MiB, defaults to 1024, max 32768.
  • --overlay-size: writable dm-snapshot size for VM changes under /, including /root and /var (default: 8G).
  • --rootfs: path to the rootfs image (default: ./rootfs-docker.ext4).
  • --kernel: path to the kernel image (default: ./wtf/root/boot/vmlinux-6.8.0-94-generic).
  • --initrd: path to the initrd image (default: ./wtf/root/boot/initrd.img-6.8.0-94-generic).

Storage Layout

  • rootfs.ext4 is used as the read-only origin for a per-VM device-mapper snapshot mounted as /.
  • Each VM gets one sparse writable overlay file (cow.ext4) that stores its changes on top of the shared base image.
  • /root and /var live inside that per-VM overlay, so VMs can install packages without copying separate disks per VM.
  • run.sh masks stale home.mount and var.mount units at boot so older images with /dev/vdb and /dev/vdc entries in /etc/fstab still boot.
  • /run and /tmp should be tmpfs via /etc/fstab.

SSH

ssh -i "./id_ed25519" root@<guest_ip>

Shortcut:

./ssh.sh <vm-name-or-ip>

VM DNS

  • Spawned VMs register <vm-name>.vm → guest IP through mapdns set.
  • VM teardown removes the mapping through mapdns rm.
  • mapdns writes to /home/thales/.local/share/mapdns/records.json.

Internet Access

VMs do not get internet access by default. You must enable forwarding and NAT:

./nat.sh up <id-or-name-prefix>

This enables net.ipv4.ip_forward=1 and installs per-VM NAT rules for the VM's guest IP and TAP device. To remove rules:

./nat.sh down <id-or-name-prefix>

Check status with:

./nat.sh status <id-or-name-prefix>

Shutdown

reboot

Customize Rootfs (Docker + Kernel Modules)

Use customize.sh to build a writable rootfs with Docker and kernel modules preloaded so Docker works out of the box. Pass the base rootfs as a positional argument; the output defaults to docker-<base filename> in the same directory unless you pass --out.

Base guest packages come from ./packages.apt. Edit that file to bake tools like vim and tmux into rebuilt images.

./customize.sh ./rootfs.ext4 --size 6G --docker

Options:

  • --size: optional size for the output image.
  • --kernel: kernel path (default: ./wtf/root/boot/vmlinux-6.8.0-94-generic).
  • --initrd: initrd path (default: ./wtf/root/boot/initrd.img-6.8.0-94-generic).
  • --modules: kernel modules directory (default: ./wtf/root/lib/modules/6.8.0-94-generic).
  • --docker: install Docker packages into the image.
  • --out: output rootfs path (default: docker-<base filename>).

After boot, enable NAT and validate Docker:

./nat.sh up <id-or-name-prefix>
ssh -i "./id_ed25519" root@<guest_ip> "systemctl enable --now docker"
ssh -i "./id_ed25519" root@<guest_ip> "docker run --rm hello-world"

Build Rootfs On Demand

run.sh defaults to ./rootfs-docker.ext4. If it is missing, run.sh will invoke make-rootfs.sh to build it.

./make-rootfs.sh

make-rootfs.sh chooses the first available base image:

  • ./rootfs.ext4

If ./packages.apt changes after rootfs-docker.ext4 is built, run.sh will warn and keep using the existing image. make-rootfs.sh will also warn and exit without rebuilding while the image already exists.

To rebuild after package changes:

rm -f ./rootfs-docker.ext4 ./rootfs-docker.ext4.packages.sha256
./make-rootfs.sh

Interactive Customization

To create a writable copy and customize it manually over SSH (no automatic package/config changes), use:

./interactive.sh ./rootfs-docker.ext4

You can override the output path:

./interactive.sh ./rootfs-docker.ext4 --out ./my-rootfs.ext4

VM Info File

Each VM writes:

  • state/vms/<id>/vm.json: local metadata under .meta plus the raw Firecracker config under .config.

Log Notes

  • PCI: Fatal: No config space access function found and MissingAddressRange lines are expected with pci=off in run.sh.
  • SELinux: Could not open policy file ... is expected in the minimal rootfs.