# mvirt 0.13.0

migrations: yes
wire: unchanged
rev: 2b4c59abe71ff8228b582a3b4693143b0f949f4b

v0.13.0 — provider object storage becomes tenant-facing, the Task primitive, and the reverse tunnel that heals itself

A MINOR by ADR-0069 D9's own grading: this tag registers five store migrations (0130–0134 — the
Task entity with its network and result documents, task secrets, the object-storage proxy flag), so
the upgrade moves the store and a rollback is the store-restore path, not the previous-generation
boot. The node↔cplane and storage wires are unchanged.

Object storage, end to end (ADR-0074/0075/0076)

- The backend registry grows its second driver: **Hetzner Object Storage**, driven entirely over
  the signed S3 API (Hetzner has no control API) — bucket lifecycle, orphan sweep, region-aware
  listings, empty-including-multiparts delete. Beside it Bunny, the M1 driver.
- The backend row gains the create-only **proxy** switch: defaulted on, refused off where the
  credential is account-scoped — a direct path would hand tenants the project-wide key.
- D5 held at the steel edge of the API: a proxied bucket's DTO names no supplier — no provider
  name, physical name, endpoint, region, capability note, or provider error text — and the
  credential reveal refuses with a pointer at the project's own S3 access keys.
- **The tenant data path runs through the platform** (D10/D11): the S3 proxy now lives inside the
  mvirt-s3 gateway. The cplane serves the proxy's complete catalog snapshot; the gateway polls it
  on a 15-second tick, swaps it atomically, and keeps the last good one on any failure. Tenant keys
  are project-scoped and authorise every proxied bucket of their project; signatures verify against
  the request path exactly as the tenant's SDK signed it (virtual-host requests included); the
  provider's per-source-IP budgets become per-key SlowDown pacing plus a fleet concurrency cap. The
  baked proof: the real gateway vs. a recording stub — the provider never sees a tenant, and the
  tenant never sees the provider's name.
- Storage deployment modes (ADR-0076): `settings.storage` with local-only volumes, the remote mode
  whose object store is a proxied provider backend, and the wizard's Storage step that walks an
  operator into the choice.

Tasks (ADR-0079/0080)

- **Platform-issued one-shot Tasks**: entity, REST + RBAC (issuer guard, result endpoint), console
  list/detail/result viewer, the in-guest result-document channel, secret injection via
  ContainerFile projection, and the first consumer — a template import is a Task now, run by the
  published importer image, with no node ever dereferencing a tenant URL.

Security lanes and reliability

- The audit's lane A/B landings: the session signing secret is per-installation rather than
  compiled into the binary; the reverse tunnel fails closed without its mTLS terminator; the
  cross-project IDORs in vm/nic/volume creates are shut; the route reflector scopes announcements
  to what each node owns; audit seams mark every mutating route; state-dir files are 0600/0700 from
  creation.
- The reverse-tunnel lifecycle is level-triggered end to end — leadership gate, stage deadlines,
  node-side service watchdog: a wedged channel re-dials itself on the next resync.
- The console's ADR-0077 shape lands: platform overview landing, flow-grouped admin rail with
  attention rollups, settings consolidation, and the workspace task split between default and
  compute node groups.
- ADR-0078 ships mvirt-smoke S0–S3: the platform proven from the user's side, first lane.


## Upgrading

Point your flake's `mvirt` input at this release and redeploy the control-plane
host first, then each node in turn:

```
nix run .#colmena -- apply boot --reboot --on <control-plane host>
nix run .#colmena -- apply boot --reboot --on <node>   # one at a time
```

**This release migrates the store.** Once the control plane has started, the
previous version will refuse to run against it — migrations are forward-only, so
rolling the control-plane host back means restoring a backup, with data loss back
to the backup point. The control plane writes a backup immediately before it
applies migrations; nodes roll back freely by booting the previous generation.
