Files
ScadaBridge/docker/central-node-a/appsettings.Central.json
T
Joseph Doherty a300f35f9d chore(rig): ScriptTrace retention override; verify-delmia-notifier step H = Flow Debugger live run + replay
Both central nodes gain a root AuditLog:PerChannelRetentionDays:ScriptTrace=30 (validator
floor). verify-delmia-notifier.sh step H: finds DelmiaRecipeDownload via api-method list,
runs flow-debug run-method with the drill parameters and asserts completed=true plus the
kind set {InboundRequest, RouteCall, ScriptRun, AttributeWrite, AttributeWait} from each
event's detailsJson, then flow-debug replay on the root ExecutionId with the same check,
retried up to 40 s for the 5 s site telemetry push; prints 'H   live+replay -> YES'.

VERIFICATION PENDING: the image scadabridge:latest was built from this branch
(docker/build.sh, exit 0) but the containers have not been recreated and step H has not
run yet — the next step is a recreate through the MAIN checkout's compose file (deploy.sh
refuses a worktree, register row 68: bind-mounted appsettings would rehome), a fresh
DelmiaRecipeDownload-scoped key, and the drill.

Claude-Session: https://claude.ai/code/session_01GSLkkHKz9JrAtmJjmQK3cP
2026-09-06 00:38:41 -04:00

114 lines
5.8 KiB
JSON

{
"ScadaBridge": {
"Node": {
"Role": "Central",
"NodeName": "central-a",
"NodeHostname": "scadabridge-central-a",
"RemotingPort": 8081,
"_metricsPort": "Register row 56. Central maps /metrics on every listener it binds, so it was always reachable on the web port (:5000) — but not on 8084, which is the port the live-scrape recipe probes (docker exec ... </dev/tcp/localhost/8084). Declaring it here binds the same dedicated HTTP/1.1 scrape listener the six site nodes use, so one probe recipe works on all eight nodes and central-only series (KPI/outbox counters, the import-session gauge + rejection counter, alarm-cache gauges) are scrapeable live. Central reads this key ONLY when present — unlike a site node, which always binds 8084 — so an existing central without it is unchanged. No host port mapping is needed: the probe runs inside the container.",
"MetricsPort": 8084
},
"Cluster": {
"SeedNodes": [
"akka.tcp://scadabridge@scadabridge-central-a:8081",
"akka.tcp://scadabridge@scadabridge-central-b:8081"
],
"SplitBrainResolverStrategy": "auto-down",
"StableAfter": "00:00:15",
"HeartbeatInterval": "00:00:02",
"FailureDetectionThreshold": "00:00:10",
"MinNrOfMembers": 1,
"_bootstrapGuard": "Gitea #33 guard ENABLED on the docker rig (2026-08-02): deploy.sh recreates all containers simultaneously, which twice split site pairs into two 1-node clusters on 2026-08-01. Lower host:port founds self-first; the higher node TCP-probes then joins peer-first.",
"BootstrapGuard": {
"Enabled": true,
"PartnerProbeSeconds": 25,
"PartnerProbeIntervalMs": 500,
"ProbeConnectTimeoutMs": 1000
}
},
"Database": {
"_comment": "ConfigurationDb/MachineDataDb (which carry the dev SQL password) are supplied as ScadaBridge__Database__* whole-key env overrides in docker/docker-compose.yml (dev-only-insecure, mirroring the ApiKeyPepper convention). The same dev password already ships committed for the sibling scadabridge-mssql container (infra/docker-compose.yml MSSQL_SA_PASSWORD) \u2014 this is consolidation, not new exposure. Env overrides layer over JSON before the ${secret:} expander runs, so the dev cluster boots with no KEK/secret store. Real/prod config uses ${secret:} tokens in src/.../appsettings.Central.json (T4)."
},
"Security": {
"Ldap": {
"Server": "host.docker.internal",
"Port": 3893,
"FallbackServers": [
"host.docker.internal:3894"
],
"_comment_FallbackServers": "Redundant local GLAuth pair (scadaproj/infra/glauth-redundant): zb-ldap-primary :3893 + zb-ldap-backup :3894 on the Docker host. Replaces the shared 10.100.0.35 GLAuth, whose serviceaccount password was rotated (SEC-36) away from the rig's dev bind creds. Also live-exercises Auth 0.2.x FallbackServers sticky failover.",
"Transport": "None",
"AllowInsecure": true,
"SearchBase": "dc=zb,dc=local",
"ServiceAccountDn": "cn=serviceaccount,dc=zb,dc=local",
"_comment_ServiceAccountPassword": "Supplied as ScadaBridge__Security__Ldap__ServiceAccountPassword env override in docker-compose.yml (dev-only-insecure)."
},
"Auth": {
"DisableLogin": false,
"User": "multi-role"
},
"_comment_JwtSigningKey": "Supplied as ScadaBridge__Security__JwtSigningKey env override in docker-compose.yml (dev-only-insecure).",
"JwtExpiryMinutes": 15,
"IdleTimeoutMinutes": 30,
"RequireHttpsCookie": false,
"ForwardedHeaders": {
"_comment": "Traefik fronts this node on the external scadabridge-net docker network. Trust X-Forwarded-For from the docker bridge address pool so LoginThrottle keys on the real client IP (arch-review R2 N2). Narrow to the Traefik container IP if the network is pinned.",
"Enabled": true,
"KnownNetworks": [
"172.16.0.0/12"
]
}
},
"Communication": {
"DeploymentTimeout": "00:02:00",
"LifecycleTimeout": "00:00:30",
"QueryTimeout": "00:00:30",
"TransportHeartbeatInterval": "00:00:05",
"TransportFailureThreshold": "00:00:15",
"CentralFetchBaseUrl": "http://scadabridge-traefik"
},
"ManagementService": {
"CommandTimeout": "00:00:30",
"LongRunningCommandTimeout": "00:05:00"
},
"HealthMonitoring": {
"ReportInterval": "00:00:30",
"OfflineTimeout": "00:01:00"
},
"InboundApi": {
"DefaultMethodTimeout": "00:00:30",
"_standbyForwarding": "2026-08-28 — a standby central node relays POST /api/{methodName} to the active peer instead of answering 503 STANDBY_NODE. List the OTHER node only.",
"StandbyForwardPeerEndpoints": [ "http://scadabridge-central-b:5000" ]
},
"Notification": {
"SmtpServer": "scadabridge-smtp",
"SmtpPort": 1025,
"AuthMode": "None",
"FromAddress": "scada-notifications@company.com"
},
"NotificationOutbox": {
"DispatchInterval": "00:00:05",
"DispatchBatchSize": 1000
},
"KpiHistory": {
"SampleInterval": "00:01:00",
"RetentionDays": 90,
"PurgeInterval": "1.00:00:00",
"DefaultMaxSeriesPoints": 200
},
"Transport": {
"SourceEnvironment": "docker-cluster",
"MaxUnlockAttemptsPerIpPerHour": 500,
"_comment_MaxUnlockAttemptsPerIpPerHour": "Test-rig override (row-44 throttle landed 2026-08-15): the production default of 10/hour/IP throttles the Playwright transport suite after ~2 back-to-back runs (all tests unlock from one host IP). 500 keeps the throttle path live while making repeated suite runs practical."
},
"Logging": {
"MinimumLevel": "Information"
}
},
"AuditLog": {
"PerChannelRetentionDays": {
"ScriptTrace": 30
}
}
}