Deep-scan catalog of every environment variable ScadaBridge reads (Host, CLI, DelmiaNotifier, EF design-time tooling, test/CI harness) plus supporting infra containers. Each entry carries scope (Runtime/Design-time/Test/Infra), whether a shipped appsettings key already exists for it, consumer, purpose, potential values, and required-ness. Compiled from GetEnvironmentVariable call sites, the Host AddEnvironmentVariables() source, docker/docker-env2 compose, install.ps1, and all appsettings*.json.
14 KiB
Environment Variables
A deep-scan inventory of every environment variable ScadaBridge reads — the Host, the CLI, the
DelmiaNotifier client, EF design-time tooling, and the test/CI harness — plus the supporting infra
containers. Compiled by scanning Environment.GetEnvironmentVariable call sites, the Host's
AddEnvironmentVariables() config source, docker/ + docker-env2/ compose files,
deploy/wonder-app-vd03/install.ps1, and every appsettings*.json.
Scan date: 2026-07-24 · Branch: main
Two columns qualify each variable:
- Scope — where it is read: Runtime (the running product — Host / CLI / DelmiaNotifier),
Design-time (
dotnet eftooling), Test (unit / integration / E2E / perf harness only, never the shipped product), or Infra (a sidecar container, not ScadaBridge code). - In appsettings? — whether a shipped
appsettings*.jsonalready carries the same config key (so the env var is an override or secret substitute), versus being env-only.${secret:...}means the key is present but its value is a secret token, not a literal.
How configuration resolves (read this first)
The Host builds its configuration in src/ZB.MOM.WW.ScadaBridge.Host/Program.cs in this precedence
order (later overrides earlier):
appsettings.jsonappsettings.{SCADABRIDGE_CONFIG}.json(role file —CentralorSite).AddEnvironmentVariables()← every variable below in the "config-override" table.AddCommandLine(args)
Then a pre-host secrets expander resolves any ${secret:NAME} tokens left in the merged config,
reading the encrypted secrets store whose master key comes from ZB_SECRETS_MASTER_KEY. Because
step 3 runs before expansion, a whole-key env override (e.g. ScadaBridge__Database__ConfigurationDb)
wins over — and short-circuits — the corresponding ${secret:...} token. That is the deliberate
"rollback / dev" path the docker rigs use.
Config-key → env-var mapping: replace each : in a config path with __ (double underscore).
So ScadaBridge:Communication:GrpcPsk is set via ScadaBridge__Communication__GrpcPsk. Any config
key in the app can be overridden this way; the table below lists only the ones with an established
purpose (secrets kept off disk, or dev-rig wiring).
1. Role & host environment selection
| Variable | Scope | In appsettings? | Consumed by | Purpose | Potential values | Required |
|---|---|---|---|---|---|---|
SCADABRIDGE_CONFIG |
Runtime | No (it selects the appsettings.{value}.json file) |
Host (Program.cs:42) |
Selects the role-specific overlay. Primary role switch. | Central, Site |
Effectively yes (falls back to DOTNET_ENVIRONMENT, then Production) |
DOTNET_ENVIRONMENT |
Runtime | No | Host (Program.cs:43) |
Fallback role selector when SCADABRIDGE_CONFIG is unset; generic .NET environment name. |
Central, Site, Development, Production |
No |
ASPNETCORE_ENVIRONMENT |
Runtime | No | ASP.NET Core + DisableLoginGuard (Program.cs:438) |
Standard ASP.NET environment. DisableLogin is refused unless this is Development. |
Development, Production |
No (defaults to Production behavior) |
ASPNETCORE_URLS |
Runtime | No (Kestrel binding; not in these appsettings) | Kestrel | HTTP bind addresses. Docker sets http://+:5000; install.ps1 sets http://+:{WebPort} (8085). |
e.g. http://+:5000, http://+:8085 |
No |
SCADABRIDGE_CONFIGandASPNETCORE_ENVIRONMENTare independent. The docker rig runsSCADABRIDGE_CONFIG=CentralandASPNETCORE_ENVIRONMENT=Developmentsimultaneously.
2. Secrets store master key (KEK)
| Variable | Scope | In appsettings? | Consumed by | Purpose | Potential values | Required |
|---|---|---|---|---|---|---|
ZB_SECRETS_MASTER_KEY |
Runtime | Name only (Secrets:MasterKey:EnvVarName in appsettings.json; the value is never committed) |
Host secrets expander | The KEK that decrypts the ZB.MOM.WW.Secrets store, from which every ${secret:...} token resolves. A missing reference fails closed (SecretNotFoundException) before any SQL/LDAP/cluster wiring. |
an opaque base64/hex master key | Yes on any node whose appsettings uses ${secret:...} (shipped Central + Site do). Docker rigs avoid it with whole-key overrides. |
The variable name is itself configurable via Secrets:MasterKey:EnvVarName (defaults to
ZB_SECRETS_MASTER_KEY). See docs/operations/2026-07-16-secrets-clustered-master-key.md.
3. Config-override env vars (ScadaBridge__* whole-key)
Map onto config keys via the :→__ rule. Keep secrets off disk (production, via a secret manager)
or wire the dev rigs without a KEK. The __site-x / __site-a leaf is the literal siteId.
All are Runtime scope (the Host reads them; Host integration-test fixtures set the same names, but
that is the same variable, not a separate one).
| Variable | In appsettings? | Config key | Purpose | Potential values | Required |
|---|---|---|---|---|---|
ScadaBridge__Database__ConfigurationDb |
Yes — ${secret:sql/scadabridge/configdb-connection} in appsettings.Central.json |
ScadaBridge:Database:ConfigurationDb |
Central config DB connection string (MS SQL). Central-only. | Server=host,1433;Database=ScadaBridgeConfig;User Id=...;Password=...;TrustServerCertificate=true |
Yes on Central — via env or the ${secret} |
ScadaBridge__Database__MachineDataDb |
No in shipped src appsettings; present in the deploy overlay (deploy/wonder-app-vd03/appsettings.Central.json, as ${...}). install.ps1 derives it as <DbName>MachineData. |
ScadaBridge:Database:MachineDataDb |
Central machine-data DB connection string. Central-only. | connection string | Yes on Central |
ScadaBridge__Security__JwtSigningKey |
Yes — ${secret:security/scadabridge/jwt-signing-key} in appsettings.Central.json |
ScadaBridge:Security:JwtSigningKey |
HMAC-SHA256 signing key for the cookie-embedded session JWT. | ≥32-char secret | Yes on Central |
ScadaBridge__Security__Ldap__ServiceAccountPassword |
Yes — ${secret:ldap/scadabridge/service-account-password} in appsettings.Central.json |
ScadaBridge:Security:Ldap:ServiceAccountPassword |
LDAP bind-account password. (Renamed from the old flat ...__LdapServiceAccountPassword.) |
the bind password | Yes on Central if LDAP bind is used |
ScadaBridge__InboundApi__ApiKeyPepper |
No — env/secret only (not in any shipped appsettings) | ScadaBridge:InboundApi:ApiKeyPepper |
Per-environment pepper for the inbound API-key peppered-HMAC verifier. Hard Central startup requirement since 2026-06-03. | ≥16-char secret, unique per environment | Yes on Central |
ScadaBridge__Communication__GrpcPsk |
Yes — ${secret:SB-GRPC-PSK-site-1} in appsettings.Site.json |
ScadaBridge:Communication:GrpcPsk |
Site side of the per-site gRPC control-plane PSK. StartupValidator refuses to boot a Site node without it; both pair nodes carry the same value. | per-site secret (e.g. dev-grpc-psk-docker-site-a); prod ${secret:SB-GRPC-PSK-<siteId>} |
Yes on Site |
ScadaBridge__Communication__SitePsks__<siteId> |
No in shipped src appsettings (central normally resolves the SB-GRPC-PSK-<siteId> secret); present only in the docker-compose env blocks |
ScadaBridge:Communication:SitePsks:<siteId> |
Central side of the same key: the value central presents when dialling that site. Must equal the site's GrpcPsk. Override for hosts without a master secret store. |
matches the site's GrpcPsk |
On Central, either this or the SB-GRPC-PSK-<siteId> store secret |
Docker dev values (dev-only-insecure, committed in docker/docker-compose.yml):
ScadaBridge__Communication__SitePsks__site-a = dev-grpc-psk-docker-site-a (also site-b, site-c);
docker-env2 uses SitePsks__site-x = dev-grpc-psk-docker-env2-site-x.
Any other config key is overridable the same way (e.g.
ScadaBridge__Cluster__SplitBrainResolverStrategy,ScadaBridge__Logging__MinimumLevel) — these are the standard .NET config binder, not "special" env vars. Preferappsettingsfiles for non-secret values.
${secret:...} tokens (resolved from the store, NOT env vars)
Not environment variables — config values resolved by the secrets expander using ZB_SECRETS_MASTER_KEY.
All are Runtime scope and, by definition, present In appsettings as tokens.
Token (in appsettings) |
Meaning |
|---|---|
${secret:sql/scadabridge/configdb-connection} |
Central config DB connection string |
${secret:ldap/scadabridge/service-account-password} |
LDAP bind password |
${secret:security/scadabridge/jwt-signing-key} |
JWT signing key |
${secret:SB-GRPC-PSK-<siteId>} |
Per-site gRPC PSK (site side); central resolves SB-GRPC-PSK-{siteId} at channel-build time |
4. CLI (scadabridge / ZB.MOM.WW.ScadaBridge.CLI)
Resolution order per value: command-line flag → env var → ~/.scadabridge/config.json. The CLI does
not read appsettings.json — its config file is ~/.scadabridge/config.json (a separate schema).
| Variable | Scope | In appsettings? | Purpose | Potential values | Required |
|---|---|---|---|---|---|
SCADABRIDGE_MANAGEMENT_URL |
Runtime (CLI) | No — CLI config file has managementUrl instead |
Management API base URL. Overridden by --url; overrides the config file. Missing all three → exit 1, NO_URL. |
e.g. http://localhost:9000 |
One of flag/env/config |
SCADABRIDGE_FORMAT |
Runtime (CLI) | No — CLI config file has defaultFormat instead |
Default output format. Overridden by --format. |
json, table |
No (defaults to json) |
SCADABRIDGE_USERNAME |
Runtime (CLI) | No | LDAP username fallback when --username absent. |
e.g. multi-role |
No (some commands need auth) |
SCADABRIDGE_PASSWORD |
Runtime (CLI) | No | LDAP password fallback when --password absent. Preferred over --password — avoids leaking into process listings / shell history. |
the user's password | No (some commands need auth) |
5. DelmiaNotifier client (WWNotifier.exe)
| Variable | Scope | In appsettings? | Purpose | Potential values | Required |
|---|---|---|---|---|---|
SCADABRIDGE_API_KEY |
Runtime (client) | No — the notifier's appsettings.json holds only BaseUrls/TimeoutSeconds/LogPath; the key is env-only by design |
Inbound API key sent as X-API-Key. Read only from the environment, never a file. Missing/empty → prints API key not configured (SCADABRIDGE_API_KEY), exit -1, no HTTP attempt. |
e.g. sbk_... |
Yes |
This is also why
System.Environmentis wholly forbidden in the script trust model — it would let a site/inbound script exfiltrateSCADABRIDGE_API_KEYand other host env secrets.
6. EF Core design-time tooling
| Variable | Scope | In appsettings? | Purpose | Potential values | Required |
|---|---|---|---|---|---|
SCADABRIDGE_DESIGNTIME_CONNECTIONSTRING |
Design-time | Fallback for ScadaBridge:Database:ConfigurationDb — the factory reads that appsettings key first, then this env var |
Connection string for dotnet ef migrations tooling when the Host's appsettings isn't the source. No hardcoded fallback — missing → actionable failure. |
a config DB connection string | Only for dotnet ef when appsettings is unavailable |
7. Test / CI-only
Not read by the running product — the test harness only. Most default gracefully (skip, or use a local docker value) when unset.
| Variable | Scope | In appsettings? | Consumed by | Purpose | Potential values |
|---|---|---|---|---|---|
SCADABRIDGE_MSSQL_TEST_CONN |
Test | No | MsSqlMigrationFixture, AuditLog migration tests |
Admin (sa) connection to a running MS SQL; absent → local docker sa default; drives Skip.If. |
sa connection string |
SCADABRIDGE_PLAYWRIGHT_DB |
Test | No | PlaywrightDbConnection |
DB connection for Playwright E2E seeding; absent → localhost:1433. |
connection string |
SCADABRIDGE_CLI_DLL |
Test | No | CliRunner |
Absolute path to the built CLI dll for Playwright when auto-probing fails. | path to ...CLI.dll |
SCADABRIDGE_AUDIT_FILTER_4KB_P95_US |
Test | No | HotPathLatencyTests |
Override P95 latency threshold (µs) for the 4 KB audit-filter perf test. | positive number (default 5) |
SCADABRIDGE_AUDIT_FILTER_RAW_P95_US |
Test | No | HotPathLatencyTests |
Override P95 threshold for the raw-capture perf test. | positive number |
SCADABRIDGE_AUDIT_FILTER_NOOP_P95_US |
Test | No | HotPathLatencyTests |
Override P95 threshold for the no-op fast-path perf test. | positive number (default 5) |
HOME / USERPROFILE |
Test | No | CliConfig tests |
Redirected to locate ~/.scadabridge/config.json under test. |
temp dir path |
The Host test fixtures (CentralDbTestEnvironment, ScadaBridgeWebApplicationFactory,
StartupValidationTests) set the section-3 config-override vars (ScadaBridge__Database__*,
ScadaBridge__InboundApi__ApiKeyPepper, etc.) and DOTNET_ENVIRONMENT=Central at runtime; those are
the same variables already documented above, not new ones.
8. Supporting infrastructure containers (not the app)
Set on the sidecar/infra containers in infra/docker-compose.yml, consumed by those images, not by
ScadaBridge code — included for completeness when standing up a rig.
| Variable | Scope | In appsettings? | Container | Purpose | Value used |
|---|---|---|---|---|---|
MSSQL_SA_PASSWORD |
Infra | No | scadabridge-mssql |
SQL Server sa password. |
ScadaBridge_Dev1# (dev-only) |
MSSQL_PID |
Infra | No | scadabridge-mssql |
SQL Server edition. | Developer |
Quick reference: minimum sets to boot a real deploy
Central node (via env, or the ${secret:...} equivalents + ZB_SECRETS_MASTER_KEY):
SCADABRIDGE_CONFIG=Central, ScadaBridge__Database__ConfigurationDb,
ScadaBridge__Database__MachineDataDb, ScadaBridge__Security__JwtSigningKey,
ScadaBridge__Security__Ldap__ServiceAccountPassword, ScadaBridge__InboundApi__ApiKeyPepper, and
for each managed site either the SB-GRPC-PSK-<siteId> secret or ScadaBridge__Communication__SitePsks__<siteId>.
Site node: SCADABRIDGE_CONFIG=Site and ScadaBridge__Communication__GrpcPsk (or its
${secret:SB-GRPC-PSK-<siteId>} form). StartupValidator hard-fails without the PSK.