- Redundancy.md: full rewrite — Akka-leader-driven ServiceLevel replaces operator-managed RedundancyRole. Documents the 5-tier ServiceLevelCalculator, RedundancyStateActor cluster singleton, and the DPS data flow. - ServiceHosting.md: full rewrite — single fused OtOpcUa.Host binary with OTOPCUA_ROLES env gating. Documents the conditional DI graph and the new health endpoints (/health/ready, /health/active, /healthz). - security.md: v2 banner at top covering path/project renames + new JWT bearer + DataProtection persisted to ConfigDb. Body unchanged because the 4-concern security model is unchanged in v2; full per-section rewrite waits for F15 (Admin pages migration) since security.md references many pages that move. - README.md: platform overview updated to v2 (fused Host + role gating).
4.1 KiB
Service Hosting (v2)
Overview
A production OtOpcUa deployment runs one binary per node, plus the optional Wonderware historian sidecar:
| Process | Project | Runtime | Platform | Responsibility |
|---|---|---|---|---|
| OtOpcUa Host | src/Server/ZB.MOM.WW.OtOpcUa.Host |
.NET 10 | AnyCPU | Single fused binary. OTOPCUA_ROLES env decides what to mount: admin (Blazor + auth + control-plane singletons), driver (OPC UA endpoint + per-driver actors), or both. |
| OtOpcUa Wonderware Historian (optional) | src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware |
.NET Framework 4.8 | x86 (32-bit) | Out-of-process sidecar exposing the Wonderware Historian SDK over a named pipe. Required only when Historian:Wonderware:Enabled=true. |
Galaxy access still uses the separately-installed mxaccessgw sidecar (see docs/v2/Galaxy.ParityRig.md); the gateway owns the MXAccess COM bitness constraint (its worker is x86 net48). Nothing in the OtOpcUa repo carries that constraint anymore.
v2 change. v1's separate
OtOpcUa.Server+OtOpcUa.AdminWindows services merged into a single role-gatedOtOpcUa.Hostbinary. Two installers became one (with a-Rolesparameter). The whole DI graph is composed inOtOpcUa.Host/Program.cs; per-role wiring is conditional on the env var.
Role gating
Program.cs reads OTOPCUA_ROLES, parses it with RoleParser, and conditionally registers services:
| Role present | Wires |
|---|---|
admin |
AddOtOpcUaAuth, AddAdminUI, AddSignalR, AddOtOpcUaAdminClients, MapOtOpcUaAuth, MapAdminUI<App>, MapOtOpcUaHubs, WithOtOpcUaControlPlaneSingletons (5 admin singletons via Akka.Hosting) |
driver |
WithOtOpcUaRuntimeActors (DriverHostActor + DbHealthProbeActor) — and the OPC UA endpoint on port 4840 |
| Either / both | AddOtOpcUaConfigDb, AddOtOpcUaCluster, AddOtOpcUaHealth (/health/ready, /health/active, /healthz) |
Single-node dev: OTOPCUA_ROLES=admin,driver. Production: typically two admin nodes (HA pair) + N driver nodes.
Akka cluster
The host joins an Akka.NET cluster bound to the address in appsettings.json::Cluster:
{
"Cluster": {
"Hostname": "0.0.0.0",
"Port": 4053,
"PublicHostname": "node-a.lan",
"SeedNodes": ["akka.tcp://otopcua@node-a.lan:4053"],
"Roles": ["admin", "driver"]
}
}
WithOtOpcUaClusterBootstrap(inOtOpcUa.Cluster) loads the embedded HOCON (split-brain resolver, pinned dispatcher, failure detector tuning) and overlays remote endpoint + cluster options.- All cluster singletons + per-node actors live on this single ActorSystem — there is no second Akka instance.
See Redundancy.md for the role-leader + ServiceLevel story.
Health endpoints
Both admin and driver nodes expose:
| Path | Status meaning |
|---|---|
/healthz |
Process alive. |
/health/ready |
ConfigDb reachable + cluster member state is Up. |
/health/active |
Admin-role leader (the node Traefik or an HA LB should route traffic to). |
Used by Traefik for the active-leader-only routing pattern (see Task 63 traefik docs — TODO).
OtOpcUa Wonderware Historian (optional)
Unchanged from v1. Pipe IPC contract lives in src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/Contracts/; sidecar pipe handler in src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Pipe/. Install via scripts/install/Install-Services.ps1 -InstallWonderwareHistorian.
Install / Uninstall
scripts/install/Install-Services.ps1 -Roles admin,driver— installsOtOpcUaHost. v2 rewrite tracked as plan Task 62.scripts/install/Uninstall-Services.ps1— stops + removes the host service (and the historian sidecar if installed).
Logging
Serilog with rolling-daily file sinks. Each host writes to logs/otopcua-*.log plus stdout (NSSM/systemd-friendly). Per-environment log level overrides go in appsettings.{Environment}.json.
Depth reference
For the full host-architecture rationale (why fused vs. split, role-gating tradeoffs, multi-node deployment shapes), see docs/plans/2026-05-26-akka-hosting-alignment-design.md §3-4.