Files
Joseph Doherty 54f111e8b1 docs(btdb): register BTDB Manager in the Overview docker registry; record the local rig
- ZB.MOM.WW.Overview appsettings.Docker.json: application "BTDB Manager", instance manager at
  http://zb-btdb-manager:9877 (HasActiveRole false); docker/README.md fleet row.
- CLAUDE.md BTDB row and sister-project-history/btdb.md §7/§9: docker/ layout, the
  dacpac + SQL CLR side-project build, scadabridge-mssql as the BT host.

Claude-Session: https://claude.ai/code/session_015dYqtaTxgf6NK2KuwriMSg
2026-09-04 06:14:22 -04:00

3.9 KiB

docker/ — containerised overview dashboard

Runtime-only image, the HistorianGateway pattern: publish on the host (which has the Gitea feed credentials), copy the output into mcr.microsoft.com/dotnet/aspnet:10.0. Docker never restores.

# from ZB.MOM.WW.Overview/
dotnet publish src/ZB.MOM.WW.Overview/ZB.MOM.WW.Overview.csproj \
  -c Release -o docker/publish -p:UseAppHost=false

cd docker && docker compose up -d --build
open http://localhost:5320

docker/publish/ is build output and is gitignored.

Why the container, and not just dotnet run

The dev rigs publish only some node ports to the host:

Fleet Health endpoint Reachable from the Mac host?
ScadaBridge central-a/b container :5000 yes — localhost:9001 / :9002
ScadaBridge site-* (6) container :8084 no — not published
OtOpcUa central-1/2 container :9000 no — only Traefik :9200, load-balanced across the pair
OtOpcUa site-* (4) container :8080 no — not published
HistorianGateway container :5220 yes — localhost:5220
BTDB Manager container :9877 yes — localhost:9877
MxAccessGateway windev :5130 yes, with the VPN up

So a host-run dashboard can only see part of the fleet. This stack joins the three rig networks (otopcua-dev_default, scadabridge-net, zb-historiangw_default, all declared external) and addresses every node by container DNS name, which is how it sees all of them. BTDB Manager (~/Desktop/BTDB/docker) sits on scadabridge-net too, because its BT database lives on ScadaBridge's infra MSSQL container, so no fourth network is needed for it.

Ports above were established by probing the running rigs on 2026-07-24, not read off the compose files — the two disagree. In particular OtOpcUa's site nodes serve health on :8080, not the :9000 their compose anchor implies, because their explicit Kestrel listeners for LocalDb sync re-bind the primary HTTP port.

Start the rigs before this stack. The networks are external, so bringing the dashboard up or down never disturbs a running rig.

Registry shape

Bound from the Overview section; appsettings.Docker.json is the worked example.

"Overview": {
  "PollIntervalSeconds": 10,      // sweep cadence; per-app and per-instance overrides allowed
  "TimeoutSeconds": 3,            // per-request timeout
  "StaleAfterSeconds": 45,        // must exceed the poll interval, or every card reads Stale
  "Applications": [
    {
      "Name": "ScadaBridge",
      "ManagementLabel": "CentralUI",   // label on each instance's management link
      "Instances": [
        {
          "Name": "central-a",
          "Group": "central",            // one Akka cluster = one group; leader + split-brain
                                         // detection are scoped to it. Omit for standalone apps.
          "BaseUrl": "http://host:5000", // /health/ready and /health/active are probed under this
          "ManagementUrl": "http://localhost:9001/",  // followed by the OPERATOR'S browser, so it
                                                      // must be host-reachable, not container DNS
          "HasActiveRole": true          // false for single-instance gateways: no Active badge,
                                         // and /health/active is never probed
        }
      ]
    }
  ]
}

appsettings.json ships the tuning defaults with an empty Applications list, so a deployment that supplies no registry fails at startup naming the missing key rather than booting into an empty dashboard.

Configuration traps

  • Never set ASPNETCORE_URLS alongside Kestrel__Endpoints__*. Declaring both puts Kestrel into explicit-endpoints mode and one of them is silently discarded.
  • No env_file, no secrets, no login. The dashboard reads anonymous health endpoints and holds no credentials. That is a requirement of the design, not an omission.