Description
## The gap
`/etc/wireguard/hub.key` is 45 bytes and it *is* the identity of the 24-peer district mesh. Lose it and every peer is re-keyed by hand.
After the 2026-08-01 restore drill (#422) the runbook now documents how to back it up and how to restore it, and the procedure is **proven** — a restored key derives the live public key. But nothing runs it. It is a manual command a human must remember. There is no schedule, no off-box destination, and no verification.
That is the same failure shape we just corrected in the runbook: documented protection that does not actually operate.
## Why hydrabackup does not cover this
See the companion hydrabackup issue. In short, it cannot, by design:
- `knownServices` in `internal/backup/scanner.go` has **no hydraguard entry** — installed on the hub today it would discover zero services and upload nothing
- every target uses `Glob: "*.yaml"`; `hub.key` is not YAML
- `skipBasenames` excludes `config.yaml` explicitly because it holds secrets — excluding secret material is a deliberate design choice, not an oversight
So hydrabackup is the right tool for `mesh.yaml` and the wrong tool for `hub.key`. Adding hydraguard to it would improve topology coverage while leaving the irreplaceable part unprotected — worth doing, but not a fix for this.
## What the current state actually is
| Artifact | Off-box copy today | Reconstructable? |
|---|---|---|
| `hub.key` | OVH snapshot only | **No** |
| `mesh.yaml` | OVH snapshot only | Yes, with effort |
| `wg0.conf` | OVH snapshot only | Yes, generated from mesh.yaml + hub.key |
And the OVH snapshot is the unreliable path: ~1 run in 5 completes `size: 0` while reporting `status: active` (2026-07-25, 2026-07-31), unalerted.
`/root/.hydraguard/backups/` is not an off-box copy — it lives on the disk it protects and never contains `hub.key`.
## Proposed
An encrypted, scheduled, off-box copy. Roughly:
```bash
sudo tar -czf - -C / etc/wireguard/hub.key root/.hydraguard/mesh.yaml \
| gpg --encrypt --recipient <backup-key> \
> hub-$(date +%F).tar.gz.gpg
```
Design questions to settle first — these are the actual decisions:
1. **Where does the encryption key live?** A passphrase in a config file on the same box protects nothing. Asymmetric (encrypt-only key on the hub, private key held elsewhere) is the shape that survives the hub being compromised.
2. **Where does the ciphertext go?** Not mirror-a while #414 is open.
3. **How is it verified?** An untested backup is what we started with. Periodic automated restore-and-compare of the derived public key is cheap and is the only check that matters.
## Alternative worth considering
Because `hub.key` is 45 bytes and changes almost never, a scheduled pipeline may be overkill. A one-time encrypted copy in a password manager or offline store, re-taken whenever the key rotates, may be a better fit than automation — fewer moving parts, and nothing to silently break. Worth deciding deliberately rather than defaulting to a cron job.
## Done when
- [ ] An encrypted copy of `hub.key` exists somewhere that is not the hub and not mirror-a
- [ ] The decryption path is documented and has been exercised at least once
- [ ] The runbook says where it is and how to use it
- [ ] Whatever refresh cadence is chosen is either automated or explicitly recorded as manual, with an owner