# Release signing: two-key trust anchor, offline recovery, manual local signing

Every shipped binary embeds the release **trusted-key set** that `spt update` verifies against (R-UPD-2), so the keys chosen at v0.1 anchor the self-update trust chain for every deployed node. We mint **two Ed25519 keypairs**: a **primary** (active — signs every release, lives on the maintainer's dev machine) and a **recovery** (generated once, never used, never stored on the dev machine). **Both public keys ship in the binary's trusted set.** Both private seeds (32 bytes each) are backed up independently of any one machine (password manager + optional paper/QR, physically separate); the recovery key exists *only* as backups.

Loss of the primary (e.g. dev machine destroyed) is then a non-event: the next release is signed with the recovery key and rotates in a fresh primary (the verify path already supports key revocation). Without the second key, losing the sole key would kill the self-update chain — nodes keep running but every future release fails verification until users manually re-run the installer.

## Considered options

- **Signing key as a CI secret (automated signing)** — rejected for now. The runners are self-hosted dev boxes running arbitrary dev workloads; a release key in their secret store is weaker custody than a manual local sign, and release cadence doesn't justify automation. Revisit if cadence demands it.
- **Single key + good backups** — rejected; rotation-after-loss requires a *second already-trusted* key, otherwise recovery means out-of-band reinstall for every user.

## Consequences

- Releasing is a partly manual ceremony: CI builds artifacts; the maintainer signs the release metadata locally before publishing.
- The signed metadata published with each release must be in the exact `SignedRelease` format the self-updater consumes, from v0.1 onward.
