banger/README.md
Thales Maciel b1fbf695ca
ssh-config: narrow the legacy-dir cleanup so it can't delete a user key
Bug: syncVMSSHClientConfig did os.RemoveAll on $ConfigDir/ssh every
daemon Open. The intent was to migrate off the pre-opt-in layout,
where banger used to write $ConfigDir/ssh/ssh_config. But a user who
sets ssh_key_path = "~/.config/banger/ssh/id_ed25519" in config.toml
has their key live exactly in that dir — and the scrub deletes it
along with every other file in the tree.

This is the same class of bug that cost the default key until
ebe6517 moved it to StateDir, but that fix was scoped to the default
path. A configured ssh_key_path pointed under the legacy dir still
dies.

Fix: replace os.RemoveAll with a narrow two-step cleanup:

 1. Skip the cleanup entirely when the configured ssh_key_path
    resolves under the legacy dir. A user who pointed banger at a
    key there must keep the enclosing directory.
 2. Otherwise, os.Remove the specific legacy file ($ConfigDir/ssh/
    ssh_config) and then os.Remove the directory. The second
    os.Remove fails with ENOTEMPTY if the dir still holds anything
    (e.g. a user-managed sibling file we don't own). Both errors
    are swallowed — this is best-effort migration, not a hard
    failure.

Tests pin all three paths: user key under legacy dir survives,
legacy dir empties and is removed when the user moved on, and a
user-managed sibling file in the legacy dir is preserved.

Also fix stale doc claims in README.md and AGENTS.md — both still
pointed at the old ~/.config/banger/ssh/id_ed25519 default, which
moved to ~/.local/state/banger/ssh/id_ed25519 in ebe6517.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:31:07 -03:00

241 lines
7.6 KiB
Markdown

# 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-<repo>.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** (`-- <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`, `--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>.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 <name>.vm` shortcut
`banger vm ssh <name>` 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 <name>` 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`.
- `firecracker_bin` — override the auto-resolved `PATH` lookup.
Full key list in `internal/config/config.go`.
### `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>.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.