Without base_commit, export diffs against the current guest HEAD. If the worker ran git commit inside the VM, HEAD advanced and the diff came back empty — committed work was silently lost. With base_commit set to the head_commit from workspace.prepare, the diff uses that fixed point instead. After git add -A the index holds the full working state, so git diff --cached <base_commit> captures everything: committed deltas (HEAD moved past base) and any uncommitted changes on top, in one patch, applied with the same git apply flow. - WorkspaceExportParams gains base_commit - WorkspaceExportResult echoes back the ref actually used - CLI gains --base-commit flag - Tests assert scripts use the caller-supplied ref and that omitting it falls back to HEAD |
||
|---|---|---|
| cmd | ||
| docs/reference | ||
| examples | ||
| internal | ||
| scripts | ||
| .gitignore | ||
| AGENTS.md | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
banger
banger manages Firecracker development VMs with a local daemon, managed image artifacts, and a localhost web UI.
Requirements
- Linux with
/dev/kvm sudo- Firecracker installed on
PATH, orfirecracker_binset in config - The usual host tools checked by
./build/bin/banger doctor
banger now owns complete managed image sets. A managed image includes:
rootfs- optional
work-seed kernel- optional
initrd - optional
modules
There is no runtime bundle anymore.
Build
make build
This writes:
./build/bin/banger./build/bin/bangerd./build/bin/banger-vsock-agent
Install
make install
That installs:
bangerbangerd- the
banger-vsock-agentcompanion helper under../lib/banger/
Config
Config lives at ~/.config/banger/config.toml.
Supported keys:
log_levelweb_listen_addrfirecracker_binssh_key_pathdefault_image_nameauto_stop_stale_afterstats_poll_intervalmetrics_poll_intervalbridge_namebridge_ipcidrtap_pool_sizedefault_dns
If ssh_key_path is unset, banger creates and uses:
~/.config/banger/ssh/id_ed25519
default_image_name now only means “use this registered image when vm create omits --image”. The daemon does not auto-register images from host paths.
Core Workflow
Check the host:
./build/bin/banger doctor
Register an existing host-side image stack:
./build/bin/banger image register \
--name base \
--rootfs /abs/path/rootfs.ext4 \
--kernel /abs/path/vmlinux \
--initrd /abs/path/initrd.img \
--modules /abs/path/modules
Build a managed image from an existing registered image:
./build/bin/banger image build \
--name devbox \
--from-image base \
--docker
Promote an unmanaged image into daemon-owned managed artifacts:
./build/bin/banger image promote base
Create and use a VM:
./build/bin/banger vm create --image devbox --name testbox
./build/bin/banger vm ssh testbox
./build/bin/banger vm stop testbox
vm create stays synchronous by default, but on a TTY it now shows live progress until the VM is fully ready.
Start a repo-backed VM session:
./build/bin/banger vm run
./build/bin/banger vm run ../some-repo --branch feature/alpine --from HEAD
vm run resolves the enclosing git repository, creates a VM, copies a git checkout plus current tracked and untracked non-ignored files into /root/repo, starts a best-effort guest tooling bootstrap that only uses mise, prints next-step commands, and exits. It does not auto-attach opencode anymore. The bootstrap runs asynchronously and logs its output inside the guest.
After vm run, use one of:
./build/bin/banger vm ssh <vm-name>
opencode attach http://<vm-name>.vm:4096 --dir /root/repo
./build/bin/banger vm acp <vm-name>
./build/bin/banger vm ssh <vm-name> -- "cd /root/repo && claude"
./build/bin/banger vm ssh <vm-name> -- "cd /root/repo && pi"
For ACP-aware host tools, ./build/bin/banger vm acp <vm-name> bridges stdio to guest opencode acp over SSH. It uses /root/repo when that checkout exists, otherwise /root, and --cwd lets you override the guest working directory explicitly.
If you want reusable orchestration primitives instead of the vm run convenience flow, use the daemon-backed workspace and session commands directly:
./build/bin/banger vm workspace prepare <vm-name>
./build/bin/banger vm workspace prepare <vm-name> ../other-repo --guest-path /root/repo --readonly
./build/bin/banger vm session start <vm-name> --name planner --cwd /root/repo --stdin-mode pipe -- pi --mode rpc --no-session
./build/bin/banger vm session list <vm-name>
./build/bin/banger vm session attach <vm-name> planner
./build/bin/banger vm session logs <vm-name> planner --stream stderr
./build/bin/banger vm session stop <vm-name> planner
vm workspace prepare materializes a local git checkout into a running VM. The default guest path is /root/repo and the default mode is a shallow metadata copy plus tracked and untracked non-ignored overlay. Repositories with git submodules must use --mode full_copy; the metadata-based modes still reject them.
vm session start creates a daemon-managed long-lived guest command. The daemon preflights that the requested guest cwd exists and that the main command, plus any repeated --require-command entries, exist in guest PATH before launch. Use --stdin-mode pipe when you need live attach; otherwise use the default detached mode and inspect sessions with list, show, logs, stop, and kill.
vm session attach is currently exclusive and same-host only. The daemon exposes a local Unix socket bridge using stdio_mux_v1, so only one active attach is allowed at a time. Pipe-mode sessions keep enough guest-side state for the daemon to rebuild that bridge after a daemon restart.
Web UI
bangerd serves a local web UI by default at:
http://127.0.0.1:7777
See the effective URL with:
./build/bin/banger daemon status
Disable it with:
web_listen_addr = ""
Guest Services
Provisioned glibc-backed images include:
banger-vsock-agent- guest networking bootstrap
miseopencodeclaudepi- a default guest
opencodeservice on0.0.0.0:4096
Alpine currently remains opencode-only.
If these host auth files exist, banger syncs them into the guest on VM start:
~/.local/share/opencode/auth.json->/root/.local/share/opencode/auth.json~/.claude/.credentials.json->/root/.claude/.credentials.json~/.pi/agent/auth.json->/root/.pi/agent/auth.json
Changes on the host take effect after the VM is restarted. Session/history directories are not copied.
From the host:
./build/bin/banger vm ports testbox
opencode attach http://<guest-ip>:4096
Manual Helpers
The shell helpers are now explicit manual workflows under ./build/manual.
Rebuild a Debian-style manual rootfs:
make rootfs ARGS='--base-rootfs /abs/path/rootfs.ext4 --kernel /abs/path/vmlinux --initrd /abs/path/initrd.img --modules /abs/path/modules'
The output lands in:
./build/manual/rootfs-docker.ext4./build/manual/rootfs-docker.work-seed.ext4
Experimental Void Flow
Stage a Void kernel:
make void-kernel
Build the experimental Void rootfs:
make rootfs-void
Register it:
make void-register
That flow uses:
./build/manual/void-kernel/./build/manual/rootfs-void.ext4./build/manual/rootfs-void.work-seed.ext4
Experimental Alpine Flow
Stage an Alpine virt kernel:
make alpine-kernel
Build the experimental Alpine rootfs:
make rootfs-alpine
Register it:
make alpine-register
Create a VM from it:
./build/bin/banger vm create --image alpine --name alpine-dev
That flow uses:
./build/manual/alpine-kernel/./build/manual/rootfs-alpine.ext4./build/manual/rootfs-alpine.work-seed.ext4
The experimental Alpine flow stages a pinned Alpine release by default. Override
that pin with ALPINE_RELEASE=... when running the make alpine-kernel and
make rootfs-alpine helpers if you need a different patch release.
Alpine support currently applies to the explicit register-and-run flow above.
The generic banger image build --from-image ... path remains Debian/systemd-
oriented and should not be treated as an Alpine image builder.
Notes
- Firecracker is resolved from
PATHby default. - Managed image delete removes the daemon-owned artifact dir.
- The companion vsock helper is internal to the install/build layout, not a user-configured runtime path.