Ships Stream B.1-B.6 — the data-plane authorization engine Phase 6.2 runs on.
Integration into OPC UA dispatch (Stream C — Read / Write / HistoryRead /
Subscribe / Browse / Call etc.) is the next PR on this branch.
New Core.Abstractions:
- OpcUaOperation enum enumerates every OPC UA surface the evaluator gates:
Browse, Read, WriteOperate/Tune/Configure (split by SecurityClassification),
HistoryRead, HistoryUpdate, CreateMonitoredItems, TransferSubscriptions,
Call, AlarmAcknowledge/Confirm/Shelve. Stream C maps each one back to its
dispatch call site.
New Core.Authorization namespace:
- NodeScope record + NodeHierarchyKind — 6-level scope addressing for
Equipment-kind (UNS) namespaces, folder-segment walk for SystemPlatform-kind
(Galaxy). NodeScope carries a Kind selector so the evaluator knows which
hierarchy to descend.
- AuthorizationDecision { Verdict, Provenance } + AuthorizationVerdict
{Allow, NotGranted, Denied} + MatchedGrant. Tri-state per decision #149;
Phase 6.2 only produces Allow + NotGranted, Denied stays reserved for v2.1
Explicit Deny without API break.
- IPermissionEvaluator.Authorize(session, operation, scope).
- PermissionTrie + PermissionTrieNode + TrieGrant. In-memory trie keyed on
the ACL scope hierarchy. CollectMatches walks Cluster → Namespace →
UnsArea → UnsLine → Equipment → Tag (or → FolderSegment(s) → Tag on
Galaxy). Pure additive union — matches that share an LDAP group with the
session contribute flags; OR across levels.
- PermissionTrieBuilder static factory. Build(clusterId, generationId, rows,
scopePaths?) returns a trie for one generation. Cross-cluster rows are
filtered out so the trie is cluster-coherent. Stream C follow-up wires a
real scopePaths lookup from the live DB; tests supply hand-built paths.
- PermissionTrieCache — process-singleton, keyed on (ClusterId, GenerationId).
Install(trie) adds a generation + promotes to "current" when the id is
highest-known (handles out-of-order installs gracefully). Prior generations
retained so an in-flight request against a prior trie still succeeds; GC
via Prune(cluster, keepLatest).
- UserAuthorizationState — per-session cache of resolved LDAP groups +
AuthGenerationId + MembershipVersion + MembershipResolvedUtc. Bounded by
MembershipFreshnessInterval (default 15 min per decision #151) +
AuthCacheMaxStaleness (default 5 min per decision #152).
- TriePermissionEvaluator — default IPermissionEvaluator. Fails closed on
stale sessions (IsStale check short-circuits to NotGranted), on cross-
cluster requests, on empty trie cache. Maps OpcUaOperation → NodePermissions
via MapOperationToPermission (total — every enum value has a mapping; tested).
Tests (27 new, all pass):
- PermissionTrieTests (7): cluster-level grant cascades to every tag;
equipment-level grant doesn't leak to sibling equipment; multi-group union
ORs flags; no-matching-group returns empty; Galaxy folder-segment grant
doesn't leak to sibling folder; cross-cluster rows don't land in this
cluster's trie; build is idempotent (B.6 invariants).
- TriePermissionEvaluatorTests (8): allow when flag matches; NotGranted when
no matching group; NotGranted when flags insufficient; HistoryRead requires
its own bit (decision-level requirement); cross-cluster session denied;
stale session fails closed; no cached trie denied; MapOperationToPermission
is total across every OpcUaOperation.
- PermissionTrieCacheTests (8): empty cache returns null; install-then-get
round-trips; new generation becomes current; out-of-order install doesn't
downgrade current; invalidate drops one cluster; prune retains most recent;
prune no-op when fewer than keep; cluster isolation.
- UserAuthorizationStateTests (4): fresh is not stale; IsStale after 5 min
default; NeedsRefresh true between freshness + staleness windows.
Full solution dotnet test: 1078 passing (baseline 906, Phase 6.1 = 1042,
Phase 6.2 Stream A = +9, Stream B = +27 = 1078). Pre-existing Client.CLI
Subscribe flake unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LmxOpcUa
OPC UA server and cross-platform client tools for AVEVA System Platform (Wonderware) Galaxy. The server exposes Galaxy tags via MXAccess as an OPC UA address space. The client stack provides a shared library, CLI tool, and Avalonia desktop application for browsing, reading/writing, subscriptions, alarms, and historical data.
Architecture
OPC UA Clients
(CLI, Desktop UI, 3rd-party)
|
v
+-----------------+ +------------------+ +-----------------+
| Galaxy Repo DB |---->| OPC UA Server |<--->| MXAccess Client |
| (SQL Server) | | (address space) | | (STA + COM) |
+-----------------+ +------------------+ +-----------------+
| |
+-------+--------+ +---------+---------+
| Status Dashboard| | Historian Runtime |
| (HTTP/JSON) | | (SQL Server) |
+----------------+ +-------------------+
Contained Name vs Tag Name
| Browse Path (contained names) | Runtime Reference (tag name) |
|---|---|
TestMachine_001/DelmiaReceiver/DownloadPath |
DelmiaReceiver_001.DownloadPath |
TestMachine_001/MESReceiver/MoveInBatchID |
MESReceiver_001.MoveInBatchID |
Server
The OPC UA server runs on .NET Framework 4.8 (x86) and bridges the Galaxy runtime to OPC UA clients.
Server Prerequisites
- .NET Framework 4.8 SDK
- AVEVA System Platform with ArchestrA Framework installed
- Galaxy repository database (SQL Server, Windows Auth)
- MXAccess COM registered (
LMXProxy.LMXProxyServer) - Wonderware Historian (optional, for historical data access)
- Windows (required for COM interop and MXAccess)
Build and Run Server
dotnet restore ZB.MOM.WW.LmxOpcUa.slnx
dotnet build src/ZB.MOM.WW.LmxOpcUa.Host
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Host
The server starts on opc.tcp://localhost:4840/LmxOpcUa with the None security profile by default. Configure Security.Profiles in appsettings.json to enable Basic256Sha256-Sign or Basic256Sha256-SignAndEncrypt for transport security. See Security Guide.
Install as Windows Service
cd src/ZB.MOM.WW.LmxOpcUa.Host/bin/Debug/net48
ZB.MOM.WW.LmxOpcUa.Host.exe install
ZB.MOM.WW.LmxOpcUa.Host.exe start
Service logon requirement: The service must run under a Windows account that has access to the AVEVA Galaxy and Historian. The default LocalSystem account can connect to MXAccess and SQL Server but cannot authenticate with the Historian SDK (HCAP). Configure the service to "Log on as" a domain or local user that is a recognized ArchestrA platform user. This can be set in services.msc or during install with ZB.MOM.WW.LmxOpcUa.Host.exe install -username DOMAIN\user -password ***.
Run Server Tests
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests
dotnet test tests/ZB.MOM.WW.LmxOpcUa.IntegrationTests
Client Stack
The client stack is cross-platform (.NET 10) and consists of three projects sharing a common IOpcUaClientService abstraction. No AVEVA software or COM is required — the clients connect to any OPC UA server.
Client Prerequisites
- .NET 10 SDK
- No platform-specific dependencies (runs on Windows, macOS, Linux)
Build All Clients
dotnet build src/ZB.MOM.WW.LmxOpcUa.Client.Shared
dotnet build src/ZB.MOM.WW.LmxOpcUa.Client.CLI
dotnet build src/ZB.MOM.WW.LmxOpcUa.Client.UI
Run Client Tests
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Client.Shared.Tests
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Client.CLI.Tests
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Client.UI.Tests
Client CLI
# Connect
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840/LmxOpcUa
# Browse Galaxy hierarchy
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- browse -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=3;s=ZB" -r -d 5
# Read a tag
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- read -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=3;s=TestMachine_001.MachineID"
# Write a tag
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- write -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=3;s=TestChildObject.TestString" -v "Hello"
# Subscribe to changes
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- subscribe -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=3;s=TestChildObject.TestInt" -i 500
# Read historical data
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- historyread -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=3;s=TestMachine_001.TestHistoryValue" --start "2026-03-25" --end "2026-03-30"
# Subscribe to alarm events
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- alarms -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=3;s=TestMachine_001" --refresh
# Query redundancy state
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- redundancy -u opc.tcp://localhost:4840/LmxOpcUa
Client UI
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.UI
The desktop application provides browse tree, subscriptions, alarm monitoring, history reads, and write dialogs. See Client UI Documentation for details.
Project Structure
src/
ZB.MOM.WW.LmxOpcUa.Host/ OPC UA server (.NET Framework 4.8, x86)
Configuration/ Config binding and validation
Domain/ Interfaces, DTOs, enums, mappers
Historian/ Wonderware Historian data source
Metrics/ Performance tracking (rolling P95)
MxAccess/ STA thread, COM interop, subscriptions
GalaxyRepository/ SQL queries, change detection
OpcUa/ Server, node manager, address space, alarms, diff
Status/ HTTP dashboard, health checks
ZB.MOM.WW.LmxOpcUa.Client.Shared/ Shared OPC UA client library (.NET 10)
ZB.MOM.WW.LmxOpcUa.Client.CLI/ Command-line client (.NET 10)
ZB.MOM.WW.LmxOpcUa.Client.UI/ Avalonia desktop client (.NET 10)
tests/
ZB.MOM.WW.LmxOpcUa.Tests/ Server unit + integration tests
ZB.MOM.WW.LmxOpcUa.IntegrationTests/ Server integration tests (live DB)
ZB.MOM.WW.LmxOpcUa.Client.Shared.Tests/ Shared library tests
ZB.MOM.WW.LmxOpcUa.Client.CLI.Tests/ CLI command tests
ZB.MOM.WW.LmxOpcUa.Client.UI.Tests/ UI ViewModel + headless tests
gr/ Galaxy repository docs, SQL queries, schema
Documentation
Server
| Component | Description |
|---|---|
| OPC UA Server | Endpoint, sessions, security policy, server lifecycle |
| Address Space | Hierarchy nodes, variable nodes, primitive grouping, NodeId scheme |
| Galaxy Repository | SQL queries, deployed package chain, change detection |
| MXAccess Bridge | STA thread, COM interop, subscriptions, reconnection |
| Data Type Mapping | Galaxy to OPC UA types, arrays, security classification |
| Read/Write Operations | Value reads, writes, access level enforcement, array element writes |
| Subscriptions | Ref-counted MXAccess subscriptions, data change dispatch |
| Alarm Tracking | AlarmConditionState nodes, InAlarm monitoring, event reporting |
| Historical Data Access | Historian data source, HistoryReadRaw, HistoryReadProcessed |
| Incremental Sync | Diff computation, subtree teardown/rebuild, subscription preservation |
| Configuration | appsettings.json binding, feature flags, validation |
| Status Dashboard | HTTP server, health checks, metrics reporting |
| Service Hosting | TopShelf, startup/shutdown sequence, error handling |
| Security | Transport security profiles, certificate trust, production hardening |
| Redundancy | Non-transparent warm/hot redundancy, ServiceLevel, paired deployment |
Client
| Component | Description |
|---|---|
| Client CLI | Connect, browse, read, write, subscribe, historyread, alarms, redundancy commands |
| Client UI | Avalonia desktop client: browse, subscribe, alarms, history, write values |
Reference
- Galaxy Repository Queries — SQL queries for hierarchy, attributes, and change detection
- Data Type Mapping — Galaxy to OPC UA type mapping with security classification
License
Internal use only.