Replace the three hardcoded host→guest credential syncs (opencode,
claude, pi) with a generic `[[file_sync]]` config list. Default is
empty — users opt in to exactly what they want synced, with no
surprise about which tools banger "supports".
```toml
[[file_sync]]
host = "~/.local/share/opencode/auth.json"
guest = "~/.local/share/opencode/auth.json"
[[file_sync]]
host = "~/.aws" # directories are copied recursively
guest = "~/.aws"
[[file_sync]]
host = "~/bin/my-script"
guest = "~/bin/my-script"
mode = "0755" # optional; default 0600 for files
```
Semantics:
- Host `~/...` expands against the host user's $HOME. Absolute host
paths are used as-is.
- Guest must live under `~/` or `/root/...` — banger's work disk is
mounted at /root in the guest, so that's the syncable namespace.
Anything outside is rejected at config load.
- Validation at config load: reject empty paths, relative paths,
`..` traversal, `~user/...`, malformed mode strings. Errors name
the offending entry index.
- Missing host paths are a soft skip with a warn log (existing
behaviour). Other errors (read, mkdir, install) abort VM create.
- File entries: `install -o 0 -g 0 -m <mode>` (default 0600).
- Directory entries: walked in Go; each source file is installed
with its own source permissions preserved. The entry's `mode` is
ignored for directories.
Removed (all dead after this):
- `ensureOpencodeAuthOnWorkDisk`, `ensureClaudeAuthOnWorkDisk`,
`ensurePiAuthOnWorkDisk`, the shared `ensureAuthFileOnWorkDisk`,
their `warn*Skipped` helpers, `resolveHost{Opencode,Claude,Pi}AuthPath`,
and the work-disk relative-path + default display-path constants.
- The capability hook registering the three syncs now calls the
generic `runFileSync` once.
Seven tests exercising the old codepath deleted; six new tests cover
the new runFileSync (no-op on empty config, file copy, custom mode,
missing-host-skip, overwrite, recursive directory). Config-layer
test adds happy-path parsing and a case-per-shape table of invalid
entries (empty, relative host, guest outside /root, '..' traversal,
`~user`, bad mode).
README updated: replaces the "Credential sync" section with a
"File sync" section showing the new config shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
210 lines
6.3 KiB
Markdown
210 lines
6.3 KiB
Markdown
# banger
|
|
|
|
One-command development sandboxes on Firecracker microVMs.
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
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 run`s 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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```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, 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`](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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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`](docs/oci-import.md)
|
|
for supported distros and caveats.
|
|
|
|
### `image register` — existing host-side stack
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
```
|
|
|
|
For ACP-aware host tooling: `banger vm acp <vm>` bridges stdio to
|
|
guest `opencode acp` over SSH.
|
|
|
|
## 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`:
|
|
|
|
```toml
|
|
[[file_sync]]
|
|
host = "~/.local/share/opencode/auth.json"
|
|
guest = "~/.local/share/opencode/auth.json"
|
|
|
|
[[file_sync]]
|
|
host = "~/.aws" # whole directory, recursive
|
|
guest = "~/.aws"
|
|
|
|
[[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 `host` → `guest` 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.
|