docs(secrets): production replication topologies decided — OtOpcUa Akka pair-local, ScadaBridge central gRPC hub (pull-only); scadaproj#3

Design doc + runbook Production-enablement section + GAPS/index updates.
The previously-untracked production-KEK decision is now recorded: one
fleet-wide KEK via appsettings/env (never committed); ScadaBridge rejects
the SqlServer hub on topology grounds (sites talk to central, never the
DB) in favor of a new ZB.MOM.WW.Secrets.Replicator.Grpc package —
pull-only, updates originate at central only, by design.

Claude-Session: https://claude.ai/code/session_014WNM4vjoVksyyBraTXSZE1
This commit is contained in:
Joseph Doherty
2026-08-07 05:28:46 -04:00
parent 537a4f524d
commit 591307c13e
4 changed files with 166 additions and 4 deletions
@@ -187,6 +187,30 @@ Start at the left and move right only when a stated requirement forces it.
---
## Production enablement (decided 2026-08-07)
Which topology each app runs in production is decided — tracked as
[scadaproj#3](https://gitea.dohertylan.com/dohertj2/scadaproj/issues/3), design at
`docs/plans/2026-08-07-secrets-production-topology-and-grpc-hub.md` (scadaproj):
- **OtOpcUa: Akka peer-to-peer, pair-local.** Each application cluster is its own 2-node Akka
mesh (mesh Phase 6), so replication converges within a pair and nowhere else — seed each pair
independently (central via `/admin/secrets`; site pairs via the `secret` CLI or ciphertext-only
bundle import).
- **ScadaBridge: central gRPC hub (§2's SQL-hub mode is NOT used).** Site servers never talk to
the DB directly — they talk to central for everything — so the SqlServer hub's site-side
connection string violates the topology rule. A `ZB.MOM.WW.Secrets.Replicator.Grpc` package
(central-hosted `secrets_hub.v1`, **pull-only: updates originate at central only, by design**)
replaces it; until that ships, ScadaBridge replication stays off.
- **KEK: one fleet-wide production KEK, delivered per node via appsettings/environment override,
never committed.** The committed dev KEK is dev-only. Verify per pair with the §0 digest
compare. A single fleet KEK is what makes bundle-based seeding between pairs possible.
- **Rotation fan-out:** re-wraps do not replicate, so `secret rewrap-all` runs against each
node's store individually (both nodes of every pair; on ScadaBridge also central). The KEK
doctor is the recovery path for a missed node.
---
## Troubleshooting
**`SecretDecryptionException` / `kek_id` mismatch after enabling replication.**