{ "_logging": "Host-021: Serilog is the sole logger provider (Program.cs calls builder.Host.UseSerilog()), so the standard Microsoft 'Logging:LogLevel' block has no effect and was removed. The minimum level is set via 'ScadaBridge:Logging:MinimumLevel' (bound to LoggingOptions per Host-011); sinks are defined under the 'Serilog' section below and applied via ReadFrom.Configuration (Host-014). See LoggerConfigurationFactory + Component-Host.md REQ-HOST-8.", "ScadaBridge": { "Security": { "Auth": { "_comment": "DisableLogin bypasses the login form and auto-authenticates every request as User with all roles. DEV/TEST ONLY — no environment guard; a startup warning is the only protection. Never enable in production.", "DisableLogin": false, "User": "multi-role" } } }, "Secrets": { "_sqlitePath": "Deliberately unset. ZB.MOM.WW.Secrets 0.6.2 validates Secrets:SqlitePath at startup — it must be ABSOLUTE and must NOT sit inside the content root, because a path inside the deployment directory is destroyed by an ordinary upgrade (this is what wiped the MxGateway API-key store on 2026-08-09). The former value here ('scadabridge-secrets.db') was relative and would now fail the boot. Leaving it unset gives dev the absolute per-user LocalApplicationData default; every real deployment supplies its own absolute path via its environment overlay.", "MasterKey": { "Source": "Environment", "EnvVarName": "ZB_SECRETS_MASTER_KEY" }, "RunMigrationsOnStartup": true, "ResolveCacheTtl": "00:00:30", "Store": { "_comment": "CENTRAL ONLY (a Site always ignores this and keeps its local SQLite store, permanently - Task 11 retired secrets replication, so there is no decorator left for a site to be given). 'Sqlite' (default) is per-node: on a clustered central, each node's secrets can silently drift the moment anyone edits one on only a single node, which then ships an inconsistent secret set to sites on artifact deploy - StartupValidator warns on a >=2-seed central left on Sqlite. 'SqlServer' points BOTH central nodes at ONE shared SQL-Server database directly (Secrets:SqlServer:ConnectionString, fail-closed on a blank or ${secret:} value), so the pair can never diverge.", "Provider": "Sqlite" }, "_replication": "Secrets replication (the gRPC hub, and the SQL-Server ReplicatingSecretStore mode that preceded it) is retired - Task 11. Secrets:Replication:Enabled=true is now a hard startup failure on either role; sites receive secrets exclusively via Deploy Artifacts, and Secrets:Store:Provider=SqlServer (above) is how a clustered central shares one secret set.", "SqlServer": { "ConnectionString": "", "SchemaName": "zbsecrets", "_comment": "Read ONLY when Secrets:Store:Provider=SqlServer: points BOTH central nodes at ONE shared SQL-Server secret store so the pair cannot diverge. Must be a LITERAL or environment value (Secrets__SqlServer__ConnectionString), never a ${secret:} reference - the pre-host expander needs this store to resolve references, so the reference could never resolve (bootstrap circularity; registration rejects it). SITES leave it empty - sites talk to central, never to central's database. Every node that handles deployed secrets must still carry the SAME ZB_SECRETS_MASTER_KEY - only ciphertext crosses the wire, so a node with a different KEK fails closed on resolve with a kek_id mismatch that reads like corruption but is a deployment error." } }, "Serilog": { "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], "WriteTo": [ { "Name": "Console", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] [{NodeRole}/{NodeHostname}] {Message:lj}{NewLine}{Exception}" } }, { "Name": "File", "Args": { "path": "logs/scadabridge-.log", "rollingInterval": "Day" } } ] } }