# mvirt 0.14.16

migrations: no
wire: unchanged
rev: 703ea8c9299625eaa675ef43472e552d6416ebe6

v0.14.16 — the platform manifest carries its own secrets, and exports itself

A control plane can now hand you a manifest that reproduces it. `GET /v1/platform/manifest/export`
renders the live configuration as a seed document; with `?includeSecrets=true` it inlines the platform
secrets so the seed is complete. That closes the gap this release opened up while looking at it: a
platform configured through the console could not be reproduced at all, because the wizard's own stored
document was not readable back (`/v1/platform/manifest` was PUT-only), and the object-storage backend
and the AI assistant had no manifest representation whatsoever.

What changed

- `GET /v1/platform/manifest/export` — server-side render of the live configuration as a manifest.
  Requires the new `platform.backup`; `?includeSecrets=true` additionally requires the new
  `platform.reveal`, and is audited on every exit. Neither permission ends in `.read`, so an ADR-0065
  read-only token is refused below RBAC rather than by enumeration.
- Platform secrets may be inline values in the manifest: the PaaS DNS API key, the object-storage
  backend credential, and the assistant's API and web-search keys. `PUT /v1/platform/manifest` strips
  the values before persisting the document, because `Node::backup()` dumps every table as zstd —
  compression, not encryption — beside its own KEK.
- The manifest models two things it never did: the `objectStorageBackend` row and `settings.assistant`.
  Without them a wizard-configured platform could not be seeded even ignoring secrets.
- The `…File` / `…Env` reference forms are REMOVED, including the pre-existing
  `settings.paas.dnsApiKeyFile`. They let a document name a host path or environment variable that the
  control plane dereferenced with its own privileges; four adversarial review rounds each found a
  different way to get the value back out. Supply a secret inline, or through the admin API.
- `PUT /v1/platform/manifest` moves from `kubernetescluster.write` to the new `platform.configure`.
  It ingests five credentials; the built-in Editor role held the old permission.
- An export never inlines a secret it cannot prove current: each sealed value carries a recorded source
  and a keyed fingerprint, and a value whose record no longer matches is named in the document's header
  instead of guessed at. Rotating a credential out of band no longer reverts on the next level pass —
  "what the manifest last applied" and "what is sealed right now" are two markers, not one.
- The export header states what the document does NOT carry, so a rebuild is not silently incomplete:
  clusters and node identities, IAM, the object-storage gateway endpoint, the repository target and
  credential, the ADR-0072 fleet declaration, and the settings singletons the schema does not model.

Upgrading

Nothing to do. A manifest still naming a `…File` / `…Env` field is refused with a parse error naming
the field; no installation carried one — the shipped `platform.yaml` did, and is updated here.


## 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.
