docs: inbound API key store — shared SQL Server provider on central pairs (spec, runbook + central-pairs procedure, DBA DDL copy, topology, env inventory, CLAUDE.md, register row 108 closed with rig proof, row 109 Secrets backoff follow-up, Delmia spec §8)

Claude-Session: https://claude.ai/code/session_013L3XdmVii9i5sY4FChU8pB
This commit is contained in:
Joseph Doherty
2026-09-02 23:31:21 -04:00
parent a83cc8613d
commit f47ceb55b1
11 changed files with 1004 additions and 12 deletions
+26 -2
View File
@@ -21,7 +21,31 @@ Central cluster only. Method scripts execute on the **active** central node; a *
## API Key Management
### Storage
- API keys are stored in the **configuration database (MS SQL)**.
- API keys are stored in the shared `ZB.MOM.WW.Auth.ApiKeys` library's store — **SQLite per node by
default**, or the shared `zbauth` schema in the **configuration database (MS SQL)** on a central
pair when `Provider=SqlServer` (see *Key store provider* below). Revoke/rotate/scope changes are
cluster-wide immediately only under `SqlServer`; under `Sqlite` they apply to whichever node served
the management call.
### Key store provider
Config section `ScadaBridge:InboundApi:ApiKeyStore` (design 2026-09-02, register row 108):
| Key | Default | Notes |
|---|---|---|
| `Provider` | `Sqlite` | `Sqlite` (per node) or `SqlServer` (shared across a central pair). Case-insensitive; an unrecognized value fails startup via `StartupValidator`. |
| `SqlServer:ConnectionString` | `ScadaBridge:Database:ConfigurationDb` | Must be a literal or env override, **never** a `${secret:}` reference — the secrets store is itself SQL-backed, so resolving one through the other would be circular. |
| `SqlServer:Schema` | `zbauth` | Owns `schema_version`, `api_keys`, `api_key_audit`. |
| `SqlServer:CommandTimeout` | 30 s | |
| `ImportFromSqlitePath` | the library's `SqlitePath` default | SQLite file imported **once**, at boot, when `Provider=SqlServer`; insert-where-absent, so both nodes of a pair may import their own file and the result is the union. On success the file is renamed `*.imported` (plus `-wal`/`-shm`); on failure the import logs Error and the node keeps booting on the keys already in SQL. |
A Central node with ≥2 seed nodes still running `Provider=Sqlite` logs a boot-time Warning (not a hard
fail — availability-first, matching the auto-down decision) naming this design, because a failover
will otherwise 401 every inbound client until keys are re-issued. Both docker rigs (`docker/`,
`docker-env2/`) run `Provider=SqlServer`; QA/prod are a separate deployment record. See
`docs/operations/inbound-api-key-reissue.md` → *Central pairs* for the reissue/migration runbook and
`docs/operations/sql/zbauth-schema.sql` for the DDL a DBA runs by hand when the app login lacks
CREATE SCHEMA/TABLE rights.
### Key Properties
- **Name/Label**: Human-readable identifier for the key (e.g., "MES-Production", "RecipeManager-Dev").
@@ -306,7 +330,7 @@ A standby now **relays** the request to the active peer instead (`InboundApiEndp
## Dependencies
- **Configuration Database (MS SQL)**: Stores API keys and method definitions.
- **Configuration Database (MS SQL)**: Stores method definitions, and — under `Provider=SqlServer` — API keys in the `zbauth` schema (see *Key store provider* above). Under the `Sqlite` default, API keys live in a per-node SQLite file instead.
- **Communication Layer**: Routes requests to sites when method implementations need site data.
- **Security & Auth**: API key validation (separate from LDAP/AD — API uses key-based auth).
- **Configuration Database (via IAuditService)**: All API key and method definition changes are audit logged.