Merge branch 'fix/standby-secret-mirror-status' (M1: standby mirror failures on the health report)

# Conflicts:
#	CLAUDE.md
#	docs/plans/2026-09-28-site-secrets-artifact-deploy-design.md
This commit is contained in:
Joseph Doherty
2026-09-28 21:37:03 -04:00
29 changed files with 1738 additions and 106 deletions
@@ -262,6 +262,15 @@ accurate as the spec:
never deletes a name it did not ledger, so they were removed once by hand on the rig (both
nodes stopped together; see the live-gate doc). QA and vd03 never ran the hub
(`Secrets:Replication:Enabled=false`), so nothing like this exists there.
- **Standby mirror failures reach central (follow-up, review finding M1).** A `DeployedSecretsMirror`
failure on the standby (e.g. a KEK mismatch) was only logged on that node — site health reports
are sent by the active node only. Each node now records its current failure set (names +
messages, never values) in a new, **12th LocalDb-replicated table `secret_mirror_status`** (one
row per node keyed by `node_name`), written only when the set changes plus once per start-up; a
per-node `SecretMirrorStatusReporter` puts every node's row on the active node's report as
`SiteHealthReport.SecretMirrorStatuses` (proto `SiteHealthReportDto` field 33, presence-wrapped),
and the Central UI health card shows a *Secret mirror failing* badge + per-node list. Requires
LocalDb replication for the standby's row to be visible, the same prerequisite as the secrets.
- **Rig posture.** `docker/docker-compose.yml`'s central nodes now carry
`Secrets__Store__Provider=SqlServer` plus a shared KEK; site-a shares that KEK (it is the
secrets-via-artifact-deploy site); site-b/site-c deliberately carry a **mismatched** KEK so the