Fix the Go control plane NAT path now that runtime state lives in the daemon instead of the old repo-local vm.json files. Add a daemon-native NAT helper that derives uplink, guest IP, and TAP rules directly from VMRecord, applies the existing iptables/sysctl behavior idempotently, and removes the broken nat.sh handoff from vm.go. Cover uplink parsing and rule generation with unit tests. Validated with go test ./... and make build; a live verify.sh --nat run installed host rules but stopped on the same guest SSH-readiness issue seen in the plain smoke test on this host. |
||
|---|---|---|
| cmd | ||
| internal | ||
| wtf/root | ||
| .gitignore | ||
| AGENTS.md | ||
| customize.sh | ||
| dns.sh | ||
| firecracker | ||
| firecracker-api.yaml | ||
| go.mod | ||
| go.sum | ||
| id_ed25519 | ||
| interactive.sh | ||
| kill.sh | ||
| list.sh | ||
| logs.sh | ||
| make-rootfs.sh | ||
| Makefile | ||
| namegen | ||
| nat.sh | ||
| packages.apt | ||
| packages.sh | ||
| ps.sh | ||
| README.md | ||
| restore.sh | ||
| rm.sh | ||
| run.sh | ||
| ssh.sh | ||
| stop.sh | ||
| verify.sh | ||
banger
Minimal Firecracker launcher.
Requirements
- Linux host with KVM (
/dev/kvmaccess) sudo,ip,curl,ssh,jqdmsetup,losetup,blockdev(device-mapper snapshot for rootfs)e2cp,e2rm(writes hostname and resolv.conf into rootfs snapshot)
Files
firecracker: Firecracker binarywtf/root/boot/vmlinux-6.8.0-94-generic: guest kernelwtf/root/boot/initrd.img-6.8.0-94-generic: guest initrdwtf/root/lib/modules/6.8.0-94-generic/: guest kernel modulesrootfs.ext4: guest root filesystem (base image if present)rootfs-docker.ext4: docker-ready guest rootfs (built viamake-rootfs.sh)packages.apt: apt packages baked into rebuilt guest imagesid_ed25519: SSH key forrootmapdns: 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:
bangerauto-starts the per-user daemon when needed.banger tuilaunches 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/rootand/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.ext4is 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. /rootand/varlive inside that per-VM overlay, so VMs can install packages without copying separate disks per VM.run.shmasks stalehome.mountandvar.mountunits at boot so older images with/dev/vdband/dev/vdcentries in/etc/fstabstill boot./runand/tmpshould 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 throughmapdns set. - VM teardown removes the mapping through
mapdns rm. mapdnswrites 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.metaplus the raw Firecracker config under.config.
Log Notes
PCI: Fatal: No config space access function foundandMissingAddressRangelines are expected withpci=offinrun.sh.SELinux: Could not open policy file ...is expected in the minimal rootfs.