Files
scadaproj/CLAUDE.md
T
Joseph Doherty fbf0f23e76 docs(config): correct OtOpcUa draft-validation description
The C# DraftValidator/DraftSnapshot has NO live caller in OtOpcUa src/ (verified
repo-wide) — it is dormant complement code. The enforced pre-publish draft
validation runs DB-side in the sp_ValidateDraft stored procedure (Status='Draft'
-> sp_PublishGeneration lifecycle). Reframe across current-state/SPEC/GAPS/README/
CLAUDE.md from 'runtime draft validation' + a false publish-pipeline caller to
'dormant managed validator; enforcement is DB-side'. Out-of-scope conclusion
for ZB.MOM.WW.Configuration is unchanged.
2026-06-01 10:13:29 -04:00

21 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this repository is

scadaproj is primarily an umbrella/index workspace that aggregates a family of related SCADA / OT / Wonderware / OPC UA "sister projects" that live as sibling directories under ~/Desktop/. It now also hosts five pieces of source itself — the shared ZB.MOM.WW.Auth/ library, the shared ZB.MOM.WW.Theme/ UI kit, the shared ZB.MOM.WW.Health/ health-check library, the shared ZB.MOM.WW.Telemetry/ observability library, and the shared ZB.MOM.WW.Configuration/ config-validation library — all the realized output of their respective component normalizations (see Component normalization). The point of this file is to give a high-level scan of each sister project — its purpose, location, stack, and primary commands — so a fresh Claude Code session can orient across the whole family without opening each repo first.

Each sister project keeps its own authoritative CLAUDE.md. This index is a summary of those files; when you actually work in a project, open that project's own CLAUDE.md for the full picture. See Refreshing this index.

The project list below is curated manually. Add or remove entries as the family changes — do not assume every directory under ~/Desktop/ belongs here.

Sister projects (core SCADA/OT family)

Runtime / implementation (active code)

Project Location Stack Repo Summary
OtOpcUa ~/Desktop/OtOpcUa .NET 10, OPC UA, gRPC gitea.dohertylan.com/dohertj2/lmxopcua OPC UA server that exposes AVEVA System Platform (Wonderware) Galaxy tags as an OPC UA address space. Galaxy access flows through an in-process GalaxyDriver → gRPC → the mxaccessgw gateway.
MxAccessGateway (mxaccessgw) ~/Desktop/MxAccessGateway .NET 10 gateway (x64) + .NET 4.8 worker (x86), gRPC gitea.dohertylan.com/dohertj2/mxaccessgw gRPC gateway giving modern clients full MXAccess parity without loading 32-bit COM. Two-process: gateway (ASP.NET Core gRPC + Blazor dashboard) + per-session x86 worker that owns the MXAccess COM STA. OtOpcUa depends on this.
ScadaBridge ~/Desktop/ScadaBridge .NET 10, Akka.NET, Docker git Full implementation of the distributed SCADA platform — hub-and-spoke (1 central cluster + N site clusters). Projects prefixed ZB.MOM.WW.ScadaBridge.*; solution ZB.MOM.WW.ScadaBridge.slnx. Ships src/, tests/, docker/ topology, and the design docs that are the spec.

Cross-project relationships

The three indexed projects are separate repos and separate processes, coupled at runtime over wire protocols (gRPC + OPC UA) — not by project/compile references. They share the ZB.MOM.WW.* product namespace (ZB.MOM.WW.OtOpcUa.*, ZB.MOM.WW.ScadaBridge.*; the gateway uses MxGateway.*). The common subject is AVEVA System Platform (Wonderware) "Galaxy" data, and mxaccessgw is the linchpin that the other two connect through.

Data flow

                 AVEVA System Platform — Wonderware "Galaxy"
                 (OT source of truth: runtime tags + Galaxy Repository SQL DB)
                                  ▲
                                  │  MXAccess COM  (32-bit, STA message pump)
                                  │
                 ┌────────────────┴─────────────────┐
                 │   MxAccessGateway  (mxaccessgw)   │  gateway x64 .NET10 + worker x86 net48
                 │   gRPC service; OWNS the 32-bit   │  protos: mxaccess_gateway / mxaccess_worker
                 │   COM bitness + STA pump          │          / galaxy_repository
                 └──────┬─────────────────────┬──────┘
        gRPC (MxCommand/MxEvent +             │ gRPC  (ScadaBridge "MxGateway" adapter:
        GalaxyRepository browse)              │        native MxAccess data + A&C alarms)
                 │                            │
           ┌─────┴──────┐                     │
           │  OtOpcUa   │  GalaxyDriver maps  │
           │ OPC UA srv │  Galaxy hierarchy → │
           │  (.NET 10) │  OPC UA addr space  │
           └─────┬──────┘                     │
                 │  OPC UA (opc.tcp; data + A&C alarms)
                 ▼                            ▼
           ┌──────────────────────────────────────────────┐
           │  ScadaBridge — Data Connection Layer (DCL)    │
           │  OPC UA adapter │ MxGateway adapter │ custom   │
           └─────────────────────────┬────────────────────┘
                                      ▼
            Instance Actors → site clusters → central cluster / UI

Edge-by-edge

  • MxAccessGateway is the foundation. It is the only component that loads 32-bit MXAccess COM (its x86 net48 worker owns the COM apartment + STA pump). It exposes that to modern x64/.NET-10 callers over gRPC, and also serves Galaxy Repository SQL browse RPCs. This is why the other two exist as .NET 10 / x64 and never touch COM directly.
  • OtOpcUa → MxAccessGateway (gRPC client). OtOpcUa's in-process GalaxyDriver (src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/) uses two gateway channels: the GalaxyRepositoryClient for the static hierarchy, and an MXAccess session (MxCommand/MxEvent protos) for live read/write/subscribe. A DeployWatcher polls the gateway's deploy-event signal to rebuild the OPC UA address space on Galaxy redeploy. OtOpcUa's job is purely a protocol bridge: it republishes Galaxy as an OPC UA address space for any OPC UA client.
  • ScadaBridge → OPC UA (OPC UA client). ScadaBridge's DCL has an OPC UA adapter that collects data and mirrors native OPC UA Alarms & Conditions. OtOpcUa is exactly such a server, so ScadaBridge can ingest Wonderware data indirectly via OtOpcUa.
  • ScadaBridge → MxAccessGateway (gRPC client). The DCL also has a dedicated MxGateway adapter that talks to mxaccessgw directly for native MxAccess data and alarms — so ScadaBridge can reach Wonderware data directly, bypassing OtOpcUa. Both adapters implement the same IAlarmSubscribableConnection seam, and a read-only NativeAlarmActor unifies OPC-UA-A&C and MxAccess alarms onto one condition model.

Net effect

  • mxaccessgw is a shared dependency of both OtOpcUa and ScadaBridge.
  • ScadaBridge has two paths to the same Wonderware data: (1) OPC UA → OtOpcUa → gateway, or (2) MxGateway adapter → gateway directly. Path 1 gives standards-based OPC UA decoupling; path 2 gives a more direct/native feed.
  • Coupling is loose: each repo references the others only as sibling context (the ## Sister Projects note in ScadaBridge's own CLAUDE.md lists MxAccessGateway and OtOpcUa with their Gitea URLs but states they are not part of its solution).
  • The break surface is the wire contracts, not code. Because coupling is by network protocol, the things that break across repo boundaries are: the gateway's .proto files (mxaccess_gateway.proto, mxaccess_worker.proto, galaxy_repository.proto), and the OPC UA address-space shape OtOpcUa publishes (browse paths, node IDs, A&C alarm model). Changes to any of these must be coordinated across the affected repos — a green build in one repo does not prove the others still interoperate.

Component normalization

Because the sister repos re-implement the same cross-cutting concerns separately and drift apart, components/ normalizes them: per component, the one target spec, each project's code-verified current state, and the gaps between. See components/README.md for the convention and workflow.

Component Status Goal Design Implementation
Auth (login / identity / authz) Built (lib 0.1.0) Shared ZB.MOM.WW.Auth lib components/auth/ ZB.MOM.WW.Auth/
UI Theme (layout / tokens / components) Built (lib 0.1.0) Shared ZB.MOM.WW.Theme RCL components/ui-theme/ ZB.MOM.WW.Theme/
Health (readiness / liveness / active-node) Built (lib 0.1.0) Shared ZB.MOM.WW.Health lib components/health/ ZB.MOM.WW.Health/
Observability (metrics / traces / logs) Built (lib 0.1.0) Shared ZB.MOM.WW.Telemetry lib + .Serilog components/observability/ ZB.MOM.WW.Telemetry/
Config + validation (options / startup validation) Built (lib 0.1.0) Shared ZB.MOM.WW.Configuration lib components/configuration/ ZB.MOM.WW.Configuration/
Audit (event model + writer seam) Built (lib 0.1.0) Shared ZB.MOM.WW.Audit lib components/audit/ ZB.MOM.WW.Audit/

The auth component is fully populated: a normalized spec, a proposed shared-contract, three current-state docs, and an adoption GAPS backlog. Common ground = LDAP/GLAuth identity + peppered-HMAC API keys; left per-project = the authz vocabularies (OPC-UA permissions / gRPC scopes / roles + site-scoping).

The shared library is built and lives in this repo at ZB.MOM.WW.Auth/ (its own nested git repo; .NET 10; 4 packages — Abstractions, Ldap, ApiKeys, AspNetCore; 172 tests; dotnet pack → 4 nupkgs @ 0.1.0). The implementation plan is at docs/plans/2026-06-01-zb-mom-ww-auth-shared-library.md. Not yet adopted by the three apps — that's the follow-on tracked in components/auth/GAPS.md (#8). Build/test from ZB.MOM.WW.Auth/: dotnet test. Consumer matrix: OtOpcUa → Abstractions+Ldap+AspNetCore; MxAccessGateway & ScadaBridge → all four (ApiKeys not used by OtOpcUa).

The UI-theme component is fully populated: a normalized spec, a design-tokens reference, a shared-contract, three current-state docs, and an adoption GAPS backlog. Shared = Technical-Light tokens + IBM Plex fonts + side-rail shell + widgets; left per-project = each app's site.css page layout, route content, scoped .razor.css.

The shared RCL is built and lives in this repo at ZB.MOM.WW.Theme/ (.NET 10 Razor Class Library; single package; 32 bUnit tests; dotnet pack → 1 nupkg @ 0.1.0). The implementation plan is at docs/plans/2026-06-01-zb-mom-ww-theme-shared-library.md. Not yet adopted by the three apps — that's the follow-on tracked in components/ui-theme/GAPS.md. Build/test from ZB.MOM.WW.Theme/: dotnet test. Consumer matrix: all three apps consume the single ZB.MOM.WW.Theme package (OtOpcUa AdminUI, MxGateway Server, ScadaBridge Host + CentralUI).

The health component is fully populated: a normalized spec, a shared-contract, three current-state docs, and an adoption GAPS backlog. Shared = three-tier endpoint convention (ready/active/healthz) + canonical JSON writer + IActiveNodeGate seam + GrpcDependencyHealthCheck + AkkaClusterHealthCheck + ActiveNodeHealthCheck

  • DatabaseHealthCheck<TContext>; left per-project = which probes each app registers, orchestrator wiring, and ScadaBridge's distributed health-monitoring pipeline.

The shared library is built and lives in this repo at ZB.MOM.WW.Health/ (.NET 10; 3 packages — ZB.MOM.WW.Health, ZB.MOM.WW.Health.Akka, ZB.MOM.WW.Health.EntityFrameworkCore; 58 tests; dotnet pack → 3 nupkgs @ 0.1.0). Not yet adopted by the three apps — that's the follow-on tracked in components/health/GAPS.md. Build/test from ZB.MOM.WW.Health/: dotnet test. Consumer matrix: MxAccessGateway → core only; OtOpcUa & ScadaBridge → all three packages.

The observability component is fully populated: a normalized spec, a metric-conventions reference, a shared-contract, three current-state docs, and an adoption GAPS backlog. Shared = OTel Resource (service.name/site.id/node.role identity triple) + standard instrumentation (ASP.NET Core, HttpClient, gRPC client, runtime, process) + Prometheus always-on exporter + OTLP opt-in

  • Serilog two-stage bootstrap + SiteId/NodeRole/NodeHostname enrichers + TraceContextEnricher (trace_id/span_id)
  • ILogRedactor seam; left per-project = application Meters/ActivitySources, sink config, per-operation enrichers, and redaction policies.

The shared library is built and lives in this repo at ZB.MOM.WW.Telemetry/ (.NET 10; 2 packages — ZB.MOM.WW.Telemetry, ZB.MOM.WW.Telemetry.Serilog; 19 tests; dotnet pack → 2 nupkgs @ 0.1.0). MxAccessGateway logging adopted (MEL → Serilog migration done on its own branch) — the one in-pass adoption. Broader OtOpcUa and ScadaBridge telemetry adoption is follow-on, tracked in components/observability/GAPS.md. Build/test from ZB.MOM.WW.Telemetry/: dotnet test. Consumer matrix: all three apps consume both packages after adoption (OtOpcUa, MxGateway Server, ScadaBridge Host + any instrumented project).

The configuration component is fully populated: a normalized spec, a shared-contract, three current-state docs, and an adoption GAPS backlog. Shared = the IValidateOptions<T> failure-accumulation base (OptionsValidatorBase<T>) + reusable rule primitives (ValidationBuilder: port / host:port / required / positive-duration / one-of / min-count) + AddValidatedOptions<TOptions,TValidator>() (bind + validate + ValidateOnStart) + the pre-host ConfigPreflight aggregator (generalizes ScadaBridge's StartupValidator, byte-compatible message); left per-project = each app's options classes + domain rules, and OtOpcUa's draft/generation-content validation (DB-side sp_ValidateDraft; its C# DraftValidator is dormant).

The shared library is built and lives in this repo at ZB.MOM.WW.Configuration/ (.NET 10; single package ZB.MOM.WW.Configuration; 27 tests; dotnet pack → 1 nupkg @ 0.1.0). The implementation plan is at docs/plans/2026-06-01-zb-mom-ww-configuration-shared-library.md. Not yet adopted by the three apps — that's the follow-on tracked in components/configuration/GAPS.md. Build/test from ZB.MOM.WW.Configuration/: dotnet test. Consumer matrix: all three apps consume the single package; ScadaBridge is the heaviest adopter (per-module validators + StartupValidatorConfigPreflight); OtOpcUa adoption is additive (it has no IValidateOptions usage today).

The audit component is fully populated: a normalized spec, an event-model reference, a shared-contract, three current-state docs, and an adoption GAPS backlog. Common ground = canonical AuditEvent record + AuditOutcome enum + IAuditWriter / IAuditRedactor seams + helpers (NullAuditRedactor, TruncatingAuditRedactor, NoOpAuditWriter, CompositeAuditWriter, RedactingAuditWriter) + AddZbAudit DI registration; left per-project = transport/storage and domain vocabulary. Closes the loop on Auth — audit's Actor field = the Auth principal. IAuditRedactor is aligned with Telemetry's ILogRedactor seam convention.

The shared library is built and lives in this repo at ZB.MOM.WW.Audit/ (.NET 10; 1 package — ZB.MOM.WW.Audit; only non-BCL dependency Microsoft.Extensions.DependencyInjection.Abstractions; 19 tests; dotnet pack → 1 nupkg @ 0.1.0). Repo: https://gitea.dohertylan.com/dohertj2/zb-mom-ww-audit. Not yet adopted by the three apps — that's the follow-on tracked in components/audit/GAPS.md. Build/test from ZB.MOM.WW.Audit/: dotnet test. Consumer matrix: all three apps consume the single ZB.MOM.WW.Audit package (OtOpcUa, MxAccessGateway, ScadaBridge each map their own audit record/seam onto the canonical type at the emit boundary).

Per-project primary commands

Run these from inside each project directory (not from scadaproj).

# OtOpcUa
dotnet build ZB.MOM.WW.OtOpcUa.slnx
dotnet test  ZB.MOM.WW.OtOpcUa.slnx
dotnet test --filter "FullyQualifiedName~MyTestClass.MyMethod"   # single test
# Docker fixtures run on shared host 10.100.0.35; control via `lmxopcua-fix` (in ~/bin)

# MxAccessGateway (PowerShell on Windows)
dotnet build src/MxGateway.sln
dotnet build src/MxGateway.Worker/MxGateway.Worker.csproj -p:Platform=x86   # worker MUST be x86
dotnet test  src/MxGateway.Tests/MxGateway.Tests.csproj                     # no MXAccess needed (fake worker)
dotnet run   --project src/MxGateway.Server/MxGateway.Server.csproj

# ScadaBridge  (~/Desktop/ScadaBridge)
dotnet build ZB.MOM.WW.ScadaBridge.slnx
bash docker/deploy.sh        # rebuild + redeploy the 8-node cluster
cd infra && docker compose up -d   # local test services (LDAP, SQL, OPC UA, SMTP, REST, Traefik)

Refreshing this index

This file is meant to be re-scanned when scadaproj is opened in Claude Code:

  1. List sibling SCADA/OT directories: find ~/Desktop -maxdepth 2 -iname "claude.md".
  2. For each project the user wants indexed, read the top of its CLAUDE.md (project overview + build/run sections) and update its row above.
  3. Keep the project set curated — only the SCADA/OT/Wonderware/OPC-UA family belongs here.
  4. Flag new duplicates/overlaps and namespace mismatches rather than silently merging them.

Other workspace projects with a CLAUDE.md (not indexed — promote on request)

Listed so they can be pulled into the index above if you decide they belong.

SCADA/OT, de-indexed (still have a CLAUDE.md under ~/Desktop/):

  • OpcUaTestServer — dual-instance OPC UA test server (.NET 10) for testing OPC UA clients / simulating automation.
  • scada (ScadaLink) — design docs + scaffolding for the distributed SCADA platform (ZB.MOM.ScadaLink.*).
  • scadalink-design-opcua-browser — ScadaBridge variant focused on OPC UA browser / Data Connection Layer work.
  • DARS — DARS → Wonderware SCADA migration (design phase, Q2 2026 go-live).
  • DARS_BU — backup snapshot of DARS.
  • plan — 3-year SCADA IT/OT modernization roadmap (markdown).

Outside the SCADA family:

  • delmia — DELMIA / Apriso (Intercim Velocity) MES customization export (ASP.NET .asmx); MES-adjacent.
  • lightctrl — Raspberry Pi Python I/O control (edge hardware).
  • codestats — Rust CLI for code statistics on .NET solutions/dirs (dev tooling for the .NET repos).
  • servecli — Rust portable SSH/SFTP server for Windows remote task management (greenfield).
  • JdeScopingTool — JD Edwards "LotFinder" .NET 4.8 → .NET 10 migration (ERP, not SCADA).
  • chat — local-first roleplay chat engine.
  • candy2 — candy.ai chat scraping / browser automation.
  • menardslist — Menards.com cart → printable picklist PDF.