# mvirt 0.14.17

migrations: no
wire: unchanged
rev: 67d3270ae69f90ae0e4a4df105f91a69deb8c3a1

v0.14.17 — an exported manifest now rebuilds the platform it came from

0.14.16 gave the control plane an export. This release makes what comes out of it
sufficient: every secret the platform holds, and the three configuration surfaces the
document could not previously express. A rebuild seeded from an export no longer needs
a handful of REST calls typed in the middle of it.

What changed

- **The export carries every sealed secret.** `?includeSecrets=true` emits whatever the
  platform holds, however it was set — manifest, admin API or setup wizard. The
  provenance gate it used to consult is gone, along with the `<field>Src` /
  `<field>SealedFp` markers and the currency proof they needed. That gate existed
  because a manifest could once name a host path the control plane read as root and
  sealed; 0.14.16 deleted the reference forms outright, so every sealed secret is now an
  operator-supplied literal and the recorded source carried no information. It only ever
  decided usefulness: document-seeded platforms carried everything, wizard-configured
  ones carried nothing.
- **`ipPrefixes[].externalRoute`** (ADR-0067) — both providers. `webhook` for a
  noez.de GRE-delivered block (webhookUrl, greRemote, greTransferCidr), and
  `hetzner-failover` for a Robot subnet (failoverNet). The webhook URL embeds the
  service auth key, so it is treated as a secret. The route itself is ensure-only, but a
  missing credential fills once: a rebuild seeded from a secret-free export can still
  seal the URL a later document supplies, without undoing an incident-time GRE
  correction.
- **`settings.failover`** — the account-level Hetzner Robot webservice credential. One
  user covers every Hetzner-routed net, which is why it is a setting rather than
  per-prefix. `robotUser` identifies rather than authenticates and stays readable; the
  password is sealed.
- **`settings.objectStorage`** — the settings singleton, above all the gateway endpoint.
  Its absence was the one gap whose cost was silent: without it the ADR-0058
  backup/template repository is never provisioned, so a rebuilt platform takes no
  backups and reports nothing wrong. The placement triple is seed-only and now bounded
  (pgCount 1..=4096, replicationFactor 1..=6), both derived from what the placement code
  assumes — a wrong value there is unrecoverable through any supported surface.

Two fixes worth naming, because both were silent

- The seed-only gate on the object-storage placement keyed on the singleton's mtime,
  which any settings write bumps. Setting the gateway endpoint first — the order a
  reinstall follows — locked pgCount against the seed without saying so.
- The sweep of retired state markers left behind `paas.dnsApiKeyHash`, the one that held
  an unkeyed blake3 of a live DNS credential in a row every voter replicates and every
  backup dumps. It is swept now.

Upgrading

Nothing to do. No schema change, so no store migration. A platform upgraded from 0.14.15
or earlier may hold a secret that was sealed from a file back when the manifest could
name one; on such a platform `?includeSecrets=true` would emit it. Both permissions the
flag requires (`platform.backup` and `platform.reveal`) are admin-grade and audited.


## 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 adds no migrations, so the control-plane host rolls back the same way
a node does: boot the previous NixOS generation.
