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>
6.3 KiB
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, orfirecracker_binset 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/repoand 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 throughbanger.
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
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--imageis omitted (defaults todebian-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-resolvedPATHlookup.web_listen_addr— experimental web UI (default127.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 = "~/.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.