Files
Joseph Doherty 5f97c9d1ed docs(glauth): point all dev/test LDAP at the shared GLAuth on 10.100.0.35
deployment.md / CLAUDE.md / env_vars.md: the per-app LDAP (scadabridge-ldap
container, OtOpcUa DevStubMode, per-box C:\publish\glauth) is replaced by one
shared zb-shared-glauth on 10.100.0.35:3893 (dc=zb,dc=local); source of truth
infra/glauth/. Fixed stale baseDNs (dc=lmxopcua/dc=otopcua -> dc=zb).
2026-06-04 16:37:52 -04:00

245 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Environment Variables — SCADA/OT family
> Cross-project audit of every environment variable used or read by the sister projects
> and the shared `ZB.MOM.WW.*` libraries. Compiled **2026-06-03** by sweeping C# reads,
> Docker/compose, `launchSettings.json`, shell/PowerShell scripts, and CI for each repo.
> This is a **summary index** — when a value matters operationally, confirm against the
> cited `file:line` in the owning repo (paths below are relative to each project root).
## Scope
| Project | Root | Covered |
|---|---|---|
| OtOpcUa | `~/Desktop/OtOpcUa` | Host, Galaxy/Historian drivers, docker-dev, tests, CI |
| MxAccessGateway | `~/Desktop/MxAccessGateway` | Server (x64), Worker (x86), client CLI, tests, pack script |
| ScadaBridge | `~/Desktop/ScadaBridge` | Host (Central/Site), CLI, `docker/`, `docker-env2/`, `infra/`, tests |
| Shared libs | `~/Desktop/scadaproj/ZB.MOM.WW.*` | Auth, Theme, Health, Telemetry, Configuration, Audit (code + build scripts) |
## How env vars reach these apps
All four .NET apps call `AddEnvironmentVariables()`, so **any** configuration key is overridable
from the environment using the **double-underscore (`__`) → colon (`:`)** convention
(`ScadaBridge__InboundApi__ApiKeyPepper` overrides `ScadaBridge:InboundApi:ApiKeyPepper`). Array
indices use a trailing `__0`, `__1` (`Cluster__SeedNodes__0`). Because *every* options key is
technically settable this way, the tables below split into:
- **Direct reads / operationally-set** — `Environment.GetEnvironmentVariable(...)` in code, or
values actually set in compose/launchSettings/scripts. These are the ones you'll really touch.
- **Config keys overridable via `__`** — the validated/notable options that are normally in
`appsettings*.json` but are commonly (or required to be) supplied via environment in containers.
Not every options key is reproduced — only validated, secret, or container-set ones.
> **Secrets:** rows marked 🔒 are secrets. Per the Auth/Config normalization, peppers/keys/passwords
> are **per-environment secrets injected out-of-band** (secret store / orchestrator), never committed.
> The dev-only values that *do* appear in compose are explicitly insecure placeholders for the local
> clusters — see `scadabridge-local-deploy-gotchas` and `docs/operations/inbound-api-key-reissue.md`.
---
## 1. OtOpcUa
### 1.1 Direct reads / operationally-set (runtime)
| Variable | Where | Purpose | Req? / default | Process |
|---|---|---|---|---|
| `OTOPCUA_ROLES` | `src/Server/.../Host/Program.cs:31` | Comma-list of roles (admin, driver) for conditional wiring | optional | Host |
| `ASPNETCORE_ENVIRONMENT` | `Host/Properties/launchSettings.json:9` | ASP.NET Core environment | optional / `Production` | Host |
| `ASPNETCORE_URLS` | `docker-dev/docker-compose.yml` | Kestrel bind address/port | optional | Host |
| `GALAXY_MXGW_API_KEY` 🔒 | `docker-dev/docker-compose.yml`; resolved in `GalaxyDriver.cs:466` | mxaccessgw API key for the Galaxy driver | required if Galaxy driver deployed | Driver (Galaxy) |
| `OTOPCUA_CONFIG_CONNECTION` 🔒 | `Configuration/DesignTimeDbContextFactory.cs:25` | SQL connection for EF design-time (`dotnet ef`) | required for migrations tooling | build-time |
| `OTOPCUA_HISTORIAN_PIPE` | `Driver.Historian.Wonderware/Program.cs:32` | Named-pipe name for the Historian sidecar IPC | required | Historian sidecar |
| `OTOPCUA_ALLOWED_SID` | `…/Program.cs:34` | Windows SID allowed to connect to the sidecar | required | Historian sidecar |
| `OTOPCUA_HISTORIAN_SECRET` 🔒 | `…/Program.cs:36` | Shared secret for named-pipe auth | required | Historian sidecar |
| `OTOPCUA_HISTORIAN_ENABLED` | `…/Program.cs:48` | Init the Historian SDK (else pipe-only) | optional / `false` | Historian sidecar |
| `OTOPCUA_HISTORIAN_SERVER` | `…/Program.cs:89` | Wonderware Historian host | optional / `localhost` | Historian sidecar |
| `OTOPCUA_HISTORIAN_PORT` | `…/Program.cs:90` | Historian port | optional / `32568` | Historian sidecar |
| `OTOPCUA_HISTORIAN_INTEGRATED` | `…/Program.cs:91` | Use Windows Integrated Security | optional / `true` | Historian sidecar |
| `OTOPCUA_HISTORIAN_USER` | `…/Program.cs:92` | SQL user (when not integrated) | optional | Historian sidecar |
| `OTOPCUA_HISTORIAN_PASS` 🔒 | `…/Program.cs:93` | SQL password (when not integrated) | optional | Historian sidecar |
| `OTOPCUA_HISTORIAN_TIMEOUT_SEC` | `…/Program.cs:94` | SQL command timeout (s) | optional / `30` | Historian sidecar |
| `OTOPCUA_HISTORIAN_MAX_VALUES` | `…/Program.cs:95` | Max values per read query | optional / `10000` | Historian sidecar |
| `OTOPCUA_HISTORIAN_COOLDOWN_SEC` | `…/Program.cs:96` | Failure cooldown before retry (s) | optional / `60` | Historian sidecar |
| `OTOPCUA_HISTORIAN_SERVERS` | `…/Program.cs:99` | Comma-list of historian servers for failover | optional | Historian sidecar |
| `OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED` | `…/Program.cs:125` | Enable alarm-event writer to historian | optional / `true` when enabled | Historian sidecar |
> The Galaxy API key supports prefix resolution (`GalaxyDriver.cs:466`): `env:VAR` (read another
> env var), `file:PATH` (read a file), `dev:LITERAL` (dev only), or an unprefixed literal.
### 1.2 Config keys overridable via `__` (set in `docker-dev/docker-compose.yml`)
| Variable | Purpose | Req? |
|---|---|---|
| `ConnectionStrings__ConfigDb` 🔒 | SQL connection for the OtOpcUa config DB | required |
| `Cluster__Hostname` / `Cluster__Port` / `Cluster__PublicHostname` | Akka remoting bind/advertise | required |
| `Cluster__SeedNodes__0` | Initial seed node URL | required |
| `Cluster__Roles__0` (`__1`) | Cluster role assignment (admin/driver) | required |
| `Security__Jwt__SigningKey` 🔒 | JWT signing key (≥32 bytes) | required |
| `Security__Jwt__Issuer` / `Security__Jwt__Audience` | JWT claims | required |
| `Security__Ldap__*` | LDAP host/bind (in `appsettings.admin*.json`) | per-deploy |
| `Authentication__Ldap__DevStubMode` | Dev LDAP stub (any user → FleetAdmin) — **removed from docker-dev**; `docker-dev` now binds the shared GLAuth on `10.100.0.35:3893` | optional / `false` |
### 1.3 Docker infra & test fixtures (OtOpcUa)
- **docker-dev SQL:** `ACCEPT_EULA=Y`, `SA_PASSWORD` 🔒 (`OtOpcUa!Dev123` dev-only), `MSSQL_PID=Developer`.
- **docker-dev seed (`seed/entrypoint.sh`):** `SQL_HOST` / `SQL_USER` / `SQL_PASSWORD` 🔒 / `SQL_DATABASE` (defaults `sql`/`sa`/`OtOpcUa!Dev123`/`OtOpcUa`).
- **Integration compose:** SQL (`SA_PASSWORD` 🔒 `OtOpcUa!Harness123`) + OpenLDAP (`LDAP_ROOT`, `LDAP_ADMIN_USERNAME`, `LDAP_ADMIN_PASSWORD` 🔒, `LDAP_USERS`, `LDAP_PASSWORDS` 🔒, `LDAP_USER_DC`) — this OpenLDAP instance is **integration-test-only**; the standard DEV auth is the shared GLAuth at `10.100.0.35:3893` (`dc=zb,dc=local`, see `scadaproj/infra/glauth/`).
- **Test-fixture overrides (all optional, default to the shared host `10.100.0.35`):** `OPCUA_SIM_ENDPOINT`, `MODBUS_SIM_ENDPOINT`, `MODBUS_SIM_PROFILE`, `AB_SERVER_ENDPOINT`, `AB_SERVER_PROFILE`, `S7_SIM_ENDPOINT`, `OTOPCUA_FOCAS_SIM_ENDPOINT`, `OTOPCUA_FOCAS_SIM_PROFILE`, `TWINCAT_HOST`, `TWINCAT_NETID`, `TWINCAT_PORT`, `AB_LEGACY_ENDPOINT`, `AB_LEGACY_CIP_PATH`, `AB_LEGACY_COMPOSE_PROFILE`, `OTOPCUA_CONFIG_TEST_SERVER`, `OTOPCUA_CONFIG_TEST_SA_PASSWORD` 🔒, `OTOPCUA_HARNESS_USE_SQL`, `OTOPCUA_HARNESS_USE_LDAP`, `MXGW_ENDPOINT`, `D1_SMOKE_OUT`.
- **CI (`.github/workflows/v2-*.yml`):** `DOTNET_NOLOGO=1`, `DOTNET_CLI_TELEMETRY_OPTOUT=1`.
---
## 2. MxAccessGateway
> Ships **no** Docker/compose assets — it's a Windows-native app (x64 .NET 10 Server + x86 .NET 4.8 Worker).
> The Server spawns the Worker via `ProcessStartInfo.Environment`, passing the two `MXGATEWAY_WORKER_*` vars below.
### 2.1 Direct reads / operationally-set (runtime)
| Variable | Where | Purpose | Req? / default | Process |
|---|---|---|---|---|
| `MXGATEWAY_WORKER_NONCE` 🔒 | Server `WorkerProcessLauncher.cs:180` → Worker `WorkerOptionsParser.cs:78` | Per-session handshake nonce (kept off the command line) | required (generated per session) | Server→Worker |
| `MXGATEWAY_WORKER_PIPE_CONNECT_ATTEMPT_TIMEOUT_MS` | Server `WorkerProcessLauncher.cs:181` → Worker `WorkerPipeClient.cs:255` | Per-attempt named-pipe connect timeout | optional / `2000` | Server→Worker |
| `ASPNETCORE_CONTENTROOT` | `GatewayApplication.cs:130` | Content-root override (logs/wwwroot) | optional / auto | Server |
| `ASPNETCORE_ENVIRONMENT` | `Server/Properties/launchSettings.json:10` | Environment selector | optional / `Production` | Server |
### 2.2 Config keys overridable via `__` (notable)
| Variable | Purpose | Req? |
|---|---|---|
| `MxGateway__ApiKeyPepper` 🔒 | HMAC pepper for API-key secrets in the SQLite auth DB | required when auth Mode=ApiKey |
| `MxGateway__Authentication__Mode` / `__SqlitePath` / `__PepperSecretName` | Auth mode, auth DB path, pepper config-key name | per-deploy |
| `MxGateway__Worker__ExecutablePath` / `__WorkingDirectory` / `__StartupTimeoutSeconds` / `__PipeConnectAttemptTimeoutMilliseconds` | x86 worker launch config | per-deploy |
| `MxGateway__Sessions__*` / `MxGateway__Events__QueueCapacity` | Session pool & event queue tuning | optional |
| `MxGateway__Galaxy__ConnectionString` 🔒 | SQL connection for Galaxy browse RPCs | per-deploy |
| `MxGateway__Alarms__*` / `MxGateway__Dashboard__*` | Alarm monitor & dashboard config | optional |
| `MxGateway__Telemetry__Exporter` / `__OtlpEndpoint` | OpenTelemetry exporter selection / OTLP endpoint | optional |
| `MxGateway__Tls__SelfSignedCertPath` | Self-signed PFX path | optional |
| `Kestrel__Endpoints__Http__Url` / `__Protocols`, `Kestrel__Endpoints__Dashboard__Url` | gRPC (h2c) + dashboard endpoints | per-deploy |
### 2.3 Client CLI, tests & build script (MxGateway)
- **Client CLI / smoke tests:** `MXGATEWAY_ENDPOINT` (default `http://localhost:5000`), `MXGATEWAY_API_KEY` 🔒 (`MxGatewayClientCli.cs:289`).
- **Live-test opt-in gates (set to `1` to enable; otherwise skipped):** `MXGATEWAY_RUN_LIVE_MXACCESS_TESTS`, `MXGATEWAY_RUN_LIVE_LDAP_TESTS`.
- **Live-test params (optional):** `MXGATEWAY_LIVE_MXACCESS_WORKER_EXE`, `_ITEM`, `_CLIENT_NAME`, `_EVENT_TIMEOUT_SECONDS`, `_WRITE_SECURED_USER`, `_WRITE_SECURED_PASSWORD` 🔒, `MXGATEWAY_LIVE_GALAXY_CONN` 🔒.
- **Pack/publish (`scripts/pack-clients.ps1`):** `GITEA_USERNAME`, `GITEA_TOKEN` 🔒 (required with `-Publish`), `JAVA_HOME` (Java client build).
---
## 3. ScadaBridge
> Role is selected by `SCADABRIDGE_CONFIG` (`Central`|`Site`), which picks `appsettings.{role}.json`
> (falls back to `DOTNET_ENVIRONMENT`, then `Production`). The pre-host `StartupValidator` /
> `ConfigPreflight` enforces the **required** keys below and fails fast if any are missing/invalid.
### 3.1 Direct reads (C#)
| Variable | Where | Purpose | Req? / default | Scope |
|---|---|---|---|---|
| `SCADABRIDGE_CONFIG` | `Host/Program.cs:31` | Role selector → appsettings file | optional (→ `DOTNET_ENVIRONMENT``Production`) | both |
| `DOTNET_ENVIRONMENT` | `Host/Program.cs:32` | Fallback role/env selector | optional | both |
| `ASPNETCORE_ENVIRONMENT` | `Host/Program.cs:245` | Dev-mode check | optional | both |
| `SCADABRIDGE_DESIGNTIME_CONNECTIONSTRING` 🔒 | `ConfigurationDatabase/DesignTimeDbContextFactory.cs:48` | EF tooling connection (`dotnet ef`) | optional (build-time) | design-time |
| `SCADABRIDGE_MANAGEMENT_URL` | `CLI/CliConfig.cs:72` | Management API URL for the CLI | optional | CLI |
| `SCADABRIDGE_FORMAT` | `CLI/CliConfig.cs:76` | CLI default output format | optional / `json` | CLI |
| `SCADABRIDGE_USERNAME` | `CLI/CliConfig.cs:81` | CLI LDAP username (safer than `--password`) | optional | CLI |
| `SCADABRIDGE_PASSWORD` 🔒 | `CLI/CliConfig.cs:85` | CLI LDAP password | optional | CLI |
### 3.2 Config keys required at startup (`__` form, enforced by `StartupValidator`)
| Variable | Purpose | Constraint | Scope |
|---|---|---|---|
| `ScadaBridge__Node__Role` | `Central` or `Site` | required | all |
| `ScadaBridge__Node__NodeHostname` | Hostname advertised to cluster | required, non-empty | all |
| `ScadaBridge__Node__RemotingPort` | Akka remoting TCP port | required, 165535 (default 8081) | all |
| `ScadaBridge__Cluster__SeedNodes__0` / `__1` | Akka seed addresses | required, ≥2 entries | all |
| `ScadaBridge__Database__ConfigurationDb` 🔒 | SQL config-DB connection | required | Central |
| `ScadaBridge__Security__JwtSigningKey` 🔒 | Cookie-JWT HMAC key | required, ≥32 chars | Central |
| `ScadaBridge__Security__Ldap__Server` | LDAP host | required | Central |
| `ScadaBridge__InboundApi__ApiKeyPepper` 🔒 | Peppered-HMAC inbound API-key pepper | **required, ≥16 chars** | Central |
| `ScadaBridge__Node__SiteId` | Site identifier | required | Site |
| `ScadaBridge__Database__SiteDbPath` | Site-local SQLite path | required | Site |
| `ScadaBridge__Node__GrpcPort` | gRPC streaming port | default 8083; ≠ remoting/metrics | Site |
| `ScadaBridge__Node__MetricsPort` | Prometheus `/metrics` port | default 8084; ≠ remoting/grpc | Site |
> LDAP service-account fields (`ScadaBridge__Security__Ldap__ServiceAccountDn`,
> `__ServiceAccountPassword` 🔒, `__SearchBase`) are validated **post-host** by `LdapOptionsValidator`
> for Central, not in the fail-fast pre-host pass.
### 3.3 Config keys overridable via `__` (optional, not validated)
Large surface — all bound from the role appsettings and overridable via env. Grouped by module
(each prefixed `ScadaBridge__`):
- **Node:** `Node__NodeName`. **Database:** `Database__MachineDataDb` 🔒, `Database__SkipMigrations`.
- **Security:** `Security__Ldap__Port|Transport|AllowInsecure`, `Security__JwtExpiryMinutes`, `Security__IdleTimeoutMinutes`, `Security__JwtRefreshThresholdMinutes`, `Security__RequireHttpsCookie`.
- **Cluster (SBR):** `Cluster__SplitBrainResolverStrategy|StableAfter|HeartbeatInterval|FailureDetectionThreshold|MinNrOfMembers|DownIfAlone`.
- **Communication:** `Communication__DeploymentTimeout|LifecycleTimeout|QueryTimeout|TransportHeartbeatInterval|TransportFailureThreshold`, `Communication__CentralContactPoints__0…` (Site→Central ClusterClient).
- **HealthMonitoring:** `HealthMonitoring__ReportInterval|OfflineTimeout|CentralOfflineTimeout`.
- **InboundApi:** `InboundApi__DefaultMethodTimeout|MaxRequestBodyBytes`.
- **Notification (SMTP):** `Notification__SmtpServer|SmtpPort|AuthMode|FromAddress|ConnectionTimeoutSeconds|MaxConcurrentConnections`.
- **NotificationOutbox (Central):** `NotificationOutbox__DispatchInterval|DispatchBatchSize|StuckAgeThreshold|TerminalRetention|PurgeInterval|DeliveredKpiWindow`.
- **Transport (Central):** `Transport__SourceEnvironment|BundleSessionTtlMinutes|MaxBundleSizeMb|MaxBundleEntryDecompressedMb|MaxBundleEntryCount|MaxBundleEntryCompressionRatio|MaxUnlockAttemptsPerSession|MaxUnlockAttemptsPerIpPerHour|Pbkdf2Iterations`.
- **Logging:** `Logging__MinimumLevel`.
- **DataConnection (Site):** `DataConnection__ReconnectInterval|TagResolutionRetryInterval|WriteTimeout|StableConnectionThreshold`.
- **StoreAndForward (Site):** `StoreAndForward__SqliteDbPath|ReplicationEnabled|DefaultRetryInterval|DefaultMaxRetries`.
- **SiteEventLog (Site):** `SiteEventLog__RetentionDays|MaxStorageMb|DatabasePath|PurgeInterval`.
- **SiteRuntime (Site):** `SiteRuntime__StartupBatchSize|StartupBatchDelayMs|MaxScriptCallDepth|ScriptExecutionTimeoutSeconds|StreamBufferSize|ScriptExecutionThreadCount`.
- **Telemetry (Site):** `Telemetry__Exporter|OtlpEndpoint`.
### 3.4 Docker / infra / build (ScadaBridge)
- **`docker/docker-compose.yml`** (3-site cluster) — Central: `SCADABRIDGE_CONFIG=Central`, `ASPNETCORE_ENVIRONMENT=Development`, `ASPNETCORE_URLS=http://+:5000`, `ScadaBridge__InboundApi__ApiKeyPepper` 🔒 = `dev-only-insecure-pepper-docker-cluster-0001`; Sites: `SCADABRIDGE_CONFIG=Site`.
- **`docker-env2/docker-compose.yml`** (1-site cluster) — same shape, pepper 🔒 = `dev-only-insecure-pepper-env2-cluster-0001` (distinct per environment).
- **`infra/docker-compose.yml`** — MSSQL (`ACCEPT_EULA=Y`, `MSSQL_SA_PASSWORD` 🔒 `ScadaBridge_Dev1#`, `MSSQL_PID=Developer`), Mailpit SMTP (`MP_SMTP_AUTH_ACCEPT_ANY=1`, `MP_SMTP_AUTH_ALLOW_INSECURE=1`, `MP_MAX_MESSAGES=500`), REST API (`API_NO_AUTH=0`, `PORT=5200`).
- **`docker/Dockerfile` build args:** `NUGET_GITEA_USER` / `NUGET_GITEA_PASS` 🔒 → injected as `NuGetPackageSourceCredentials_dohertj2-gitea`. **`docker/build.sh`** reads `MXGW_NUGET_USER` / `MXGW_NUGET_PASS` 🔒 from host env (blank ⇒ anonymous feed).
- **Seed/init scripts** (`docker*/init-db.sh`, `seed-sites.sh`) hardcode the dev SA password 🔒 `ScadaBridge_Dev1#`.
- **`launchSettings.json`** profiles set `DOTNET_ENVIRONMENT`, `ASPNETCORE_ENVIRONMENT`, `SCADABRIDGE_CONFIG` (`Central`/`Site`).
---
## 4. Shared libraries (`ZB.MOM.WW.*`)
The libraries deliberately read almost nothing from the environment directly — config flows through
strongly-typed options bound by the **consuming** app. Notable exceptions:
| Variable | Where | Purpose | Req? / default |
|---|---|---|---|
| `GITEA_NUGET_SOURCE` | `ZB.MOM.WW.{Auth,Theme,Audit}/build/push.sh:16` | Gitea NuGet feed URL for `dotnet nuget push` | required to publish |
| `GITEA_NUGET_KEY` 🔒 | `…/build/push.sh:17` | Gitea token (`package:write`) | required to publish |
| `ZB_LDAP_IT` | `ZB.MOM.WW.Auth/tests/.../GLAuthIntegrationTests.cs:48` | Gate flag (`1`) to run the live LDAP test | optional (skipped if unset) |
| `ZB_LDAP_SERVER` / `_PORT` / `_BASE` / `_SVC_DN` / `_SVC_PW` 🔒 / `_USER` / `_PW` 🔒 / `_USERATTR` | `…/GLAuthIntegrationTests.cs:52-59` | Live LDAP test connection params | optional (defaults: `localhost`/`3893`/`dc=zb,dc=local`/…); point at the shared GLAuth (`10.100.0.35:3893`, `dc=zb,dc=local`) for the live test |
**Telemetry:** `ZB.MOM.WW.Telemetry` does **not** read standard `OTEL_*` env vars — OTel identity/exporter
come from `ZbTelemetryOptions` passed to `AddZbTelemetry()`. It only reads system properties
(`Environment.MachineName`, `Environment.ProcessId` in `ZbResource.cs:20`) to form
`service.instance.id` / `host.name`. Consuming apps that want OTLP wire it via their own
`…Telemetry__OtlpEndpoint` config key (see MxGateway §2.2, ScadaBridge §3.3).
**Health, Configuration, Audit, Theme:** no direct environment-variable reads (code or build).
---
## 5. Cross-cutting cheat sheet
### Standard framework vars (honored everywhere)
`ASPNETCORE_ENVIRONMENT`, `ASPNETCORE_URLS`, `ASPNETCORE_CONTENTROOT`, `DOTNET_ENVIRONMENT`,
`DOTNET_NOLOGO`, `DOTNET_CLI_TELEMETRY_OPTOUT`.
### Build/publish (Gitea NuGet feed) — naming differs per repo
| Repo | Vars |
|---|---|
| Shared libs | `GITEA_NUGET_SOURCE`, `GITEA_NUGET_KEY` 🔒 |
| MxGateway pack | `GITEA_USERNAME`, `GITEA_TOKEN` 🔒 |
| ScadaBridge image build | `MXGW_NUGET_USER`, `MXGW_NUGET_PASS` 🔒 (→ `NUGET_GITEA_USER`/`NUGET_GITEA_PASS` build args) |
### Secrets inventory (🔒) — inject out-of-band, never commit real values
- **Peppers:** `ScadaBridge__InboundApi__ApiKeyPepper` (≥16, Central-only), `MxGateway__ApiKeyPepper`.
- **Signing/JWT keys:** `Security__Jwt__SigningKey` (OtOpcUa), `ScadaBridge__Security__JwtSigningKey`.
- **API keys / nonces:** `GALAXY_MXGW_API_KEY`, `MXGATEWAY_API_KEY`, `MXGATEWAY_WORKER_NONCE`, `OTOPCUA_HISTORIAN_SECRET`.
- **DB / LDAP / SMTP passwords:** all `*SA_PASSWORD`/`MSSQL_SA_PASSWORD`, `ConnectionStrings__ConfigDb`, `ScadaBridge__Database__ConfigurationDb`, `*Ldap*Password`, `SCADABRIDGE_PASSWORD`, `OTOPCUA_HISTORIAN_PASS`.
- **Feed tokens:** `GITEA_NUGET_KEY`, `GITEA_TOKEN`, `MXGW_NUGET_PASS`/`NUGET_GITEA_PASS`.
> The only secret-typed values that legitimately appear in source are the **dev-only, insecure**
> local-cluster placeholders in `docker*/docker-compose.yml` and the dev SA passwords in the
> `infra`/seed scripts — usable for the local stacks only, never as real secrets.