Files
scadaproj/CLAUDE.md
T
Joseph Doherty 6c2d16d4af docs: refresh HistorianGateway + GalaxyRepository status in index
HistorianGateway is now pushed to gitea (gitea.dohertylan.com/dohertj2/historiangw), and
ZB.MOM.WW.GalaxyRepository is published to the Gitea feed and consumed as a PackageReference
(no longer a cross-repo ProjectReference). Updates the sister-project row, the component
table, and the GalaxyRepository narrative; test figure 584 green -> 590 total (584 on macOS).
2026-06-24 07:28:25 -04:00

29 KiB
Raw Blame History

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 six 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, the shared ZB.MOM.WW.Configuration/ config-validation library, and the new ZB.MOM.WW.GalaxyRepository/ Galaxy browse 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 industrial data sources under a unified Equipment-based address space — native-protocol drivers (Modbus, S7, AB CIP/Legacy, TwinCAT, FOCAS, OpcUaClient) and AVEVA System Platform (Wonderware) Galaxy, now a standard Equipment-kind driver (the old SystemPlatform mirror / alias-tag model was retired ~2026-06-12). Galaxy access flows through the in-process GalaxyDriver → gRPC → the mxaccessgw gateway. Surfaces live read + authorized write, native OPC UA Part 9 alarms, and server-side HistoryRead.
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.
HistorianGateway ~/Desktop/HistorianGateway .NET 10 x64, gRPC, Blazor gitea.dohertylan.com/dohertj2/historiangw Single-process gRPC sidecar exposing (1) full read/write API to the AVEVA Historian (5 gRPC services; 15 retrieval modes; historical/backfill writes; tag-config lifecycle; SQL live-value path; store-forward + redundancy resilience; all default-disabled) and (2) read-only Galaxy object-hierarchy browse via the shared ZB.MOM.WW.GalaxyRepository lib (consumed as a Gitea-feed package). No COM, no x86 worker. Dashboard on :5220 (HTTP/1.1); gRPC h2c on :5221. Vendors AVEVA.Historian.Client from histsdk. 590 tests total — 584 green on macOS; the env-gated live historian + Galaxy integration suite (6 tests) skips without a live server.

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 a protocol bridge: it republishes Galaxy — now bound as a standard Equipment-kind driver alongside its native-protocol drivers, not a special SystemPlatform mirror — as an OPC UA address space (live values, Part 9 alarms, HistoryRead) 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.
  • HistorianGateway is a new, independent sidecar (no runtime coupling to the three above). It reaches the Historian via its vendored gRPC client and the Galaxy Repository SQL DB directly, not through mxaccessgw. It consumes the shared ZB.MOM.WW.GalaxyRepository lib (cross-repo ProjectReference). Any client that needs Historian data or Galaxy browse can target HistorianGateway independently; it is not a dependency of OtOpcUa or ScadaBridge today.
  • 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), the historian_gateway.v1 proto (HistorianGateway's own contract), 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) Adopted (lib 0.1.3; all 3 apps, merged to local default main/master + pushed to origin (gitea)) Shared ZB.MOM.WW.Auth lib components/auth/ ZB.MOM.WW.Auth/
UI Theme (layout / tokens / components) Adopted (lib 0.2.0; all 3 apps, merged to local default + pushed to origin (gitea)) 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) Adopted (lib 0.1.0; all 3 apps, local) Shared ZB.MOM.WW.Configuration lib components/configuration/ ZB.MOM.WW.Configuration/
Audit (event model + writer seam) Adopted (lib 0.1.0; all 3 apps, merged to local default main/master + pushed to origin (gitea)) Shared ZB.MOM.WW.Audit lib components/audit/ ZB.MOM.WW.Audit/
Galaxy Repository (object-hierarchy SQL browse + gRPC service) Built (lib 0.1.0, published to the Gitea feed; consumed by HistorianGateway as a feed PackageReference) Shared ZB.MOM.WW.GalaxyRepository lib (design in histsdk + design doc 2026-06-23) ZB.MOM.WW.GalaxyRepository/

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. Adopted across all three apps on 2026-06-02 (auth GAPS #1#8) on each repo's feat/adopt-zb-auth branch — committed + reviewed, then fast-forward-merged into the repo's local default (main/master) and PUSHED to origin (gitea) on 2026-06-03 (in sync; the feat/* branches kept locally as history). Cutover: shared Auth.Ldap, Auth.ApiKeys (ScadaBridge inbound fully re-architected to the keyId/Bearer model), IGroupRoleMapper<TRole> seam, Transport-enum config, canonical ZbClaimTypes/ZbCookieDefaults, unified dev base DN dc=zb,dc=local, and the canonical-six role vocabulary (with ScadaBridge's accepted auditor/admin SoD collapse). Consumer pins: OtOpcUa 0.1.1, MxGateway 0.1.2, ScadaBridge 0.1.3. Per-repo detail in components/auth/GAPS.md + docs/plans/2026-06-02-auth-audit-normalization*.md. 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; 44 bUnit tests; dotnet pack → 1 nupkg @ 0.2.0, published to the Gitea feed). The build plan is at docs/plans/2026-06-01-zb-mom-ww-theme-shared-library.md; the adoption plan at docs/plans/2026-06-03-ui-theme-adoption.md. Adopted across all three apps on 2026-06-03 (full canonical cutover, SPEC §7) on each repo's feat/adopt-zb-theme branch — committed + spec/code-reviewed, then fast-forward-merged into each repo's local default (master/main) and PUSHED to origin (gitea) (in sync; feat/* kept locally as history): OtOpcUa →lmxopcua master@11de14d, ScadaBridge main@58352a6, MxGateway→mxaccessgw main@73e54e2. The 0.1.0 → 0.2.0 bump first promoted nav-expand persistence into the kit (NavRailSection.Key/data-nav-key + a localStorage nav-state.js enhancer emitted by a new <ThemeScripts/>), so all three apps share one persistence mechanism (OtOpcUa's bespoke cookie/JS-interop nav island retired); MxGateway additionally gained a net-new Blazor <LoginCard> /login page over its existing hardened endpoint. Per-app result 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). Adopted across all three apps on 2026-06-01 (branch feat/adopt-zb-telemetry per repo, behaviour-preserving): AddZbTelemetry (Resource + standard instrumentation + Prometheus /metrics) everywhere; OtOpcUa + MxGateway on AddZbSerilog (MxGateway's MEL→Serilog migration + metrics export both landed in this pass — they were not actually done beforehand despite an earlier claim); ScadaBridge keeps its LoggerConfigurationFactory (min-level governance) and only adds the shared TraceContextEnricher. Deferred: MxGateway mss + Meter rename, ScadaBridge app instruments + Site-node HTTP/1.1 metrics listener, OTLP wiring. Per-repo result 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. Adopted across all three apps on 2026-06-01 (OtOpcUa, MxAccessGateway, ScadaBridge) on each repo's local default branch (main/master) — merged, not yet pushed to remotes; the package was first published to the Gitea feed. Behaviour-preserving onto OptionsValidatorBase/AddValidatedOptions for MxGateway + ScadaBridge (validator messages byte-identical), StartupValidatorConfigPreflight for ScadaBridge, and net-new Ldap/OpcUa validators for OtOpcUa. Per-app result 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. Adopted across all three apps on 2026-06-02 (audit GAPS #1#6) on each repo's feat/adopt-zb-audit branch (stacked on feat/adopt-zb-auth) — committed + reviewed, then merged into the repo's local default (main/master) and PUSHED to origin (gitea) on 2026-06-03 (in sync). Depth = DEEP adopt (the canonical 9-field AuditEvent is the record everywhere; domain fields ride in DetailsJson). OtOpcUa: canonical record + AuditWriterActor : IAuditWriter + Outcome column/migration + ClusterAudit fix. MxGateway: new canonical SQLite audit_event store + IAuditWriter + IApiKeyAuditStore→canonical adapter. ScadaBridge: a full audit-subsystem re-architecture (the program's largest task) — canonical record everywhere via a deterministic codec; site SQLite split into audit_event + an audit_forward_state forwarding sidecar; central partitioned dbo.AuditLog collapsed to 10 canonical cols + persisted computed cols (CollapseAuditLogToCanonical migration, MSSQL-verified). Phase 3 wires Actor from the Auth principal at authenticated emit sites (per-app IAuditActorAccessor). Per-repo detail in components/audit/GAPS.md + docs/plans/2026-06-02-auth-audit-normalization-phase2-deep.md + …-scadabridge-audit-rearch.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 — DEEP-adopted as the canonical record).

The Galaxy Repository component normalizes the Galaxy object-hierarchy SQL browse + reusable gRPC service that was previously embedded in mxaccessgw. Shared = canonical galaxy_repository.v1 proto (wire-compatible with mxaccessgw's existing contract so OtOpcUa's GalaxyRepositoryClient is unaffected), the SQL browse provider (HierarchySql / AttributesSql validated reverse-engineered queries), in-memory hierarchy cache + snapshot + deploy-poll refresh BackgroundService, GalaxyHierarchyProjector, and AddZbGalaxyRepository / MapZbGalaxyRepository DI extension. Left per-consumer = section path, subtree auth filtering, and any app-specific paging defaults.

The shared library is built and lives in this repo at ZB.MOM.WW.GalaxyRepository/ (.NET 10; single package ZB.MOM.WW.GalaxyRepository; dotnet pack → 1 nupkg @ 0.1.0, published to the Gitea NuGet feed gitea.dohertylan.com/api/packages/dohertj2/nuget). The design doc is at docs/plans/2026-06-23-historian-gateway-design.md (§10, component 1). Consumed by HistorianGateway as a PackageReference from that Gitea feed, pinned at 0.1.0 (originally a cross-repo ProjectReference to this scadaproj tree; switched to the feed package 2026-06-24). mxaccessgw adoption is a tracked follow-on — once adopted, mxaccessgw's inline Galaxy browse code is replaced by the shared lib (the galaxy_repository.v1 wire contract is already identical, so OtOpcUa and ScadaBridge clients are unaffected). Build/test from ZB.MOM.WW.GalaxyRepository/: dotnet test. Consumer matrix: HistorianGateway (initial); mxaccessgw (follow-on adoption).

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 (SQL, OPC UA, SMTP, REST, Traefik) — LDAP is NOT here

# HistorianGateway  (~/Desktop/HistorianGateway)
dotnet build ZB.MOM.WW.HistorianGateway.slnx
dotnet test  ZB.MOM.WW.HistorianGateway.slnx   # unit + golden; live integration tests skip without env vars
dotnet run --project src/ZB.MOM.WW.HistorianGateway.Server/ZB.MOM.WW.HistorianGateway.Server.csproj
# dashboard on :5220, gRPC h2c on :5221
# Live integration (need HISTORIAN_GRPC_HOST + HISTORIAN_GRPC_WRITE_SANDBOX_TAG + GALAXY_SQL_CONNSTR set)
dotnet test ZB.MOM.WW.HistorianGateway.slnx --filter "Category=LiveIntegration"

Shared GLAuth (all three apps + HistorianGateway): LDAP auth for every local dev/test stack is provided by a single zb-shared-glauth container on the Linux fixture host 10.100.0.35:3893 (baseDN dc=zb,dc=local, Transport=None). Source of truth and deploy runbook: scadaproj/infra/glauth/ (config.toml + docker-compose.yml + README.md).

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.