# banger One-command development sandboxes on Firecracker microVMs. ## Quick start ```bash make install banger vm run --name sandbox ``` That's it. `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, creates a VM, starts it, and drops you into an interactive ssh session. First run takes a couple minutes (bundle download); subsequent `vm run`s are seconds. ## Requirements - **x86_64 / amd64 Linux** — arm64 is not supported today. The companion binaries, the published kernel catalog, and the OCI import path all assume `linux/amd64`. `banger doctor` surfaces this as a failing check on other architectures. - `/dev/kvm` - `sudo` - Firecracker on `PATH`, or `firecracker_bin` set in config - host tools checked by `banger doctor` ## Build + install ```bash make install ``` Installs `banger` (CLI), `bangerd` (daemon, auto-starts on first CLI call), and `banger-vsock-agent` (companion, under `$PREFIX/lib/banger/`). To remove the binaries (and stop the daemon): ```bash make uninstall ``` User data stays in place — the target prints the paths so you can `rm -rf` them if you want a full purge: - `~/.config/banger/` — config, managed SSH keys - `~/.local/state/banger/` — VM records, rootfs images, kernels, daemon DB/log - `~/.cache/banger/` — OCI layer cache ### Shell completion `banger` ships completion scripts for bash, zsh, fish, and powershell. Tab-completion covers subcommands, flags, and live resource names (VM, image, kernel) looked up from the daemon. With the daemon down, resource completion silently returns nothing — no file-completion fallback. ```bash # bash (system-wide) banger completion bash | sudo tee /etc/bash_completion.d/banger # zsh (user-local; ~/.zfunc must be on fpath) banger completion zsh > ~/.zfunc/_banger # fish banger completion fish > ~/.config/fish/completions/banger.fish ``` `banger completion --help` shows the shell-specific loading recipes. ## `vm run` One command, four common shapes: ```bash 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, exits with its status banger vm run --rm -- script.sh # ephemeral: VM is deleted on exit ``` - **Bare mode** gives you a clean shell. - **Workspace mode** (path given) copies the repo's git-tracked 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-.log`. Untracked files (including local `.env`, scratch notes, credentials that aren't gitignored) are skipped by default — pass `--include-untracked` to also ship them. Pass `--dry-run` to print the exact file list and exit without creating a VM. - **Command mode** (`-- `) 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`, `--from`, `--include-untracked`, and `--dry-run` apply only to workspace mode. `--rm` skips the delete when the initial ssh wait times out, so a wedged sshd leaves the VM alive for `banger vm logs` inspection. ## Hostnames: reaching `.vm` banger's daemon runs a DNS server for the `.vm` zone. With host-side DNS routing you can `curl http://sandbox.vm:3000` from anywhere on the host — no copy-pasting guest IPs. On systemd-resolved hosts this is auto-wired; everywhere else there's a short recipe. See [`docs/dns-routing.md`](docs/dns-routing.md). ### Optional: `ssh .vm` shortcut `banger vm ssh ` works out of the box. If you'd also like plain `ssh sandbox.vm` from any terminal (using banger's key + known_hosts), opt in: ```bash banger ssh-config --install # adds `Include ~/.config/banger/ssh_config` # to ~/.ssh/config in a marker-fenced block banger ssh-config --uninstall # reverse it banger ssh-config # show the include line to paste manually ``` banger never touches `~/.ssh/config` on its own — the daemon keeps its file fresh at `~/.config/banger/ssh_config`; whether and how it's pulled into your SSH config is up to you. ## Image catalog `banger image pull ` fetches a pre-built bundle from the embedded catalog. `vm run` calls this for you on demand. Today's catalog: | Name | What it is | |------|-----------| | `debian-bookworm` | Debian 12 slim + sshd + docker + dev tools | See [`docs/image-catalog.md`](docs/image-catalog.md) for the bundle format and how to publish a new entry. ## Config Config lives at `~/.config/banger/config.toml`. All keys optional. Most commonly set: - `default_image_name` — image used when `--image` is omitted (default `debian-bookworm`, auto-pulled from the catalog if not local). - `ssh_key_path` — host SSH key. If unset, banger creates `~/.local/state/banger/ssh/id_ed25519`. Accepts absolute paths or `~/`-anchored paths; `~/foo` expands against `$HOME`. Relative paths are rejected at config load. - `firecracker_bin` — override the auto-resolved `PATH` lookup. Full key list in `internal/config/config.go`. > **Migration note.** The auto-generated default moved from > `~/.config/banger/ssh/id_ed25519` to > `~/.local/state/banger/ssh/id_ed25519`. If you have the old path > hardcoded in `config.toml`, either keep it (banger preserves the > directory when `ssh_key_path` points inside it) or unset the key > and banger will manage the new default for you. The first time the > daemon starts against the new default, guest VMs need a fresh > workspace sync (`banger vm stop && start`, or `--rm` flows are > unaffected) so their `authorized_keys` pick up the new fingerprint. ### `vm_defaults` — sizing for new VMs Every `vm run` / `vm create` prints a `spec:` line up front showing the vCPU, RAM, and disk the VM will get. When the flags aren't set, those values come from: 1. `[vm_defaults]` in config (if present, wins). 2. Host-derived heuristics (roughly: `cpus/4` capped at 4, `ram/8` capped at 8 GiB, 8 GiB disk). 3. Built-in constants (floor). `banger doctor` prints the effective defaults with provenance. ```toml [vm_defaults] vcpu = 4 memory_mib = 4096 disk_size = "16G" ``` All keys optional — omit whichever you want banger to decide. ### `file_sync` — host → guest file copies ```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; default 0600 for files ``` Runs at `vm create` time. Each entry copies `host` → `guest` onto the VM's work disk (mounted at `/root` in the guest). Guest paths must live under `~/` or `/root/...`. Default is no entries — add the ones you want. ## Advanced The common path is `vm run`. Power-user flows (`vm create`, OCI pull for arbitrary images, `image register`, manual workspace prepare) are documented in [`docs/advanced.md`](docs/advanced.md). ## Security Guest VMs are single-user development sandboxes, not multi-tenant servers. Each guest's sshd is configured with: ``` PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication no AuthorizedKeysFile /root/.ssh/authorized_keys ``` The host SSH key is the only authentication mechanism. `StrictModes` is on (sshd's default); banger normalises `/root`, `/root/.ssh`, and `authorized_keys` perms at provisioning time so the default passes. 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. ## Further reading - [`docs/dns-routing.md`](docs/dns-routing.md) — resolving `.vm` hostnames from the host. - [`docs/image-catalog.md`](docs/image-catalog.md) — bundle format and publishing. - [`docs/kernel-catalog.md`](docs/kernel-catalog.md) — kernel bundles. - [`docs/oci-import.md`](docs/oci-import.md) — pulling arbitrary OCI images. - [`docs/advanced.md`](docs/advanced.md) — power-user flows.