HydraIssues

hub.key has no automated off-box backup — the documented procedure is manual
open improvement Priority: high Project: hydraguard Reporter: 1 Aug 2026 18:22

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:

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