Files
ScadaBridge/docs/deployment/installation-guide.md
T
Joseph Doherty 12fa7affc3 chore(deps): LocalDb 0.3.4 -> 0.3.7 (#130, scadaproj#53)
Pins ZB.MOM.WW.LocalDb, .Replication and .Contracts at 0.3.7 (published
2026-11-12). Wire-compatible; no config or code change needed.

- 0.3.6 (scadaproj#36): the library sync service enforces
  LocalDb:Replication:ApiKey itself. LocalDbSyncAuthInterceptor is kept for
  its refuse-every-stream-when-blank rule; its doc comment is updated. The
  rig's site-a pair already carries one identical key on both nodes.
- 0.3.7 (scadaproj#16): a tombstone is held until the peer has it, so a
  node offline past TombstoneRetention no longer resurrects deleted rows.
  The survivor's ledger (sf_messages / site_events deletes) grows for the
  length of the outage; optional TombstoneHardCap bounds it.

Deploy notes added to the topology + installation guides, the S&F
requirement and CLAUDE.md: stop/start the pair together, same ApiKey on
both nodes, ledger growth while a peer is down.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 13:44:53 -05:00

6.6 KiB

ScadaBridge Installation Guide

Prerequisites

  • Windows Server 2019 or later
  • .NET 10.0 Runtime
  • SQL Server 2019+ (Central nodes only)
  • Network connectivity between all cluster nodes (TCP ports 8081-8082)
  • LDAP/Active Directory server accessible from Central nodes
  • SMTP server accessible from all nodes (for Notification Service)

Single Binary Deployment

ScadaBridge ships as a single executable (ZB.MOM.WW.ScadaBridge.Host.exe) that runs in either Central or Site role based on configuration.

Windows Service Installation

# Central Node
sc.exe create "ScadaBridge-Central" binPath="C:\ScadaBridge\ZB.MOM.WW.ScadaBridge.Host.exe" start=auto
sc.exe description "ScadaBridge-Central" "ScadaBridge SCADA Central Hub"

# Site Node
sc.exe create "ScadaBridge-Site" binPath="C:\ScadaBridge\ZB.MOM.WW.ScadaBridge.Host.exe" start=auto
sc.exe description "ScadaBridge-Site" "ScadaBridge SCADA Site Agent"

Directory Structure

C:\ScadaBridge\
  ZB.MOM.WW.ScadaBridge.Host.exe
  appsettings.json
  appsettings.Production.json
  data\                          # Site: SQLite databases
    site.db                      # Deployed configs, static overrides
    store-and-forward.db         # S&F message buffer
  logs\                          # Rolling log files
    scadabridge-20260316.log

Configuration Templates

Central Node — appsettings.json

{
  "ScadaBridge": {
    "Node": {
      "Role": "Central",
      "NodeHostname": "central-01.example.com",
      "RemotingPort": 8081
    },
    "Cluster": {
      "SeedNodes": [
        "akka.tcp://scadabridge@central-01.example.com:8081",
        "akka.tcp://scadabridge@central-02.example.com:8081"
      ]
    },
    "Database": {
      "ConfigurationDb": "Server=sqlserver.example.com;Database=ScadaBridge;User Id=scadabridge_svc;Password=<CHANGE_ME>;Encrypt=true;TrustServerCertificate=false"
    },
    "Security": {
      "LdapServer": "ldap.example.com",
      "LdapPort": 636,
      "LdapUseTls": true,
      "AllowInsecureLdap": false,
      "LdapSearchBase": "dc=example,dc=com",
      "JwtSigningKey": "<GENERATE_A_32_PLUS_CHAR_RANDOM_STRING>",
      "JwtExpiryMinutes": 15,
      "IdleTimeoutMinutes": 30
    },
    "HealthMonitoring": {
      "ReportInterval": "00:00:30",
      "OfflineTimeout": "00:01:00"
    },
    "Logging": {
      "MinimumLevel": "Information"
    }
  },
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "Akka": "Warning"
      }
    }
  }
}

Site Node — appsettings.json

{
  "ScadaBridge": {
    "Node": {
      "Role": "Site",
      "NodeHostname": "site-01-node-a.example.com",
      "SiteId": "plant-north",
      "RemotingPort": 8081
    },
    "Cluster": {
      "SeedNodes": [
        "akka.tcp://scadabridge@site-01-node-a.example.com:8081",
        "akka.tcp://scadabridge@site-01-node-b.example.com:8081"
      ]
    },
    "Database": {
      "SiteDbPath": "C:\\ScadaBridge\\data\\site.db"
    },
    "DataConnection": {
      "ReconnectInterval": "00:00:05",
      "TagResolutionRetryInterval": "00:00:30"
    },
    "StoreAndForward": {
      "SqliteDbPath": "C:\\ScadaBridge\\data\\store-and-forward.db",
      "DefaultRetryInterval": "00:00:30",
      "DefaultMaxRetries": 50,
      "ReplicationEnabled": true
    },
    "SiteRuntime": {
      "ScriptTimeoutSeconds": 30,
      "StaggeredStartupDelayMs": 50
    },
    "SiteEventLog": {
      "RetentionDays": 30,
      "MaxStorageMB": 1024,
      "PurgeIntervalHours": 24
    },
    "Communication": {
      "CentralSeedNode": "akka.tcp://scadabridge@central-01.example.com:8081"
    },
    "HealthMonitoring": {
      "ReportInterval": "00:00:30"
    },
    "Logging": {
      "MinimumLevel": "Information"
    }
  }
}

Database Setup (Central Only)

SQL Server

  1. Create the configuration database:
CREATE DATABASE ScadaBridge;
CREATE LOGIN scadabridge_svc WITH PASSWORD = '<STRONG_PASSWORD>';
USE ScadaBridge;
CREATE USER scadabridge_svc FOR LOGIN scadabridge_svc;
ALTER ROLE db_owner ADD MEMBER scadabridge_svc;
  1. Create the machine data database:
CREATE DATABASE ScadaBridge_MachineData;
USE ScadaBridge_MachineData;
CREATE USER scadabridge_svc FOR LOGIN scadabridge_svc;
ALTER ROLE db_owner ADD MEMBER scadabridge_svc;
  1. Apply EF Core migrations (development):

    • Migrations auto-apply on startup in Development environment.
  2. Apply EF Core migrations (production):

    • Generate SQL script: dotnet ef migrations script --project src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase
    • Review and execute the SQL script against the production database.

Network Requirements

Source Destination Port Protocol Purpose
Central A Central B 8081 TCP Akka.NET remoting
Site A Site B 8081 TCP Akka.NET remoting
Site nodes Central nodes 8081 TCP Central-site communication
Central nodes LDAP server 636 TCP/TLS Authentication
All nodes SMTP server 587 TCP/TLS Notification delivery
Central nodes SQL Server 1433 TCP Configuration database
Users Central nodes 443 HTTPS Blazor Server UI

Firewall Rules

Ensure bidirectional TCP connectivity between all Akka.NET cluster peers. The remoting port (default 8081) must be open in both directions.

Upgrading a Site Pair

Stop both nodes of a site pair, upgrade both, then start both. Rolling one node at a time is not supported as of LocalDb Phase 2 — the legacy snapshot-compatibility handler that made a mixed-version pair converge was deleted with the bespoke replicator, and a mixed pair now diverges silently. See docs/deployment/topology-guide.md for the reasoning.

For a replicated pair (LocalDb 0.3.7):

  • LocalDb:Replication:ApiKey must be set, byte-identical, on both nodes (a ${secret:...} reference or an environment variable — never a literal in a shipped config). Both the library and ScadaBridge's LocalDbSyncAuthInterceptor refuse the sync stream without it.
  • While one node is down, the survivor's delete tombstones are held until the partner returns and acks them, so its LocalDb file grows for the length of the outage. Deleted rows no longer resurrect on rejoin, however long the outage (scadaproj#16).

Post-Installation Verification

  1. Start the service: sc.exe start ScadaBridge-Central
  2. Check the log file: type C:\ScadaBridge\logs\scadabridge-*.log
  3. Verify the readiness endpoint: curl http://localhost:5000/health/ready
  4. For Central: verify the UI is accessible at https://central-01.example.com/