docs: site secrets ride the artifact deploy; gRPC hub retired in ScadaBridge

This commit is contained in:
Joseph Doherty
2026-09-28 20:22:45 -04:00
parent bc29dc412b
commit 0ccf68951d
8 changed files with 259 additions and 6 deletions
+41
View File
@@ -120,6 +120,14 @@ Both central nodes share state through:
because a failover then 401s every inbound client until keys are re-issued on the survivor. Both
docker rigs run `SqlServer`; see `docs/operations/inbound-api-key-reissue.md` → *Central pairs* to
migrate an existing pair.
- **Secrets store**: shared **only** when `Secrets:Store:Provider = SqlServer` (design 2026-09-28,
`docs/plans/2026-09-28-site-secrets-artifact-deploy-design.md`) — both nodes then read/write the
same shared SQL Server secret store, so `/admin/secrets` create/rotate/delete changes are
cluster-wide immediately and both nodes deploy the same secret set to sites regardless of which
is active. The library default, `Provider = Sqlite`, stores secrets in a **per-node** file instead;
running `Sqlite` on a central pair is a documented misconfiguration — `StartupValidator` logs a
boot-time Warning. See *Secrets: shared store, per-site deploy, and the KEK* below for the KEK
requirement this store does **not** by itself satisfy.
### Load Balancer
@@ -558,6 +566,39 @@ path. That is the accepted posture today — the same trusted-network assumption
already made, now with authentication rather than none. TLS on these listeners is follow-on
hardening and needs no change to the key design.
#### Secrets: shared store, per-site deploy, and the KEK
Site secrets ride the artifact deploy (2026-09-28, `docs/plans/2026-09-28-site-secrets-artifact-deploy-design.md`;
see that doc's *Implementation notes (as built)* for where the shipped code deviates from the
original design). This section is the operational summary; component detail lives in
Component-Host.md → *Secrets*, Component-DeploymentManager.md → *Site secrets*, and
Component-SiteRuntime.md → *Deployed Secrets*.
- **Central**: `Secrets:Store:Provider = SqlServer` on both central nodes — see *Shared State*
above. `SiteSecretsCollector` resolves each site's referenced secrets from that store at deploy
time; a missing/tombstoned secret fails that site's deploy before anything is sent.
- **Every node must share the same KEK** (`ZB_SECRETS_MASTER_KEY` / master-key file) — **every
central node and every site node**, not just the central pair. This is the same requirement the
retired gRPC secrets hub had; it did not change. **Nothing checks this at boot or at the next
reconnect** — a mismatch surfaces only at **deploy time**, on the site: `DeployedSecretsMirror`'s
verify step trial-decrypts every applied secret and reports a distinct error naming the secret
and its `KekId` if the local master key cannot open it. Verify the KEK is identical everywhere
**before** turning this on for a site — the rig's `docker/docker-compose.yml` deliberately runs
site-b/site-c on a mismatched KEK precisely to exercise this failure path live.
- **Standby / passive-node convergence.** The active site node applies a deploy's secrets
synchronously (so the deploy response reflects that apply immediately); the standby node never
receives that synchronous call — it converges through ordinary LocalDb replication of the
`deployed_secrets` table plus its **own** `DeployedSecretsMirror` running on its own schedule,
`ScadaBridge:SiteRuntime:DeployedSecretsMirrorInterval` (default **15s**). A freshly-promoted
standby is therefore current on secrets within one interval of the last deploy, not instantly.
- **Rollout is a deployment record, not a code change** — see the deferred-work register for the
vd03/QA prerequisites (shared store provisioning, `Provider=SqlServer` on both centrals, the KEK
alignment check above, recreating any hand-seeded site secrets centrally, then *Deploy Artifacts*).
- **The gRPC secrets hub is retired in ScadaBridge** — `Secrets:Replication:Enabled` now fails
startup on either role (Component-Host.md → *Secrets*). QA and vd03 currently run
`Replication:Enabled=false`, so upgrading them to a build carrying this change will not itself
fail fast.
## Scaling Guidelines
### Target Scale