{ "ScadaBridge": { "_secretsClusterPosture": "Central pair KEK/store posture (production): see docs/operations/2026-07-16-secrets-clustered-master-key.md. Committed default is Source=Environment + relative SqlitePath (dev-safe); production uses a File KEK + shared store volume delivered out-of-band. Do NOT hardcode /shared paths here — the pre-host expander migrates the store at every Central boot and would break dev/test.", "_nodeName": "Host-018: NodeName stamps SourceNode on AuditLog/Notifications/SiteCalls rows (CLAUDE.md 'Centralized Audit Log' decision) and backs IX_AuditLog_Node_Occurred. Convention: 'central-a'/'central-b' for central nodes, 'node-a'/'node-b' for site nodes. Override per-node in multi-node deployments (the docker per-node configs do this). When left at the default below, single-node dev rows are stamped with 'central-a'; an empty value normalises to a NULL SourceNode.", "Node": { "Role": "Central", "NodeHostname": "localhost", "RemotingPort": 8081, "NodeName": "central-a" }, "Cluster": { "SeedNodes": [ "akka.tcp://scadabridge@localhost:8081", "akka.tcp://scadabridge@localhost:8082" ], "SplitBrainResolverStrategy": "auto-down", "StableAfter": "00:00:15", "HeartbeatInterval": "00:00:02", "FailureDetectionThreshold": "00:00:10", "MinNrOfMembers": 1 }, "_secrets": "Host-003: Secrets are NOT committed in this file. The ${secret:...} references below are resolved at startup by the pre-host secrets expander (before StartupValidator runs) from the encrypted secrets store (SQLite, seeded via the ZB.MOM.WW.Secrets store/CLI/UI). The KEK is supplied out-of-band via the ZB_SECRETS_MASTER_KEY environment variable and never committed; an unseeded reference fails closed (SecretNotFoundException) before any SQL/LDAP/cluster wiring. ROLLBACK: the whole-key environment override still wins — set ScadaBridge__Database__ConfigurationDb / ScadaBridge__Security__Ldap__ServiceAccountPassword / ScadaBridge__Security__JwtSigningKey and .AddEnvironmentVariables() overlays the concrete value before expansion runs, so the ${secret:...} token is never evaluated. NOTE (Task 1.4): the LDAP settings moved into the nested Security:Ldap sub-section (bound to the shared ZB.MOM.WW.Auth LdapOptions) — the service-account-password env var is ScadaBridge__Security__Ldap__ServiceAccountPassword (was ScadaBridge__Security__LdapServiceAccountPassword).", "Database": { "ConfigurationDb": "${secret:sql/scadabridge/configdb-connection}" }, "Security": { "Ldap": { "Server": "localhost", "Port": 3893, "Transport": "None", "AllowInsecure": true, "SearchBase": "dc=zb,dc=local", "ServiceAccountDn": "cn=admin,dc=zb,dc=local", "ServiceAccountPassword": "${secret:ldap/scadabridge/service-account-password}" }, "JwtSigningKey": "${secret:security/scadabridge/jwt-signing-key}", "JwtExpiryMinutes": 15, "IdleTimeoutMinutes": 30 }, "Communication": { "DeploymentTimeout": "00:02:00", "LifecycleTimeout": "00:00:30", "QueryTimeout": "00:00:30", "TransportHeartbeatInterval": "00:00:05", "TransportFailureThreshold": "00:00:15", "CentralFetchBaseUrl": "http://localhost:5000" }, "HealthMonitoring": { "ReportInterval": "00:00:30", "OfflineTimeout": "00:01:00" }, "InboundApi": { "DefaultMethodTimeout": "00:00:30" }, "Notification": { "SmtpServer": "localhost", "SmtpPort": 1025, "AuthMode": "None", "FromAddress": "scada-notifications@company.com" }, "NotificationOutbox": { "DispatchInterval": "00:00:10", "DispatchBatchSize": 100, "StuckAgeThreshold": "00:10:00", "TerminalRetention": "365.00:00:00", "PurgeInterval": "1.00:00:00", "DeliveredKpiWindow": "00:01:00" }, "KpiHistory": { "SampleInterval": "00:01:00", "RetentionDays": 90, "PurgeInterval": "1.00:00:00", "DefaultMaxSeriesPoints": 200 }, "Transport": { "BundleSessionTtlMinutes": 30, "MaxBundleSizeMb": 100, "MaxUnlockAttemptsPerSession": 3, "MaxUnlockAttemptsPerIpPerHour": 10, "Pbkdf2Iterations": 600000, "SchemaVersionMajor": 1 }, "Logging": { "MinimumLevel": "Information" } } }