diff --git a/CLAUDE.md b/CLAUDE.md index 34dde4f..b9aedb0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,14 +6,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co `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/`](ZB.MOM.WW.Auth/) library, the shared -[`ZB.MOM.WW.Theme/`](ZB.MOM.WW.Theme/) UI kit, the shared -[`ZB.MOM.WW.Health/`](ZB.MOM.WW.Health/) health-check library, the shared -[`ZB.MOM.WW.Telemetry/`](ZB.MOM.WW.Telemetry/) observability library, the shared -[`ZB.MOM.WW.Configuration/`](ZB.MOM.WW.Configuration/) config-validation library, and the new -[`ZB.MOM.WW.GalaxyRepository/`](ZB.MOM.WW.GalaxyRepository/) Galaxy browse library — all the realized output of their +directories under `~/Desktop/`**. It now also **hosts nine shared libraries itself** (verified +2026-07-18) — [`ZB.MOM.WW.Auth/`](ZB.MOM.WW.Auth/) (login/identity), +[`ZB.MOM.WW.Theme/`](ZB.MOM.WW.Theme/) (UI kit), +[`ZB.MOM.WW.Health/`](ZB.MOM.WW.Health/) (health checks), +[`ZB.MOM.WW.Telemetry/`](ZB.MOM.WW.Telemetry/) (observability), +[`ZB.MOM.WW.Configuration/`](ZB.MOM.WW.Configuration/) (config validation), +[`ZB.MOM.WW.Audit/`](ZB.MOM.WW.Audit/) (audit event model), +[`ZB.MOM.WW.Secrets/`](ZB.MOM.WW.Secrets/) (encrypted secret store), +[`ZB.MOM.WW.GalaxyRepository/`](ZB.MOM.WW.GalaxyRepository/) (Galaxy browse), and +[`ZB.MOM.WW.LocalDb/`](ZB.MOM.WW.LocalDb/) (embedded cache + 2-node sync) — all the realized output of their respective component normalizations (see [Component normalization](#component-normalization)). +(A tenth directory, `ZB.MOM.WW.SPHistorianClient/`, is **retired** — 0 tracked files, only +gitignored build residue left on disk.) 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. @@ -137,14 +142,14 @@ each project's **code-verified current state**, and the **gaps** between. See | Component | Status | Goal | Design | Implementation | |---|---|---|---|---| | Auth (login / identity / authz) | Adopted (lib `0.1.4`; all 3 apps, merged to **local default** main/master + **pushed to origin** (gitea)) | Shared `ZB.MOM.WW.Auth` lib | [`components/auth/`](components/auth/) | [`ZB.MOM.WW.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/`](components/ui-theme/) | [`ZB.MOM.WW.Theme/`](ZB.MOM.WW.Theme/) | -| Health (readiness / liveness / active-node) | Built (lib `0.1.0`) | Shared `ZB.MOM.WW.Health` lib | [`components/health/`](components/health/) | [`ZB.MOM.WW.Health/`](ZB.MOM.WW.Health/) | -| Observability (metrics / traces / logs) | Built (lib `0.1.0`) | Shared `ZB.MOM.WW.Telemetry` lib + `.Serilog` | [`components/observability/`](components/observability/) | [`ZB.MOM.WW.Telemetry/`](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/`](components/configuration/) | [`ZB.MOM.WW.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/`](components/audit/) | [`ZB.MOM.WW.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/`](ZB.MOM.WW.GalaxyRepository/) | -| Secrets (encrypted store + `${secret:}` resolution) | Built (libs `0.1.2`, **published to the Gitea feed**; **HistorianGateway adopted + live-proven** 2026-07-16; **mxaccessgw adopted G-4/G-5/G-6 + merged to `origin/main` @ `e088dfa`** 2026-07-16, box-verified fail-closed + encrypt-at-rest; **ScadaBridge adopted G-3/G-4/G-5/G-6 + merged to `origin/main` @ `128f1596`** 2026-07-16, **G-3 live-proven vs the real production MxGateway gateway** `wonder-app-vd03:5120` (secret-ref ApiKey → Connected + browsed real Galaxy); **OtOpcUa adopted G-2/G-4/G-5/G-6 + merged to `origin/master` @ `872cf7e3`** (lmxopcua) 2026-07-16 — the last app, so **all four now adopted**; Layer-B driver secrets (Galaxy API key + OpcUaClient `Password`/`UserCertificatePassword`) resolve fail-closed at driver session-open, `AddZbSecrets` registered unconditionally so driver-only nodes work; **G-2 live-proven vs the real production MxGateway** `wonder-app-vd03:5120` — a `secret:`-ref Galaxy ApiKey resolved through OtOpcUa's real `GalaxyDriverBrowser` → dummy key `MxGatewayAuthenticationException`, real key → browsed the real Galaxy root (10 nodes); temp key minted+revoked+deleted); **G-8 KEK-rotation BUILT (lib bumped `0.1.2`→`0.1.3`, 2026-07-17):** `Rewrap` DEK primitive + CAS-guarded `ApplyRewrapAsync` + `KekRotationService.RewrapAllAsync` + `secret rewrap-all` CLI + operator runbook (`ZB.MOM.WW.Secrets/docs/operations/kek-rotation.md`), full suite green + CLI smoke + adversarial crypto review PASS (TOCTOU closed via compare-and-swap); republish to feed is opt-in. **G-7 clustered replication DESIGNED + PLANNED** — fork resolved to **build the shared SQL-Server `ISecretStore`** (Akka replicator deferred phase-2), executable plan `docs/plans/2026-07-17-secrets-g7-*`. Both not yet committed/pushed (awaiting go-ahead) | Shared `ZB.MOM.WW.Secrets` lib (3 packages + CLI) | [`components/secrets/`](components/secrets/) | [`ZB.MOM.WW.Secrets/`](ZB.MOM.WW.Secrets/) | -| LocalDb (embedded cache + 2-node sync) | Built (3 pkgs `0.1.0`, **NOT yet published to the feed**; no app adoption yet) | Shared `ZB.MOM.WW.LocalDb` lib | [`docs/plans/2026-07-17-localdb-design.md`](docs/plans/2026-07-17-localdb-design.md) | [`ZB.MOM.WW.LocalDb/`](ZB.MOM.WW.LocalDb/) | +| UI Theme (layout / tokens / components) | Adopted (feed `0.2.0`–`0.3.1`; **all 4 apps pinned `0.3.1`**, pushed to origin) | Shared `ZB.MOM.WW.Theme` RCL | [`components/ui-theme/`](components/ui-theme/) | [`ZB.MOM.WW.Theme/`](ZB.MOM.WW.Theme/) | +| Health (readiness / liveness / active-node) | **Adopted** (lib `0.1.0`, on the feed; **all 4 apps** reference it + wire `MapZbHealth` — verified 2026-07-18; the prior "not yet adopted" claim was stale) | Shared `ZB.MOM.WW.Health` lib | [`components/health/`](components/health/) | [`ZB.MOM.WW.Health/`](ZB.MOM.WW.Health/) | +| Observability (metrics / traces / logs) | **Adopted** (libs `0.1.0`, on the feed; **all 4 apps** call `AddZbTelemetry`; `AddZbSerilog` in all but ScadaBridge, which keeps its own factory by design) | Shared `ZB.MOM.WW.Telemetry` lib + `.Serilog` | [`components/observability/`](components/observability/) | [`ZB.MOM.WW.Telemetry/`](ZB.MOM.WW.Telemetry/) | +| Config + validation (options / startup validation) | Adopted (lib `0.1.0`, on the feed; **all 4 apps**, pushed — the "local only / not yet pushed" caveat was stale) | Shared `ZB.MOM.WW.Configuration` lib | [`components/configuration/`](components/configuration/) | [`ZB.MOM.WW.Configuration/`](ZB.MOM.WW.Configuration/) | +| Audit (event model + writer seam) | Adopted (lib `0.1.0`, on the feed; **all 4 apps**, pushed to origin) | Shared `ZB.MOM.WW.Audit` lib | [`components/audit/`](components/audit/) | [`ZB.MOM.WW.Audit/`](ZB.MOM.WW.Audit/) | +| Galaxy Repository (object-hierarchy SQL browse + gRPC service) | **Adopted** (feed `0.1.0` + `0.2.0`; consumed at `0.2.0` by HistorianGateway **and mxaccessgw**, whose Server wires `AddZbGalaxyRepository` — verified 2026-07-18; the prior "mxaccessgw adoption is a follow-on" claim was stale) | Shared `ZB.MOM.WW.GalaxyRepository` lib | _(design in histsdk + design doc 2026-06-23)_ | [`ZB.MOM.WW.GalaxyRepository/`](ZB.MOM.WW.GalaxyRepository/) | +| Secrets (encrypted store + `${secret:}` resolution) | Built (libs `0.1.2`, **published to the Gitea feed**; **HistorianGateway adopted + live-proven** 2026-07-16; **mxaccessgw adopted G-4/G-5/G-6 + merged to `origin/main` @ `e088dfa`** 2026-07-16, box-verified fail-closed + encrypt-at-rest; **ScadaBridge adopted G-3/G-4/G-5/G-6 + merged to `origin/main` @ `128f1596`** 2026-07-16, **G-3 live-proven vs the real production MxGateway gateway** `wonder-app-vd03:5120` (secret-ref ApiKey → Connected + browsed real Galaxy); **OtOpcUa adopted G-2/G-4/G-5/G-6 + merged to `origin/master` @ `872cf7e3`** (lmxopcua) 2026-07-16 — the last app, so **all four now adopted**; Layer-B driver secrets (Galaxy API key + OpcUaClient `Password`/`UserCertificatePassword`) resolve fail-closed at driver session-open, `AddZbSecrets` registered unconditionally so driver-only nodes work; **G-2 live-proven vs the real production MxGateway** `wonder-app-vd03:5120` — a `secret:`-ref Galaxy ApiKey resolved through OtOpcUa's real `GalaxyDriverBrowser` → dummy key `MxGatewayAuthenticationException`, real key → browsed the real Galaxy root (10 nodes); temp key minted+revoked+deleted); **G-8 KEK-rotation BUILT (lib bumped `0.1.2`→`0.1.3`, 2026-07-17):** `Rewrap` DEK primitive + CAS-guarded `ApplyRewrapAsync` + `KekRotationService.RewrapAllAsync` + `secret rewrap-all` CLI + operator runbook (`ZB.MOM.WW.Secrets/docs/operations/kek-rotation.md`), full suite green + CLI smoke + adversarial crypto review PASS (TOCTOU closed via compare-and-swap). **G-7 clustered replication DESIGNED + PLANNED** — fork resolved to **build the shared SQL-Server `ISecretStore`** (Akka replicator deferred phase-2), executable plan `docs/plans/2026-07-17-secrets-g7-*`. **Corrected 2026-07-18:** G-8 + the G-7 plan **ARE committed and pushed** on `main` (`d82d345`) — the "not yet committed/pushed" note was stale. The real open gap is that **`0.1.3` is NOT on the feed** (feed tops out at `0.1.2`, where all 4 apps are pinned), so KEK rotation is unconsumable until published; G-7 has no `ISecretStore` SQL implementation yet | Shared `ZB.MOM.WW.Secrets` lib (3 packages + CLI) | [`components/secrets/`](components/secrets/) | [`ZB.MOM.WW.Secrets/`](ZB.MOM.WW.Secrets/) | +| LocalDb (embedded cache + 2-node sync) | Built (3 pkgs `0.1.0`, **published to the Gitea feed** 2026-07-18, restore-verified from a scratch consumer; no app adoption yet) | Shared `ZB.MOM.WW.LocalDb` lib | [`docs/plans/2026-07-17-localdb-design.md`](docs/plans/2026-07-17-localdb-design.md) | [`ZB.MOM.WW.LocalDb/`](ZB.MOM.WW.LocalDb/) | The auth component is fully populated: a normalized [`spec`](components/auth/spec/SPEC.md), a proposed [`shared-contract`](components/auth/shared-contract/ZB.MOM.WW.Auth.md), three @@ -154,7 +159,7 @@ 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/`](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 +215 tests passing + 1 skipped (verified 2026-07-18); feed carries `0.1.0`–`0.1.4`). The implementation plan is at [`docs/plans/2026-06-01-zb-mom-ww-auth-shared-library.md`](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 @@ -176,7 +181,7 @@ backlog. Shared = Technical-Light tokens + IBM Plex fonts + side-rail shell + wi 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/`](ZB.MOM.WW.Theme/) -(.NET 10 Razor Class Library; single package; 44 bUnit tests; `dotnet pack` → 1 nupkg @ 0.2.0, +(.NET 10 Razor Class Library; single package; 48 bUnit tests (verified 2026-07-18); feed carries `0.2.0`–`0.3.1`, **published to the Gitea feed**). The build plan is at [`docs/plans/2026-06-01-zb-mom-ww-theme-shared-library.md`](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`](docs/plans/2026-06-03-ui-theme-adoption.md). @@ -201,8 +206,8 @@ orchestrator wiring, and ScadaBridge's distributed health-monitoring pipeline. The shared library is **built and lives in this repo** at [`ZB.MOM.WW.Health/`](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`](components/health/GAPS.md). +64 tests (verified 2026-07-18); `dotnet pack` → 3 nupkgs @ 0.1.0). +**Adopted by all four apps** — verified 2026-07-18 (each references the package and wires `MapZbHealth`); the previous "not yet adopted" claim was stale. Per-app probe coverage vs the spec has NOT been re-verified — see [`components/health/GAPS.md`](components/health/GAPS.md). Build/test from `ZB.MOM.WW.Health/`: `dotnet test`. Consumer matrix: MxAccessGateway → core only; OtOpcUa & ScadaBridge → all three packages. @@ -217,7 +222,7 @@ backlog. Shared = OTel Resource (service.name/site.id/node.role identity triple) enrichers, and redaction policies. The shared library is **built and lives in this repo** at [`ZB.MOM.WW.Telemetry/`](ZB.MOM.WW.Telemetry/) -(.NET 10; 2 packages — `ZB.MOM.WW.Telemetry`, `ZB.MOM.WW.Telemetry.Serilog`; 19 tests; +(.NET 10; 2 packages — `ZB.MOM.WW.Telemetry`, `ZB.MOM.WW.Telemetry.Serilog`; 34 tests (verified 2026-07-18); `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 @@ -240,7 +245,7 @@ message); left per-project = each app's options classes + domain rules, and OtOp 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/`](ZB.MOM.WW.Configuration/) -(.NET 10; single package `ZB.MOM.WW.Configuration`; 27 tests; `dotnet pack` → 1 nupkg @ 0.1.0). +(.NET 10; single package `ZB.MOM.WW.Configuration`; 42 tests (verified 2026-07-18); `dotnet pack` → 1 nupkg @ 0.1.0). The implementation plan is at [`docs/plans/2026-06-01-zb-mom-ww-configuration-shared-library.md`](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 @@ -265,7 +270,7 @@ principal. `IAuditRedactor` is aligned with Telemetry's `ILogRedactor` seam conv The shared library is **built and lives in this repo** at [`ZB.MOM.WW.Audit/`](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`. +23 tests (verified 2026-07-18); `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 = diff --git a/README.md b/README.md index ac65e54..310a30d 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,18 @@ it produces. | Path | What it is | |---|---| | [`CLAUDE.md`](CLAUDE.md) | High-level index of the sister projects + working guidance | -| [`components/`](components/) | Component-normalization framework (per concern: target spec, current state, gaps) | -| [`components/auth/`](components/auth/) | First normalized component — login / identity / authorization | -| [`components/ui-theme/`](components/ui-theme/) | Second normalized component — UI theme / design tokens / layout | -| [`docs/plans/`](docs/plans/) | Implementation plans (e.g. the ZB.MOM.WW.Auth and ZB.MOM.WW.Theme builds) | -| [`ZB.MOM.WW.Auth/`](ZB.MOM.WW.Auth/) | **Built shared library** (.NET 10) — the realized output of the auth normalization | -| [`ZB.MOM.WW.Theme/`](ZB.MOM.WW.Theme/) | **Built shared RCL** (.NET 10) — the realized output of the UI-theme normalization | +| [`components/`](components/) | Component-normalization framework (per concern: target spec, current state, gaps) — 7 components | +| [`docs/plans/`](docs/plans/) | Implementation + design plans (incl. Galaxy Repository and LocalDb, which have no `components/` entry) | +| [`infra/glauth/`](infra/glauth/) | The shared GLAuth every app's dev/test LDAP points at (`10.100.0.35:3893`) | +| [`ZB.MOM.WW.Auth/`](ZB.MOM.WW.Auth/) | **Built shared library** (.NET 10) — login / identity / API keys | +| [`ZB.MOM.WW.Theme/`](ZB.MOM.WW.Theme/) | **Built shared RCL** (.NET 10) — Technical-Light design system | +| [`ZB.MOM.WW.Audit/`](ZB.MOM.WW.Audit/) | **Built shared library** — canonical audit event + writer seam | +| [`ZB.MOM.WW.Health/`](ZB.MOM.WW.Health/) | **Built shared library** — readiness / liveness / active-node probes | +| [`ZB.MOM.WW.Telemetry/`](ZB.MOM.WW.Telemetry/) | **Built shared library** — OTel metrics / traces + Serilog | +| [`ZB.MOM.WW.Configuration/`](ZB.MOM.WW.Configuration/) | **Built shared library** — options validation + startup preflight | +| [`ZB.MOM.WW.Secrets/`](ZB.MOM.WW.Secrets/) | **Built shared library** — AES-256-GCM secret store + `${secret:}` resolution + CLI | +| [`ZB.MOM.WW.GalaxyRepository/`](ZB.MOM.WW.GalaxyRepository/) | **Built shared library** — Galaxy object-hierarchy SQL browse + gRPC service | +| [`ZB.MOM.WW.LocalDb/`](ZB.MOM.WW.LocalDb/) | **Built shared library** — embedded SQLite cache + 2-node gRPC replication | ## The sister projects @@ -33,6 +39,8 @@ not by a shared build. scadaproj *indexes* them; it does not contain them. MXAccess parity without loading 32-bit COM. The linchpin both others depend on. - **ScadaBridge** — distributed SCADA platform on Akka.NET (hub-and-spoke: one central cluster + N site clusters); its Data Connection Layer ingests OPC UA and MxAccess. +- **HistorianGateway** (`historiangw`) — gRPC sidecar over the AVEVA Historian (read/write) plus + read-only Galaxy browse; no COM, no x86 worker. OtOpcUa's sole historian backend. Data flow: @@ -51,17 +59,27 @@ The sister repos kept re-implementing the same cross-cutting concerns and drifti **code-verified current state**, and a **gaps / adoption backlog**. Convention + workflow in [`components/README.md`](components/README.md). -| Component | Status | Folder | -|---|---|---| -| Auth (login / identity / authz) | Built (library 0.1.0); apps not yet adopted | [`components/auth/`](components/auth/) | -| UI Theme (layout / tokens / components) | Built (RCL 0.1.0); apps not yet adopted | [`components/ui-theme/`](components/ui-theme/) | +Status verified 2026-07-18 against the feed + consumer branches (see [Roadmap](#roadmap)). +"All four" = OtOpcUa, MxAccessGateway, ScadaBridge, HistorianGateway. + +| Component | Feed | Adoption | Folder | +|---|---|---|---| +| Auth (login / identity / authz) | `0.1.0`–`0.1.4` | all four (pins drift `0.1.1`–`0.1.4`) | [`components/auth/`](components/auth/) | +| UI Theme (layout / tokens / components) | `0.2.0`–`0.3.1` | all four @ `0.3.1` | [`components/ui-theme/`](components/ui-theme/) | +| Audit (event model + writer seam) | `0.1.0` | all four (DEEP) | [`components/audit/`](components/audit/) | +| Config + validation | `0.1.0` | all four | [`components/configuration/`](components/configuration/) | +| Observability (metrics / traces / logs) | `0.1.0` | all four | [`components/observability/`](components/observability/) | +| Health (readiness / liveness / active-node) | `0.1.0` | all four | [`components/health/`](components/health/) | +| Secrets (encrypted store + `${secret:}`) | `0.1.0`–`0.1.2` | all four @ `0.1.2` (lib `0.1.3` unpublished) | [`components/secrets/`](components/secrets/) | +| Galaxy Repository (SQL browse + gRPC) | `0.1.0`, `0.2.0` | HistorianGateway + mxaccessgw | _(design in `docs/plans/`)_ | +| LocalDb (embedded cache + 2-node sync) | `0.1.0` | none yet | _(design in `docs/plans/`)_ | ## `ZB.MOM.WW.Theme` — the shared UI kit -The UI-theme component, realized as a single-package .NET 10 Razor Class Library the three -apps can adopt to stop copy-pasting the Technical-Light design system. **Built and tested at -0.1.0; adoption by the apps is the follow-on** (tracked in -[`components/ui-theme/GAPS.md`](components/ui-theme/GAPS.md)). +The UI-theme component, realized as a single-package .NET 10 Razor Class Library that replaced +each app's copy-pasted Technical-Light design system. **Adopted by all four apps, currently on +`0.3.1`** (feed carries `0.2.0`–`0.3.1`); adoption history in +[`components/ui-theme/GAPS.md`](components/ui-theme/GAPS.md). | Asset / Component | Purpose | Used by | |---|---|---| @@ -74,16 +92,16 @@ apps can adopt to stop copy-pasting the Technical-Light design system. **Built a | `LoginCard` | Static form-POST sign-in card | OtOpcUa, ScadaBridge (MxGateway when login page added) | | `TechButton`, `TechCard`, `TechField` | Common controls (Bootstrap 5 wrappers) | all | -**Consumer matrix:** all three apps consume the single `ZB.MOM.WW.Theme` package — -OtOpcUa `AdminUI`, MxAccessGateway `Server`, ScadaBridge `Host` + `CentralUI`. +**Consumer matrix:** all four apps consume the single `ZB.MOM.WW.Theme` package — +OtOpcUa `AdminUI`, MxAccessGateway `Server`, ScadaBridge `Host` + `CentralUI`, HistorianGateway `Server`. ### Build & test ```bash cd ZB.MOM.WW.Theme dotnet build -c Release # 0 warnings (TreatWarningsAsErrors) -dotnet test # 32 bUnit tests -./build/pack.sh # → ./artifacts/ZB.MOM.WW.Theme.0.1.0.nupkg +dotnet test # 48 bUnit tests (verified 2026-07-18) +./build/pack.sh # → ./artifacts/ZB.MOM.WW.Theme..nupkg (feed is at 0.3.1) ``` Stack: .NET 10 · Razor Class Library · bUnit · xUnit · central package management. @@ -93,9 +111,10 @@ More detail: [`ZB.MOM.WW.Theme/README.md`](ZB.MOM.WW.Theme/README.md). ## `ZB.MOM.WW.Auth` — the shared library -The auth component, realized as a four-package .NET 10 library the three apps can adopt to stop -re-implementing authentication. **Built and tested at 0.1.0; adoption by the apps is the -follow-on** (tracked in [`components/auth/GAPS.md`](components/auth/GAPS.md)). +The auth component, realized as a four-package .NET 10 library that replaced each app's +re-implemented authentication. **Adopted by all four apps** — pins drift deliberately +(OtOpcUa `0.1.1`, ScadaBridge `0.1.3`, MxAccessGateway + HistorianGateway `0.1.4`); history in +[`components/auth/GAPS.md`](components/auth/GAPS.md). | Package | Purpose | Used by | |---|---|---| @@ -105,7 +124,7 @@ follow-on** (tracked in [`components/auth/GAPS.md`](components/auth/GAPS.md)). | `ZB.MOM.WW.Auth.AspNetCore` | Canonical claim types, cookie defaults, LDAP DI helpers | all (web UIs) | **Consumer matrix:** OtOpcUa → `Abstractions + Ldap + AspNetCore` (no ApiKeys / SQLite); -MxAccessGateway & ScadaBridge → all four. +MxAccessGateway, ScadaBridge & HistorianGateway → all four packages. Identity (LDAP / GLAuth, the canonical role set) is normalized; each app keeps its own **authorization enforcement** (OPC-UA permissions / gRPC scopes / roles + site-scoping) and maps @@ -117,8 +136,8 @@ and [`CANONICAL-ROLES.md`](components/auth/spec/CANONICAL-ROLES.md). ```bash cd ZB.MOM.WW.Auth dotnet build -c Release # 0 warnings -dotnet test # 172 passing, 1 skipped (opt-in LDAP integration test) -dotnet pack -c Release -o ./artifacts # → 4 nupkgs @ 0.1.0 +dotnet test # 215 passing, 1 skipped (opt-in LDAP integration test) — verified 2026-07-18 +dotnet pack -c Release -o ./artifacts # → 4 nupkgs (feed is at 0.1.4) ``` Stack: .NET 10 · xUnit · `Novell.Directory.Ldap.NETStandard` · `Microsoft.Data.Sqlite` · central @@ -133,10 +152,33 @@ ZB_LDAP_IT=1 dotnet test # requires a reachable GLAuth (e.g. a sister repo's i ## Roadmap -- ✅ Auth component normalized (spec + canonical roles + current-state + gaps). -- ✅ `ZB.MOM.WW.Auth` shared library built and tested (0.1.0). -- ⬜ Adopt `ZB.MOM.WW.Auth` in OtOpcUa, MxAccessGateway, ScadaBridge — [`components/auth/GAPS.md`](components/auth/GAPS.md) (#8). -- ✅ UI-theme component normalized (spec + design tokens + current-state + gaps). -- ✅ `ZB.MOM.WW.Theme` shared UI kit built and tested (0.1.0); apps not yet adopted. -- ⬜ Adopt `ZB.MOM.WW.Theme` in OtOpcUa [low risk], ScadaBridge [med], MxAccessGateway [high risk] — [`components/ui-theme/GAPS.md`](components/ui-theme/GAPS.md). +> **Verified 2026-07-18** against the Gitea feed (`nuget/query?q=ZB.MOM.WW`) and the actual +> `PackageReference`s on each consumer's default branch — not from status claims in docs. +> Per-component detail in each `components//GAPS.md`. + +**Shipped** + +- ✅ Auth normalized + `ZB.MOM.WW.Auth` built (feed: `0.1.0`–`0.1.4`) and **adopted by all four apps**. + Version pins drift on purpose: OtOpcUa `0.1.1`, ScadaBridge `0.1.3`, MxAccessGateway + HistorianGateway `0.1.4`. +- ✅ UI Theme normalized + `ZB.MOM.WW.Theme` built (feed: `0.2.0`–`0.3.1`); **all four apps on `0.3.1`**. +- ✅ Audit — `ZB.MOM.WW.Audit` `0.1.0` on the feed; adopted by all four (DEEP: canonical record everywhere). +- ✅ Configuration — `ZB.MOM.WW.Configuration` `0.1.0` on the feed; adopted by all four. +- ✅ Observability — `ZB.MOM.WW.Telemetry` + `.Serilog` `0.1.0` on the feed; adopted by all four. +- ✅ Health — `ZB.MOM.WW.Health` (+ `.Akka`, `.EntityFrameworkCore`) `0.1.0` on the feed; **adopted by all four** + (`MapZbHealth` wired in each; previously documented as "not yet adopted" — that was stale). +- ✅ Galaxy Repository — `ZB.MOM.WW.GalaxyRepository` `0.2.0` on the feed; consumed by HistorianGateway + **and mxaccessgw** (which wires `AddZbGalaxyRepository` in its Server; previously documented as a + pending follow-on — also stale). +- ✅ Secrets — `ZB.MOM.WW.Secrets` (+ `.Abstractions`, `.Ui`) published through `0.1.2`; all four apps on `0.1.2`. +- ✅ LocalDb — `ZB.MOM.WW.LocalDb` (+ `.Contracts`, `.Replication`) built at `0.1.0` and **published to the + feed 2026-07-18** (restore-verified from a scratch consumer). + +**Open** + +- ⬜ Publish `ZB.MOM.WW.Secrets` `0.1.3` (KEK rotation — built, feed still tops out at `0.1.2`) and execute + the G-7 clustered-replication plan — [`components/secrets/GAPS.md`](components/secrets/GAPS.md). +- ⬜ Adopt `ZB.MOM.WW.LocalDb` in an app — no consumer references it yet. +- ⬜ Give LocalDb a `components/localdb/` entry; it is the one shared lib whose design lives only in + [`docs/plans/2026-07-17-localdb-design.md`](docs/plans/2026-07-17-localdb-design.md). +- ⬜ Converge the Auth version pins (OtOpcUa is three minors behind at `0.1.1`). - ⬜ Normalize the next cross-cutting component. diff --git a/ZB.MOM.WW.LocalDb/README.md b/ZB.MOM.WW.LocalDb/README.md index cf5440c..4679daf 100644 --- a/ZB.MOM.WW.LocalDb/README.md +++ b/ZB.MOM.WW.LocalDb/README.md @@ -18,7 +18,7 @@ last-writer-wins over a hybrid logical clock. All timestamps are UTC. | `ZB.MOM.WW.LocalDb.Contracts` | `localdb_sync.v1` proto + generated gRPC types (packable; a future peer could be non-.NET). | | `ZB.MOM.WW.LocalDb.Replication` | gRPC sync engine: passive `LocalDbSyncService`, initiating `SyncBackgroundService`, `MaintenanceBackgroundService`, LWW apply, watermark acks + pruning, snapshot resync, `ISyncStatus`, `LocalDbMetrics`, `AddZbLocalDbReplication()` / `MapZbLocalDbSync()`. | -> Status: **built, not yet published to the Gitea feed; no app has adopted it yet.** +> Status: **built and published to the Gitea feed at `0.1.0` (2026-07-18); no app has adopted it yet.** ## Quickstart diff --git a/ZB.MOM.WW.LocalDb/build/push.sh b/ZB.MOM.WW.LocalDb/build/push.sh new file mode 100755 index 0000000..17fde8a --- /dev/null +++ b/ZB.MOM.WW.LocalDb/build/push.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# push.sh — pack and push the ZB.MOM.WW.LocalDb NuGet packages (core, Contracts, Replication) to the Gitea feed. +# +# Required environment variables: +# GITEA_NUGET_SOURCE — full URL of the Gitea NuGet feed +# e.g. https://gitea.dohertylan.com/api/packages/dohertj2/nuget/index.json +# GITEA_NUGET_KEY — Gitea access token with package:write permission +# +# Usage: +# export GITEA_NUGET_SOURCE="https://gitea.dohertylan.com/api/packages/dohertj2/nuget/index.json" +# export GITEA_NUGET_KEY="your-gitea-token" +# ./build/push.sh + +set -euo pipefail + +: "${GITEA_NUGET_SOURCE:?set GITEA_NUGET_SOURCE to your Gitea NuGet feed URL}" +: "${GITEA_NUGET_KEY:?set GITEA_NUGET_KEY to your Gitea access token}" + +dotnet pack -c Release -o ./artifacts + +dotnet nuget push "./artifacts/*.nupkg" \ + --source "$GITEA_NUGET_SOURCE" \ + --api-key "$GITEA_NUGET_KEY" \ + --skip-duplicate diff --git a/components/audit/GAPS.md b/components/audit/GAPS.md index 713c508..cd33fc1 100644 --- a/components/audit/GAPS.md +++ b/components/audit/GAPS.md @@ -3,6 +3,10 @@ Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to reach the shared `ZB.MOM.WW.Audit` library. Status legend: ⛔ gap · 🟡 partial · ✅ matches. +> **Current state (verified 2026-07-18):** `ZB.MOM.WW.Audit` `0.1.0` is on the Gitea feed and +> **all four** apps reference it — **HistorianGateway is a fourth consumer** this file predates. +> Library suite: **23 tests**, green 2026-07-18. +> > **✅ ADOPTED 2026-06-02 (local-only) — DEEP.** The backlog (#1–#6) was implemented across all three apps on each repo's > **`feat/adopt-zb-audit`** branch (stacked on `feat/adopt-zb-auth`) — committed + spec/code-reviewed, then **merged to > each repo's local default (main/master) and PUSHED to origin (gitea) on 2026-06-03** (in sync). The user chose **DEEP adopt**: diff --git a/components/auth/GAPS.md b/components/auth/GAPS.md index 59f22fa..0b3edf3 100644 --- a/components/auth/GAPS.md +++ b/components/auth/GAPS.md @@ -3,6 +3,12 @@ Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to reach the shared `ZB.MOM.WW.Auth` library. Status legend: ⛔ gap · 🟡 partial · ✅ matches. +> **Current state (verified 2026-07-18):** feed carries `0.1.0`–`0.1.4`; consumer pins are +> OtOpcUa `0.1.1`, ScadaBridge `0.1.3`, MxAccessGateway `0.1.4`, **HistorianGateway `0.1.4`** +> (a fourth consumer this file predates, taking all four packages). OtOpcUa is three minors +> behind — it does not get the `0.1.4` API-key-expiry enforcement. Library suite: **215 passing, +> 1 skipped**, green 2026-07-18. +> > **✅ ADOPTED 2026-06-02 (local-only).** The full backlog (#1–#8) was implemented across all three apps on each repo's > **`feat/adopt-zb-auth`** branch — committed + spec/code-reviewed, then **merged to each repo's local default > (main/master) and PUSHED to origin (gitea) on 2026-06-03** (in sync; `feat/*` kept locally). Shared diff --git a/components/configuration/GAPS.md b/components/configuration/GAPS.md index 50eabb0..db04e78 100644 --- a/components/configuration/GAPS.md +++ b/components/configuration/GAPS.md @@ -1,10 +1,13 @@ # Configuration validation — gaps & adoption backlog Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to adopt -the shared `ZB.MOM.WW.Configuration` library. The library is **BUILT @ 0.1.0** (42 tests) at +the shared `ZB.MOM.WW.Configuration` library. The library is **BUILT @ 0.1.0** (42 tests, re-run +green 2026-07-18) at [`../../ZB.MOM.WW.Configuration/`](../../ZB.MOM.WW.Configuration/) and was **ADOPTED across all three -apps on 2026-06-01** — published to the Gitea feed, then consumed on each repo's local default branch -(merged, **not yet pushed** to remotes). The adoption items below are now largely closed: MxGateway + +apps on 2026-06-01** — published to the Gitea feed, then consumed on each repo's local default branch. +**Re-verified 2026-07-18:** `0.1.0` is on the feed and **all four** apps reference it (HistorianGateway +too, which this file predates) on their pushed default branches — the earlier "merged, **not yet pushed** +to remotes" caveat is stale and has been removed. The adoption items below are now largely closed: MxGateway + ScadaBridge migrated to `OptionsValidatorBase`/`AddValidatedOptions` behaviour-preservingly (validator messages byte-identical), ScadaBridge's `StartupValidator` → `ConfigPreflight`, and OtOpcUa gained net-new `Ldap`/`OpcUa` validators (plus a follow-on pass: real `Security:Ldap` binding, `ValidateOnStart` diff --git a/components/health/GAPS.md b/components/health/GAPS.md index fd04cea..df5590a 100644 --- a/components/health/GAPS.md +++ b/components/health/GAPS.md @@ -3,6 +3,20 @@ Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to reach the shared `ZB.MOM.WW.Health` library. Status legend: ⛔ gap · 🟡 partial · ✅ matches. +> **✅ ADOPTED — verified 2026-07-18.** This file previously carried no adoption banner and the +> root docs claimed the library was "**not yet adopted**". That was stale. Verified directly: +> `ZB.MOM.WW.Health` `0.1.0` is on the Gitea feed, and **all four apps reference it and wire +> `MapZbHealth`** — OtOpcUa (+ `.Akka` + `.EntityFrameworkCore`, uses `AkkaClusterHealthCheck`), +> MxAccessGateway (core only), ScadaBridge (all three packages; uses `AkkaClusterHealthCheck` + +> `IActiveNodeGate`), HistorianGateway (core; uses `GrpcDependencyHealthCheck`). +> +> **Caveat on the evidence:** this was verified by `PackageReference` + registration-call grep, +> which proves the library is wired in — *not* that every per-app probe below is registered or +> that the endpoint tiers match the spec. The divergence tables further down were written +> pre-adoption and have **not** been re-verified against current code; treat them as historical +> until someone re-walks them. They also predate HistorianGateway, which is absent from every +> table below. + ## Divergence vs spec ### §1 Endpoint tiers diff --git a/components/observability/GAPS.md b/components/observability/GAPS.md index f127d3b..0864b8d 100644 --- a/components/observability/GAPS.md +++ b/components/observability/GAPS.md @@ -3,6 +3,19 @@ Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to reach the shared `ZB.MOM.WW.Telemetry` library. Status legend: ⛔ gap · 🟡 partial · ✅ matches. +> **✅ ADOPTED — verified 2026-07-18.** `ZB.MOM.WW.Telemetry` + `.Serilog` `0.1.0` are on the Gitea +> feed and **all four apps reference both and call `AddZbTelemetry`** (Resource + standard +> instrumentation + Prometheus `/metrics`). Logging: OtOpcUa, MxAccessGateway and **HistorianGateway** +> (a fourth consumer this file predates) use `AddZbSerilog`; ScadaBridge deliberately keeps its own +> `LoggerConfigurationFactory` for min-level governance and adds only the shared `TraceContextEnricher` +> — that split is by design, not a gap. Library suite: **34 tests**, green 2026-07-18. +> +> **The `⛔ not set` divergence tables below are the pre-adoption 2026-06-01 snapshot** — the §1 +> Resource-attribute gaps they describe are what `AddZbTelemetry` closed. Read them as the +> historical case for the library, not as current state. Deferred items noted at the time +> (MxGateway `ms`→`s` + Meter rename, ScadaBridge app instruments + Site-node HTTP/1.1 metrics +> listener, OTLP wiring) were **not** re-verified in this pass. + ## Divergence vs spec ### §1 OTel Resource / `service.name` (P1 — nobody has it) diff --git a/components/secrets/GAPS.md b/components/secrets/GAPS.md index 9068a55..9fc5e12 100644 --- a/components/secrets/GAPS.md +++ b/components/secrets/GAPS.md @@ -5,6 +5,28 @@ The delta between each project's [`current-state`](current-state/) and the reference-consumer-proven**; everything here is per-app adoption of an existing lib (not lib construction). +## Current state (verified 2026-07-18) + +- **Source is at `0.1.3`; the feed is at `0.1.2`.** `ZB.MOM.WW.Secrets/Directory.Build.props` + declares `0.1.3` and the G-8 KEK-rotation work (`KekRotationService`, + `RewrapReport`, `secret rewrap-all`, `docs/operations/kek-rotation.md`) is **committed and + pushed** on `main` (`d82d345`). The Gitea feed carries only `0.1.0`–`0.1.2` for all three + packages. **→ `0.1.3` is unpublished: no app can consume KEK rotation yet.** (An earlier note + claiming G-8 was "not yet committed/pushed" was stale — it is committed; only the *publish* + is outstanding.) +- **All four apps are pinned to `0.1.2`** — OtOpcUa, MxAccessGateway, ScadaBridge take + `Secrets` + `.Abstractions` + `.Ui`; HistorianGateway takes `Secrets` + `.Ui`. +- **G-7 clustered replication is designed + planned, not built** — plan at + `docs/plans/2026-07-17-secrets-g7-*` (resolved to build the shared SQL-Server `ISecretStore`; + Akka replicator deferred to phase 2). No `ISecretStore` SQL-Server implementation exists yet. +- Library suite: **97 passing, 1 skipped**, green 2026-07-18. + +### Open + +- ⬜ Publish `ZB.MOM.WW.Secrets` `0.1.3` (+ `.Abstractions`, `.Ui`) to the Gitea feed. +- ⬜ Execute the G-7 plan (shared SQL-Server `ISecretStore`). +- ⬜ Per-app live wonder gates for the G-8 rotation path. + ## Execution status (2026-07-16) - **mxaccessgw — G-4 + G-5 + G-6 DONE.** Executed via the plan below (8 tasks, subagent-driven, diff --git a/components/ui-theme/GAPS.md b/components/ui-theme/GAPS.md index b02725e..931ab26 100644 --- a/components/ui-theme/GAPS.md +++ b/components/ui-theme/GAPS.md @@ -3,6 +3,12 @@ Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to reach adoption of the `ZB.MOM.WW.Theme` shared RCL. Status legend: ⛔ gap · 🟡 partial · ✅ matches. +> **Current state (verified 2026-07-18):** the kit has moved on from the `0.2.0` this banner +> describes — the Gitea feed carries `0.2.0`, `0.2.1`, `0.3.0`, `0.3.1`, and **all four apps are +> pinned to `0.3.1`** (HistorianGateway is a fourth consumer this file predates). The `0.3.0`/`0.3.1` +> nav/aria/login fixes are recorded in `themeissues.md`, not here. Library suite: **48 bUnit tests**, +> green 2026-07-18. History below is accurate for the original cutover. +> > **✅ ADOPTED 2026-06-03 (local-only).** Backlog #2–#4 implemented across all three apps on each repo's > **`feat/adopt-zb-theme`** branch — full canonical cutover (SPEC §7): ``/``, > thin `MainLayout` → `` + `NavRailItem`/`NavRailSection`, per-app `theme.css`/IBM-Plex fonts/