# Changelog

All notable changes to banger are documented here. The format is based
on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

The version line printed by `banger version` is the canonical reference
for what's installed; this file is the canonical reference for what
changed between versions.

## [Unreleased]

## [v0.1.10] - 2026-05-03

### Added

- README now includes an animated demo GIF showing the typical
  sandbox lifecycle (`vm run`, host-side `ssh demo.vm`, stop/start
  with file persistence, `vm exec`, `curl http://demo.vm`). The
  recording script lives at `assets/demo.tape` and is rendered with
  [VHS](https://github.com/charmbracelet/vhs).

## [v0.1.9] - 2026-05-01

### Fixed

- `vm exec` no longer falls back to `cd /root/repo` on VMs that have
  no recorded workspace. Previously, running `vm exec` against a plain
  VM (one that never had `vm workspace prepare` / `vm run ./repo`)
  blew up with `cd: /root/repo: No such file or directory` — surfaced
  via the login shell's mise activate hook because `bash -lc` sources
  profile.d before the explicit cd. Now the auto-cd only fires when
  the user passes `--guest-path` or the VM actually has a workspace
  recorded; otherwise the command runs from root's home. Mise wrapping
  is unchanged — without a `.mise.toml` it's a no-op.

### Changed

- `vm exec --guest-path` default in `--help` now reads "from last
  workspace prepare; otherwise root's home" (was "or /root/repo").
  Anyone who relied on the implicit `/root/repo` default for a VM that
  has a repo there but no workspace record must now pass
  `--guest-path /root/repo` explicitly.

### Notes

- Internal: smoke-test harness ported from `scripts/smoke.sh` to a
  Go test suite under `internal/smoketest`. `make smoke` is unchanged
  for maintainers; no user-visible effect.

## [v0.1.8] - 2026-05-01

### Fixed

- `<vm>.vm` resolution from the host (NSS path: curl, ssh hostname,
  etc.) now works on systemd-resolved hosts. The root helper's
  `validateResolverAddr` was rejecting the `host:port` form
  (`127.0.0.1:42069`) that banger constructs to point resolved at the
  in-process DNS server, so the auto-wire silently failed at every
  daemon startup. `dig @127.0.0.1` worked because that bypasses NSS;
  any tool going through glibc's resolver chain didn't.
- Validator now accepts both bare IPs and `IP:port` (matching what
  `resolvectl dns` itself accepts) with new test coverage for the
  port'd form.

### Notes

- Existing v0.1.x installs that already booted with the broken
  validator have stale per-link resolved state. After updating to
  v0.1.8, run `sudo banger system restart` once to re-trigger the
  auto-wire, or restart the host. systemd-resolved restarts also
  wipe per-link state — banger restores it on its own daemon
  startup but won't re-run for an already-running daemon.

## [v0.1.7] - 2026-05-01

### Added

- `vm run -d` / `--detach` creates the VM, runs workspace prep + tooling
  bootstrap, then exits without attaching to ssh. Reconnect later with
  `banger vm ssh <name>`. The combos `-d --rm` and `-d -- <cmd>` are
  rejected before VM creation.
- `vm run --no-bootstrap` skips the mise tooling install entirely; useful
  when a workspace has a `.mise.toml` you don't want banger to act on.
- `banger doctor --verbose` / `-v` prints every check with details.
  Without it, doctor's default output now collapses (see Changed).

### Changed

- **`vm run` refuses early when bootstrap can't succeed.** Previously, a
  workspace containing `.mise.toml` or `.tool-versions` without `--nat`
  set silently failed the bootstrap into a log file and dropped you into
  ssh with tools missing. It now refuses before VM creation with
  `tooling bootstrap requires --nat (or pass --no-bootstrap to skip)`.
  Existing scripts that relied on the silent-failure path will need to
  add `--nat` or `--no-bootstrap`.
- **`banger doctor` default output is now compact.** A healthy host
  collapses to a single line (`all N checks passed`); failing or warning
  checks print only the affected entries plus a summary footer
  (`N passed, M warnings, K failures`). Pass `--verbose` for the full
  per-check output. Anything parsing the previous always-verbose output
  needs to switch to `doctor --verbose`.

### Fixed

- The detached bootstrap path runs synchronously (foreground, tee'd to
  the existing log file) so the CLI only returns once installs finish.
  Interactive mode keeps today's nohup'd background behaviour so the ssh
  session starts promptly.

## [v0.1.6] - 2026-04-29

### Fixed

- v0.1.4's "running VMs survive daemon restart" fix was incomplete:
  the binary-level reconcile path was correct, but `/run/banger` (the
  daemon's runtime dir) was being wiped on every daemon stop because
  systemd defaults to `RuntimeDirectoryPreserve=no`. The api-sock
  symlinks the helper had created for live VMs vanished with it,
  and `findByJailerPidfile` couldn't resolve them to find the chroot
  + pidfile. v0.1.6 sets `RuntimeDirectoryPreserve=yes` on both
  unit templates so the symlinks (and helper RPC sock) survive
  the restart window. Live-verified: FC PID and guest boot_id both
  unchanged across a full helper+daemon restart cycle with a VM
  running.
- v0.1.4's CHANGELOG correction stands: existing v0.1.x installs
  (where x < 6) need a one-time `sudo banger system install` after
  updating to v0.1.6 to pick up both the new `KillMode=process` and
  the new `RuntimeDirectoryPreserve=yes` directives. `banger update`
  swaps binaries, not unit files.

## [v0.1.5] - 2026-04-29

No functional changes. Verification release for v0.1.4: the previous
release shipped the running-VMs-survive-update fix, but updating
*to* v0.1.4 from v0.1.3 used v0.1.3's buggy driver, so the fix
couldn't be verified live in that direction. v0.1.5 exists so a
host on v0.1.4 can update to it and observe a running VM survive
end-to-end with v0.1.4 in the driver seat.

## [v0.1.4] - 2026-04-29

### Fixed

- Daemon restarts no longer kill running VMs. Two changes together:
  - The `bangerd-root.service` and `bangerd.service` unit templates
    now set `KillMode=process`. The default (`control-group`) sent
    SIGKILL to every process in the unit's cgroup on stop/restart,
    including the jailer-spawned firecracker children — fork/exec
    doesn't escape a systemd cgroup. With `KillMode=process` only
    the unit's main PID is signalled; firecracker children survive.
  - `fcproc.FindPID` now also looks up jailer'd firecracker
    processes via the pidfile jailer writes at
    `<chroot>/firecracker.pid` (sibling of the api-sock target).
    Previously the only lookup path was `pgrep -n -f <api-sock>`,
    which can't see jailer'd processes because their cmdline only
    carries the chroot-relative `--api-sock /firecracker.socket`.
    Reconcile after a daemon restart now correctly re-attaches to
    surviving guests instead of mistaking them for stale and tearing
    down their dm-snapshot.

### Notes

- v0.1.0's CHANGELOG line "daemon restarts do not interrupt running
  guests" was wrong: it was true at the systemd cgroup layer in
  theory but the default `KillMode` defeated it, and even with
  `KillMode=process` the daemon's reconcile would mistake
  surviving FCs for stale and tear them down. v0.1.4 is the version
  where this actually works end-to-end.
- Updating from v0.1.0–v0.1.3 to v0.1.4 still kills running VMs
  because the *driver* of the update is the buggy older binary.
  Updates from v0.1.4 onward preserve running VMs across the
  helper+daemon restart that `banger update` performs.
- Existing v0.1.0–v0.1.3 installs that update to v0.1.4 do NOT
  automatically pick up the new unit files — `banger update` swaps
  binaries, not systemd units. Run `sudo banger system install` once
  on those hosts after updating to refresh the units. New v0.1.4+
  installs get the correct units from the start.

## [v0.1.3] - 2026-04-29

No functional changes. Verification release: v0.1.2 fixed
`banger update`'s install.toml handling, but the fix only takes
effect when v0.1.2 (or later) is the driver of an update. v0.1.3
exists so a host running v0.1.2 can update to it and confirm the
fix works end-to-end with the new code in the driver seat.

## [v0.1.2] - 2026-04-29

### Fixed

- `banger update` now writes the freshly-installed binary's commit
  and built_at fields to `/etc/banger/install.toml`, not the running
  CLI's. Previously install.toml's `version` was correct after an
  update but `commit` + `built_at` still pointed at the pre-update
  binary's identity, which made `banger doctor` raise a false-positive
  "CLI/install drift" warning on every update. Caught by the v0.1.0
  → v0.1.1 live update smoke-test.

## [v0.1.1] - 2026-04-29

### Added

- `install.sh` — one-command installer published at
  `https://releases.thaloco.com/banger/install.sh`. Runs as the
  invoking user, downloads + verifies the latest signed release with
  the embedded cosign public key, and re-execs `sudo` only for the
  actual system-install step. Pre-sudo summary explains in plain
  language why elevation is needed.
- `BANGER_INSTALL_NONINTERACTIVE=1` env var on `install.sh` for
  non-interactive use through `curl | bash` (CI, automated provisioning).

## [v0.1.0] - 2026-04-29

First public release. banger runs disposable development sandboxes as
Firecracker microVMs: each sandbox boots in a few seconds, gets its own
root filesystem and network, and exits on demand.

### Added

**Sandbox VMs**
- `banger vm run` boots a microVM, drops you into ssh, and tears it down
  on exit. Optional positional path ships a host repo into the guest;
  `-- cmd args` runs a command non-interactively and exits with its
  status.
- Long-lived VMs via `vm create` / `vm start` / `vm stop` /
  `vm restart` / `vm ssh` / `vm exec` / `vm logs` / `vm stats` /
  `vm ports` / `vm kill`. `vm list` and `ps` enumerate state;
  `vm prune` deletes every non-running VM.
- `vm workspace` ships a host repo into a guest and pulls diffs back.
- Per-VM cgroup-isolated firecracker process under jailer chroot;
  daemon restarts do not interrupt running guests.

**Images**
- `banger image pull <name>` pulls a curated rootfs+kernel bundle from
  the banger image catalog. `image pull <oci-ref>` pulls any OCI image.
- `image list` / `image show` / `image delete` / `image promote` /
  `image register` round out the lifecycle.
- `image cache` manages the OCI layer-blob cache.
- Concurrent pulls of the same image are coalesced; the first pull
  wins, the rest wait.

**Kernels**
- `banger kernel pull <name>` pulls a Firecracker-compatible kernel
  from the banger kernel catalog. `kernel list` / `kernel show` /
  `kernel rm` manage the local store.

**Host networking**
- Per-host bridge with NAT; per-VM tap device; deterministic IPv4
  assignment; iptables rules installed/removed with VM lifecycle.
- DNS routing: local resolver on `127.0.0.1:42069` answers queries
  for `<vm>.vm` so plain `ssh <vm>.vm` reaches the guest.
- `banger ssh-config` writes a one-time `~/.ssh/config` include so
  ssh, scp, and rsync resolve `<vm>.vm` from any terminal.

**System install**
- `sudo banger system install` installs an owner-mode daemon
  (`bangerd.service`) and a root-helper (`bangerd-root.service`) as
  systemd units. The owner daemon runs as the invoking user; only the
  root helper holds privilege, and only for a vetted set of operations.
- `system status` / `system restart` / `system uninstall` round out
  the lifecycle. `daemon` is a thin alias.
- `banger doctor` audits host readiness: architecture, CLI/install
  version drift, state store, host runtime, vm lifecycle prerequisites,
  vsock guest agent, vm defaults, ssh shortcut, /root work disk, DNS,
  NAT, firecracker binary version, systemd units, socket permissions,
  helper unit hardening directives.

**Self-update**
- `banger update` downloads, verifies, and installs newer releases
  from the public manifest. Flow: fetch manifest, refuse if any VM
  operation is in flight, download tarball + `SHA256SUMS` +
  `SHA256SUMS.sig`, verify the cosign signature against the embedded
  public key, verify the tarball hash, stage to a scratch dir, run
  `bangerd --check-migrations` against the staged binary, atomically
  swap the three banger binaries, restart the systemd units, run
  `banger doctor`, finalise the install record.
- Pre-restart abort and post-restart auto-rollback both restore the
  previous install on failure.
- `banger update --check` reports whether a newer release is
  available without applying it; `--to vX.Y.Z` pins a specific
  version; `--dry-run` prints the plan; `--force` skips the
  in-flight-op refusal.

**Trust model**
- Every release is cosign-signed. The public key is embedded in the
  banger binary at build time; the signed payload is `SHA256SUMS`,
  which in turn covers the release tarball. Verification uses the
  Go standard library (`crypto/ecdsa.VerifyASN1`); cosign is needed
  only for *signing*, not for verification.
- The release manifest URL is hardcoded into the binary so a
  compromised daemon config cannot redirect the updater to a different
  bucket.

**CLI surface**
- Top-level: `vm`, `ps`, `image`, `kernel`, `ssh-config`, `system`,
  `daemon`, `doctor`, `update`, `version`, `completion`.
- `banger version` reports the version, commit SHA, and build
  timestamp baked in via ldflags at release-build time.

### Compatibility

- The host-side and guest-side vsock agent protocol is informally
  stable across **patch** versions (v0.1.x). Minor-version bumps
  (v0.2.x) may change it; existing VMs created against an older
  minor will need to be re-pulled. `banger doctor` warns when a
  running VM's agent is older than the daemon expects but does not
  block lifecycle operations.
- The on-disk store schema is forward-only. Downgrading the binary
  against a database written by a newer binary is unsupported; the
  updater detects this via `bangerd --check-migrations` and refuses
  the swap rather than starting up against an incompatible store.
- Linux only. amd64 only. KVM required.

[Unreleased]: https://git.thaloco.com/thaloco/banger/compare/v0.1.10...HEAD
[v0.1.10]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.10
[v0.1.9]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.9
[v0.1.8]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.8
[v0.1.7]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.7
[v0.1.6]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.6
[v0.1.5]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.5
[v0.1.4]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.4
[v0.1.3]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.3
[v0.1.2]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.2
[v0.1.1]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.1
[v0.1.0]: https://git.thaloco.com/thaloco/banger/releases/tag/v0.1.0
