docs: site secrets ride the artifact deploy; gRPC hub retired in ScadaBridge
This commit is contained in:
@@ -249,6 +249,58 @@ reporting that it is a standby. The verdict remains fully observable through the
|
||||
|
||||
---
|
||||
|
||||
## Secrets (2026-09-28)
|
||||
|
||||
The Host composes `ZB.MOM.WW.Secrets` (a `scadaproj` shared library, not a ScadaBridge component —
|
||||
hence it has no row in the Component Registration Matrix below) via `SecretsRegistration.AddScadaBridgeSecrets`,
|
||||
called on both roles from their respective composition-root `Configure` methods. Design:
|
||||
`docs/plans/2026-09-28-site-secrets-artifact-deploy-design.md` (see its *Implementation notes (as
|
||||
built)* section for where the shipped code deviates from the original design).
|
||||
|
||||
- **Site role — always a local, undecorated store.** A site node's `ISecretStore`/`ISecretResolver`
|
||||
is unconditionally the plain node-local SQLite store (`AddZbSecrets`), regardless of any
|
||||
`Secrets:Store:Provider` value — a Site never reads that key. This is a hard invariant, not
|
||||
just a default: `DeployedSecretsMirror` (Component-SiteRuntime.md) calls `ISecretStore.DeleteAsync`
|
||||
directly on the assumption that a delete is node-local; a replication-decorated store would have
|
||||
published that delete fleet-wide. Secrets reach a site only via Deploy Artifacts (see
|
||||
Component-DeploymentManager.md → *Site secrets*) — there is no other channel.
|
||||
- **Central role — `Secrets:Store:Provider`** (`Sqlite` default | `SqlServer`), read **only** on the
|
||||
Central role:
|
||||
- `Sqlite` (default): each central node keeps its own per-node SQLite secret store. On a
|
||||
**clustered** central (≥ 2 seed nodes) this is a documented misconfiguration — `StartupValidator`
|
||||
logs a boot-time Warning (same shape as the inbound-API-key-store precedent, register row 108),
|
||||
because the two nodes would deploy different secret sets to sites depending on which is active.
|
||||
- `SqlServer`: both central nodes read and write **one shared SQL Server store**
|
||||
(`Secrets:SqlServer:ConnectionString`, a literal/env value — never a `${secret:}` reference,
|
||||
the same bootstrap-circularity rule the inbound-API-key shared store follows) — the `/admin/secrets`
|
||||
page edits it, and it is the store `SiteSecretsCollector` resolves from. The pre-host `${secret:}`
|
||||
expander (the Layer-A pass that resolves `${secret:X}` tokens inside `appsettings` itself, before
|
||||
the DI container is built) reads the **same** shared store when `Provider = SqlServer`, so a
|
||||
central node's boot-time config tokens and its running `ISecretResolver` never disagree about
|
||||
what a token resolves to.
|
||||
- A connection string that is blank, or is itself a `${secret:}` reference (a bootstrap circularity),
|
||||
fails fast naming the offending key.
|
||||
- **Bootstrap secrets are never deployed.** The site's gRPC PSK (`SB-GRPC-PSK-{siteId}`), the LocalDb
|
||||
replication key, and the secrets master key (KEK) are never data-connection-referenced and are
|
||||
never shipped through Deploy Artifacts — a site needs them *before* it can receive a deploy at all,
|
||||
and pushing a rotated one through the channel it authenticates would sever that channel. They stay
|
||||
out-of-band: service environment variables, a hand-seeded store, or a master-key file, exactly as
|
||||
before this feature. Every node — every central and every site — **must share the same KEK**;
|
||||
see `docs/deployment/topology-guide.md` → *Secrets: shared store, per-site deploy, and the KEK*
|
||||
for where that is (and is not) checked.
|
||||
- **The gRPC secrets hub is retired in ScadaBridge (2026-09-28).** ScadaBridge no longer composes
|
||||
`ZB.MOM.WW.Secrets.Replicator.Grpc` on either role, and the historical SQL-Server *replication*
|
||||
mode (`Secrets:Replication:Mode=SqlServer` — distinct from `Secrets:Store:Provider=SqlServer`
|
||||
above, which shares one store rather than replicating between two) is retired too. Setting
|
||||
`Secrets:Replication:Enabled` to anything other than absent/empty/`false` now fails
|
||||
`StartupValidator.Validate` outright, on **either** role, with a message pointing at Deploy
|
||||
Artifacts (sites) and `Secrets:Store:Provider=SqlServer` (a clustered central). A leftover
|
||||
`Secrets:Replication:Mode` or any `Secrets:GrpcHub:*` key (harmless once `Enabled` is unset, but
|
||||
dead configuration) logs a startup Warning naming both sections so they can be cleaned up. The
|
||||
library itself keeps the hub for other consumers; only ScadaBridge stops using it.
|
||||
|
||||
---
|
||||
|
||||
## Component Registration Matrix
|
||||
|
||||
| Component | Central | Site | DI (`AddXxx`) | Actors (`AddXxxActors`) | Endpoints (`MapXxx`) |
|
||||
|
||||
Reference in New Issue
Block a user