One-command development sandboxes on Firecracker microVMs. https://git.thaloco.com/thaloco/banger/
Find a file
Thales Maciel b5c13e3938
Remove opencode package + vm acp command (dead code)
The `internal/opencode` package and the `opencodeCapability` that
consumed it were hard-wired to wait for opencode on guest port 4096
when an image shipped an initrd. After the prune commits (void /
alpine / customize.sh / image build all removed), nothing banger
produces today carries an initrd, so the capability's wait path was
unreachable: every startup short-circuited to the "direct-boot, skip
opencode" branch.

Same logic for `banger vm acp`: it SSHes to `opencode acp --cwd
<path>`, a binary the golden image no longer ships. Users who run
their own image with opencode can still invoke
`ssh vm -- opencode acp --cwd /root/repo` directly — no banger
scaffolding required.

Removed:
- internal/opencode/ (whole package, 255 LOC incl. tests)
- internal/daemon/opencode.go (opencodeCapability)
- cli `vm acp` command + its helpers (runVMACP, sshACPCommandArgs,
  vmACPRemoteCommand) + their tests
- The opencodeCapability{} entry in registeredCapabilities() plus
  the test that pinned its presence
- `wait_opencode` progress-stage label from the vm-create renderer
- Stale mentions in daemon/doc.go, README, and webui test fixtures

~480 lines gone, 12 added. `banger/internal` is now 25 packages
instead of 26.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:54:37 -03:00
cmd cli: restrict ExitCodeError unwrap to the CLI's own type 2026-04-17 15:37:47 -03:00
configs Generic kernel + init= boot path for OCI-pulled images 2026-04-16 20:12:56 -03:00
docs Remove image build --from-image; doctor treats catalog images as OK 2026-04-18 15:54:29 -03:00
images/golden Prune legacy void/alpine + customize.sh flows 2026-04-18 15:39:53 -03:00
internal Remove opencode package + vm acp command (dead code) 2026-04-18 16:54:37 -03:00
scripts Prune legacy void/alpine + customize.sh flows 2026-04-18 15:39:53 -03:00
.gitignore Untrack todos scratch file; ignore it 2026-04-18 15:40:46 -03:00
AGENTS.md Remove image build --from-image; doctor treats catalog images as OK 2026-04-18 15:54:29 -03:00
go.mod Phase 1: imagepull package — pull, flatten, ext4 2026-04-16 17:22:13 -03:00
go.sum Phase 1: imagepull package — pull, flatten, ext4 2026-04-16 17:22:13 -03:00
LICENSE Add LICENSE, update .gitignore, add security note to README 2026-04-14 16:54:33 -03:00
Makefile Prune legacy void/alpine + customize.sh flows 2026-04-18 15:39:53 -03:00
README.md Remove opencode package + vm acp command (dead code) 2026-04-18 16:54:37 -03:00

banger

One-command development sandboxes on Firecracker microVMs.

Quick start

make install
banger vm run --name sandbox

banger vm run auto-pulls the default golden image (Debian bookworm with systemd, sshd, Docker CE, git, jq, mise, and the usual dev tools) and kernel from the embedded catalog if they aren't already local, creates a VM, starts it, and drops you into an interactive ssh session. First run takes a couple minutes (bundle download); subsequent vm runs are seconds.

Requirements

  • Linux with /dev/kvm
  • sudo
  • Firecracker on PATH, or firecracker_bin set in config
  • host tools checked by banger doctor

Build + install

make install

Installs:

  • banger (CLI)
  • bangerd (daemon, auto-starts on first CLI call)
  • banger-vsock-agent (companion, under $PREFIX/lib/banger/)

vm run

One command, three modes:

banger vm run                          # bare sandbox — drops into ssh
banger vm run ./repo                   # workspace at /root/repo — drops into ssh
banger vm run ./repo -- make test      # workspace + run command, exit with its status
banger vm run --rm -- script.sh        # ephemeral: VM is deleted on exit
  • Bare mode gives you a clean shell.
  • Workspace mode (with a path) copies the repo's tracked + untracked non-ignored files into /root/repo and kicks off a best-effort mise tooling bootstrap from the repo's .mise.toml / .tool-versions. Log: /root/.cache/banger/vm-run-tooling-<repo>.log.
  • Command mode (-- <cmd>) runs the command in the guest; exit code propagates through banger.

Disconnecting from an interactive session leaves the VM running. Use vm stop / vm delete to clean up — or pass --rm so the VM auto-deletes once the session / command exits.

--branch and --from apply only to workspace mode.

--rm delete is skipped when the initial ssh wait times out, so a wedged sshd leaves the VM alive for banger vm logs inspection.

Image catalog

banger image pull <name> resolves <name> in the embedded catalog and fetches a pre-built bundle (rootfs.ext4 + manifest, tar+zstd). The kernel referenced by the manifest auto-pulls too. vm run calls this for you on demand.

Today's catalog:

Name Distro Kernel
debian-bookworm Debian 12 slim + sshd + docker + dev tools generic-6.12

The catalog ships embedded in the banger binary. See docs/image-catalog.md for maintenance.

Power-user flows

Skip this section if vm run is enough.

vm create — low-level primitive

For scripting or --no-start provisioning:

banger vm create --image debian-bookworm --name testbox --no-start
banger vm start testbox
banger vm ssh testbox
banger vm stop testbox

image pull <oci-ref> — arbitrary container images

For images outside the catalog, pull from any OCI registry:

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

Layers are flattened, ownership is fixed, banger's guest agents are injected, and a first-boot service installs openssh-server via the guest's package manager. See docs/oci-import.md for supported distros and caveats.

image register — existing host-side stack

banger image register --name base \
  --rootfs /abs/path/rootfs.ext4 \
  --kernel-ref generic-6.12

For custom images, write a Dockerfile and either publish to the catalog (see docs/image-catalog.md) or pull it via the OCI path.

Workspace + session primitives

Long-lived guest commands managed by the daemon, attachable over a local Unix socket bridge:

banger vm workspace prepare <vm> ./other-repo --guest-path /root/repo
banger vm session start <vm> --name planner --cwd /root/repo --stdin-mode pipe -- pi --mode rpc
banger vm session attach <vm> planner
banger vm session logs <vm> planner --stream stderr
banger vm session stop <vm> planner

Config

Config lives at ~/.config/banger/config.toml. All keys optional.

Commonly set:

  • default_image_name — image to use when --image is omitted (defaults to debian-bookworm, auto-pulled from the catalog if not local).
  • ssh_key_path — host SSH key; if unset banger creates ~/.config/banger/ssh/id_ed25519.
  • firecracker_bin — override the auto-resolved PATH lookup.
  • web_listen_addr — experimental web UI (default 127.0.0.1:7777; set to "" to disable).
  • Network: bridge_name, bridge_ip, cidr, tap_pool_size, default_dns.

Full key list in internal/config/config.go.

File sync

Host → guest file/directory copies, declared per-user in ~/.config/banger/config.toml:

[[file_sync]]
host = "~/.aws"          # whole directory, recursive
guest = "~/.aws"

[[file_sync]]
host = "~/.config/gh/hosts.yml"
guest = "~/.config/gh/hosts.yml"

[[file_sync]]
host = "~/bin/my-script"
guest = "~/bin/my-script"
mode = "0755"            # optional; defaults to 0600 for files

Runs at vm create time. Each entry copies hostguest onto the VM's work disk (mounted at /root in the guest). Guest paths must live under ~/ or /root/.... Host-side changes take effect after the next vm create. Missing host paths are a soft skip with a warning in the daemon log.

Default is no entries — add the ones you want.

Web UI (experimental)

bangerd serves a local web UI at http://127.0.0.1:7777 by default. Convenient for local observability, not a stable interface. Do not expose the listen address to a shared network.

Security

Guest VMs are single-user development sandboxes, not multi-tenant servers. Every provisioned image is configured with:

PermitRootLogin yes
StrictModes no

The host SSH key is the only authentication mechanism, no password auth is enabled, and VMs are reachable only through the host bridge network (172.16.0.0/24 by default). Do not expose the bridge interface or guest IPs to an untrusted network.

Notes

  • Managed image delete removes the daemon-owned artifact dir.
  • Layer blob cache for OCI pulls lives under ~/.cache/banger/oci/.
  • Image bundle cache doesn't exist — bundles are extracted directly into the image store; re-pulls download fresh.