Files
scadalink-design/docs/deployment/installation-guide.md
Joseph Doherty b659978764 Phase 8: Production readiness — failover tests, security hardening, sandboxing, deployment docs
- WP-1-3: Central/site failover + dual-node recovery tests (17 tests)
- WP-4: Performance testing framework for target scale (7 tests)
- WP-5: Security hardening (LDAPS, JWT key length, no secrets in logs) (11 tests)
- WP-6: Script sandboxing adversarial tests (28 tests, all forbidden APIs)
- WP-7: Recovery drill test scaffolds (5 tests)
- WP-8: Observability validation (structured logs, correlation IDs, metrics) (6 tests)
- WP-9: Message contract compatibility (forward/backward compat) (18 tests)
- WP-10: Deployment packaging (installation guide, production checklist, topology)
- WP-11: Operational runbooks (failover, troubleshooting, maintenance)
92 new tests, all passing. Zero warnings.
2026-03-16 22:12:31 -04:00

196 lines
5.7 KiB
Markdown

# ScadaLink 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
ScadaLink ships as a single executable (`ScadaLink.Host.exe`) that runs in either Central or Site role based on configuration.
### Windows Service Installation
```powershell
# Central Node
sc.exe create "ScadaLink-Central" binPath="C:\ScadaLink\ScadaLink.Host.exe" start=auto
sc.exe description "ScadaLink-Central" "ScadaLink SCADA Central Hub"
# Site Node
sc.exe create "ScadaLink-Site" binPath="C:\ScadaLink\ScadaLink.Host.exe" start=auto
sc.exe description "ScadaLink-Site" "ScadaLink SCADA Site Agent"
```
### Directory Structure
```
C:\ScadaLink\
ScadaLink.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
scadalink-20260316.log
```
## Configuration Templates
### Central Node — `appsettings.json`
```json
{
"ScadaLink": {
"Node": {
"Role": "Central",
"NodeHostname": "central-01.example.com",
"RemotingPort": 8081
},
"Cluster": {
"SeedNodes": [
"akka.tcp://scadalink@central-01.example.com:8081",
"akka.tcp://scadalink@central-02.example.com:8081"
]
},
"Database": {
"ConfigurationDb": "Server=sqlserver.example.com;Database=ScadaLink;User Id=scadalink_svc;Password=<CHANGE_ME>;Encrypt=true;TrustServerCertificate=false",
"MachineDataDb": "Server=sqlserver.example.com;Database=ScadaLink_MachineData;User Id=scadalink_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`
```json
{
"ScadaLink": {
"Node": {
"Role": "Site",
"NodeHostname": "site-01-node-a.example.com",
"SiteId": "plant-north",
"RemotingPort": 8081
},
"Cluster": {
"SeedNodes": [
"akka.tcp://scadalink@site-01-node-a.example.com:8081",
"akka.tcp://scadalink@site-01-node-b.example.com:8081"
]
},
"Database": {
"SiteDbPath": "C:\\ScadaLink\\data\\site.db"
},
"DataConnection": {
"ReconnectInterval": "00:00:05",
"TagResolutionRetryInterval": "00:00:30"
},
"StoreAndForward": {
"SqliteDbPath": "C:\\ScadaLink\\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://scadalink@central-01.example.com:8081"
},
"HealthMonitoring": {
"ReportInterval": "00:00:30"
},
"Logging": {
"MinimumLevel": "Information"
}
}
}
```
## Database Setup (Central Only)
### SQL Server
1. Create the configuration database:
```sql
CREATE DATABASE ScadaLink;
CREATE LOGIN scadalink_svc WITH PASSWORD = '<STRONG_PASSWORD>';
USE ScadaLink;
CREATE USER scadalink_svc FOR LOGIN scadalink_svc;
ALTER ROLE db_owner ADD MEMBER scadalink_svc;
```
2. Create the machine data database:
```sql
CREATE DATABASE ScadaLink_MachineData;
USE ScadaLink_MachineData;
CREATE USER scadalink_svc FOR LOGIN scadalink_svc;
ALTER ROLE db_owner ADD MEMBER scadalink_svc;
```
3. Apply EF Core migrations (development):
- Migrations auto-apply on startup in Development environment.
4. Apply EF Core migrations (production):
- Generate SQL script: `dotnet ef migrations script --project src/ScadaLink.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.
## Post-Installation Verification
1. Start the service: `sc.exe start ScadaLink-Central`
2. Check the log file: `type C:\ScadaLink\logs\scadalink-*.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/`