Commit Graph

113 Commits

Author SHA1 Message Date
Joseph Doherty da669bfc9b fix(auth.apikeys): stamp schema version 2 to match donor gateway DBs; bump 0.1.2
The store was extracted from MxAccessGateway, whose deployed gateway-auth.db
is at schema_version=2. The library capped at 1 and threw on a newer on-disk
version -> gateway would fail to boot. Final schema is byte-identical since v1;
stamp 2 so existing deployed DBs interoperate (no key re-issuance). +2 tests.
2026-06-02 01:45:57 -04:00
Joseph Doherty 2d50d5dcf0 plan(phase1): 1.2/1.4 done across 3 repos (lib 0.1.1); remaining 1.3/1.5-1.7 2026-06-02 01:38:50 -04:00
Joseph Doherty aecc106657 fix(auth.ldap): skip LdapOptionsValidator when Enabled=false; bump 0.1.1
A disabled LDAP provider's connection fields are inert — don't require
Server/SearchBase/ServiceAccountDn at startup when Enabled=false. Surfaced
by the MxGateway 1.2 review (dashboard LDAP can be disabled). +1 test.
2026-06-02 01:17:53 -04:00
Joseph Doherty 0586e64f64 plan(phase1): record Task 1.2 review findings + LdapOptionsValidator 0.1.1 question 2026-06-02 01:12:20 -04:00
Joseph Doherty 37c03e5fc2 plan(phase1): note Roles sub-namespace; Task 1.1 done+approved (3 repos) 2026-06-02 00:34:13 -04:00
Joseph Doherty bea08f9673 plan(phase1): lock resolved decisions (SB ApiKeys full adopt, roles, dev hatches) 2026-06-02 00:25:53 -04:00
Joseph Doherty 32fd953969 plan(phase1): Task 1.0 exploration findings + elaborated Auth cutover
Per-app cutover steps mapped to the library surface; flags 5 findings that
change the plan (OtOpcUa section is Security:Ldap not Authentication:Ldap;
singleton 'bug' already mitigated; ScadaBridge inbound API keys are a
re-architecture not a reformat; OtOpcUa config+DB mapping + DevStubMode +
2nd LDAP consumer; MxGateway ApiKeys is the low-risk donor path).
2026-06-02 00:24:03 -04:00
Joseph Doherty c715565bd2 build(audit): add Gitea push.sh mirroring Auth's 2026-06-02 00:13:24 -04:00
Joseph Doherty f98fa84e4a plan: implementation plan + task graph for Auth+Audit normalization
Phase 0 command-exact (publish + feed-map); Phases 1-3 decomposed into
bite-sized cutover tasks with files-to-edit contracts, classification,
parallelizability, and per-phase explore/elaborate gates. Co-located
.tasks.json mirrors native tasks #7-#31.
2026-06-02 00:11:48 -04:00
Joseph Doherty 6ec1ea7d65 docs: design for full Auth+Audit normalization across 3 sister projects
Approved brainstorming output: two-library program (publish + adopt
ZB.MOM.WW.Auth then ZB.MOM.WW.Audit across OtOpcUa, MxAccessGateway,
ScadaBridge), library-major waterfall, ending with audit Actor wired
from the Auth principal. Local-only delivery; verified feed/source state.
2026-06-02 00:04:33 -04:00
Joseph Doherty c3ab37523a docs: record ZB.MOM.WW.Configuration fleet-wide adoption + add design/plan
Configuration is now adopted across all three sister apps (local branches),
so flip the status lines in CLAUDE.md, components/configuration/GAPS.md, and the
lib README/CLAUDE.md from 'not adopted' to adopted (also corrects 27->42 tests).
Adds the brainstorm design doc + bite-sized implementation plan (+tasks.json)
under docs/plans/ that drove the adoption.
2026-06-01 23:18:02 -04:00
Joseph Doherty 2f124fa02c docs(observability): record telemetry follow-ons DONE (metric normalization, ScadaBridge instruments, OTLP opt-in, site metrics listener, Serilog alignment) 2026-06-01 17:16:46 -04:00
Joseph Doherty 6c2a43a238 docs: plan for ZB.MOM.WW.Telemetry follow-ons (A additive/hygiene, B metric normalization, C ScadaBridge instruments, D OTLP opt-in) 2026-06-01 16:32:57 -04:00
Joseph Doherty dee55aadc6 docs(observability): record ZB.MOM.WW.Telemetry adoption across 3 apps; correct false MxGateway logging-status claim
All 3 apps adopted on branch feat/adopt-zb-telemetry (behaviour-preserving).
Records the per-repo result + accepted scope deviations (ScadaBridge keeps
LoggerConfigurationFactory + TraceContextEnricher instead of AddZbSerilog;
MxGateway keeps GatewayLogScope, exposes redaction via ILogRedactor seam) and
deferred follow-ons (#6 ms->s, #7 meter rename, #9 app instruments, OTLP, and
the new ScadaBridge Site-node HTTP/1.1 metrics-listener item). Corrects the
prior false 'MxGateway logging adopted on its own branch' claim — that migration
actually landed in this pass.
2026-06-01 15:58:10 -04:00
Joseph Doherty 30425726d4 docs: implementation plan for ZB.MOM.WW.Telemetry adoption across the 3 sister apps
13 tasks: Task 0 publishes/verifies the 2 nupkgs on Gitea (gates all); then 3
independent per-repo phases — OtOpcUa (1-3), ScadaBridge (4-6), MxGateway (7-11,
incl. the high-risk MEL->Serilog swap) — and Task 12 scadaproj bookkeeping last.
Records two behaviour-preserving refinements vs the design: ScadaBridge keeps
LoggerConfigurationFactory (+TraceContextEnricher) instead of AddZbSerilog, and
MxGateway keeps GatewayLogScope as-is. Breaking items #6/#7 deferred.
2026-06-01 15:24:28 -04:00
Joseph Doherty 3729ff2152 docs: design for ZB.MOM.WW.Telemetry adoption across the 3 sister apps
Second cross-fleet shared-library adoption (after Health). Full scope:
AddZbTelemetry (OTel Resource identity triple + standard instrumentation +
Prometheus /metrics) on all 3, plus shared Serilog on all 3 — including the
MxGateway MEL->Serilog migration. Records the correction that MxGateway's
logging was NOT actually adopted on main despite the docs' claim. Behaviour-
preserving bar; breaking items (#6 unit, #7 rename) deferred.
2026-06-01 15:11:50 -04:00
Joseph Doherty 19f7ea5eeb docs(health): record ZB.MOM.WW.Health adoption across 3 apps + deferrals + accepted /health/active startup behaviour change 2026-06-01 13:50:09 -04:00
Joseph Doherty 1e91784ba3 docs(health-plan): publish done; fix source-mapping (two patterns); note user-level creds 2026-06-01 13:23:46 -04:00
Joseph Doherty 5a965639f9 docs: implementation plan for ZB.MOM.WW.Health adoption across the 3 sister apps
Detailed task-by-task plan (publish to Gitea, then per-repo behaviour-preserving
probe swaps) incorporating recon findings that revised the design: MxGateway worker
IPC is named pipes (custom SQLite readiness probe instead of gRPC), ScadaBridge
ActorSystem is not in DI (transient bridge), downstream gRPC probes + IDbContextFactory
switch + ScadaBridge seam unification deferred.
2026-06-01 13:15:48 -04:00
Joseph Doherty f72403d6f0 docs: design for ZB.MOM.WW.Health adoption across the 3 sister apps
Plan to integrate the built-but-unadopted Health library into OtOpcUa,
MxAccessGateway, and ScadaBridge: Gitea-registry distribution, per-repo
behaviour-preserving probe swaps (preset-based), canonical tiers + writer,
MxGateway-first sequencing.
2026-06-01 13:01:36 -04:00
Joseph Doherty f47d4e1030 docs: remove upcoming.md (remaining normalization candidates won't be standardized) 2026-06-01 12:42:51 -04:00
Joseph Doherty 7ae25f8510 Re-stamp Telemetry-002/003 resolutions: nested redaction implemented in 05cc62a
Telemetry-002 was first resolved by documenting the scalar-only limitation; it is now
implemented (recursive nested redaction). Updated the two resolution notes to record
05cc62a and the replaced limitation test, preserving the audit trail. README unchanged
(still 0 pending / 35 total).
2026-06-01 12:13:05 -04:00
Joseph Doherty 05cc62aab3 Implement nested log redaction (Telemetry-002)
RedactionEnricher now projects each property into a mutable view the ILogRedactor
can edit: scalars stay as their CLR value, while StructureValue/SequenceValue/
DictionaryValue become nested IDictionary<string,object?>/IList<object?> the
redactor descends into recursively. A field nested inside a destructured {@Object}
can now be masked or removed — closing the gap documented as a limitation.

- Project/Rebuild round-trip preserves StructureValue.TypeTag and original
  dictionary keys; redactor-synthesised plain dicts/lists are rebuilt too.
- Untouched properties are not reallocated: structural ValueEquals skips write-back
  unless a property actually changed. Scalar fast path and no-redactor/no-property
  short-circuits retained.
- +5 nested-reach tests (mask/remove a field, sequence element, dictionary value,
  two-levels-deep); the old 'cannot reach' limitation test replaced. Serilog 34, 0 warnings.
- ILogRedactor XML doc + library README updated to document the recursive reach.
2026-06-01 12:12:26 -04:00
Joseph Doherty ae0ccc9a3a Mark all baseline code-review findings resolved
All 35 findings fixed in 544a6dd and marked Status: Resolved with resolution
notes. README regenerated: 0 pending / 35 total across 6 libraries.
2026-06-01 11:22:37 -04:00
Joseph Doherty 544a6ddb77 Fix all baseline code-review findings across the six shared libraries
Resolves the 35 findings from the 2026-06-01 baseline (commit 26ba1c7),
test-first for every behavioral change. +51 tests (331 -> 382 passing, 0 failed).

- Telemetry-001 (HIGH): RedactionEnricher now honours property removal, so a
  redactor that drops a key actually scrubs the secret from the event.
- Auth: LDAP validator ValidateOnStart; API-key verify no longer fails on a
  best-effort MarkUsed write or a corrupt scopes column (fail-closed); LDAP cert
  validation hook; KeyPrefix persistence aligned; README algorithm corrected.
- Health: Akka checks return Degraded (not throw) when the cluster isn't up yet;
  GrpcDependencyHealthCheck catch-all; null 'description' rendered; composite
  endpoint builder; XML docs shipped.
- Audit: CompositeAuditWriter no longer re-throws OperationCanceledException;
  TruncatingAuditRedactor over-redact scrubs Target + safe negative max; options
  record; XML docs shipped.
- Configuration: TryAddEnumerable idempotent registration; consistent port
  quoting; strict invariant port parsing; XML docs + README packaged.
- Theme: mobile toggle is now CSS-only (no Bootstrap JS); token/CSS hygiene;
  XML docs on the public parameter surface.

Shared-contract/spec docs updated where the code was the source of truth
(observability service.instance.id, MapZbMetrics, redactor reach). All changes
additive/back-compatible at v0.1.0. code-reviews bookkeeping follows separately.
2026-06-01 11:22:14 -04:00
Joseph Doherty 26ba1c7215 Baseline code review of the six ZB.MOM.WW.* shared libraries
All six libraries reviewed at commit 5f75cd4 against their components/ specs,
following code-reviews/REVIEW-PROCESS.md. 35 findings (0 Critical, 1 High,
9 Medium, 25 Low); none block adoption.

- Auth      0/0/3/3  (security core sound; startup-validation + key-verify contract gaps)
- Telemetry 0/1/2/5  (HIGH Telemetry-001: redactor 'remove' is a no-op -> secrets reach sinks)
- Health    0/0/2/4  (Akka checks throw instead of Degraded when cluster not yet up)
- Theme     0/0/1/5  (undocumented Bootstrap-collapse JS dep; token/CSS hygiene)
- Audit     0/0/1/4  (composite re-throws OCE vs never-throw writer contract)
- Configuration 0/0/0/4 (DI idempotency, port-parse strictness, packaging)

Cross-cutting: XML docs authored but GenerateDocumentationFile unset -> docs
not shipped in any nupkg (Auth/Health/Telemetry/Configuration/Audit).

README.md regenerated from the per-library findings; regen-readme.py --check passes.
2026-06-01 11:08:12 -04:00
Joseph Doherty 5f75cd4dab Add per-library code-review scaffolding for the ZB.MOM.WW.* shared libs
Adapts the code-reviews convention (process, README generator, template) from
the ScadaBridge app model (per-src/-module, Akka conventions) to scadaproj's
reality: six shared libraries reviewed against their components/ specs.

- REVIEW-PROCESS.md: review unit is a library; library->component-spec mapping;
  checklist re-targeted for reusable .NET libs (public API/semver, packaging &
  dependency hygiene, spec/shared-contract adherence) instead of actor/supervision.
- _template/findings.md: library/packages/component-spec/shared-contract header.
- regen-readme.py: per-library prose, data-driven Summary, '-' for unreviewed.
- Seed Auth/Theme/Health/Telemetry/Configuration/Audit findings stubs (0 findings).
- README.md generated; --check passes.
2026-06-01 10:46:16 -04:00
Joseph Doherty 899efc2cbf Merge fix/config-otopcua-draft-accuracy: correct OtOpcUa draft-validation description
DraftValidator is dormant (no src/ caller); enforcement is DB-side sp_ValidateDraft.
2026-06-01 10:13:34 -04:00
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
Joseph Doherty e47ecacb0d Merge feat/zb-mom-ww-configuration: Configuration normalization component + ZB.MOM.WW.Configuration (0.1.0)
Shared startup-options-validation library (single package, 27 tests) — OptionsValidatorBase,
ValidationBuilder primitives, AddValidatedOptions (ValidateOnStart), and pre-host ConfigPreflight
(byte-compatible with ScadaBridge's StartupValidator). Plus components/configuration normalization
docs (spec, shared-contract, 3x current-state, GAPS) and index registration. Not yet adopted by
the three apps — adoption tracked in components/configuration/GAPS.md.
2026-06-01 09:58:08 -04:00
Joseph Doherty 69fb6cb077 chore: mark configuration plan tasks complete 2026-06-01 09:56:01 -04:00
Joseph Doherty a29f226a70 docs: list Checks.cs in library CLAUDE.md src tree 2026-06-01 09:55:47 -04:00
Joseph Doherty 3fa77b70fc docs: register ZB.MOM.WW.Configuration in indexes 2026-06-01 09:51:22 -04:00
Joseph Doherty 46c4bfae31 docs(config): components/configuration normalization (spec, shared-contract, current-state x3, GAPS, README) 2026-06-01 09:48:49 -04:00
Joseph Doherty b754873a44 docs: README + CLAUDE.md; verify 0.1.0 pack
ZB.MOM.WW.Configuration — README with purpose, what's-in-the-box,
three usage snippets (validator subclass, DI wiring, ConfigPreflight),
build/test/pack instructions, and dependency note.
CLAUDE.md with one-screen orientation: package table, commands,
source layout, and component-normalization status note.
27 tests pass; dotnet pack produces exactly one nupkg (0.1.0).
2026-06-01 09:40:20 -04:00
Joseph Doherty 8d91a3021d fix(config): centralize port wording, harden HostPort/key guards, doc null/singleton semantics, add tests 2026-06-01 09:37:53 -04:00
Joseph Doherty 8145d79dc6 feat: ConfigPreflight raw-config aggregator 2026-06-01 09:32:44 -04:00
Joseph Doherty e191893738 feat: AddValidatedOptions bind+validate+ValidateOnStart 2026-06-01 09:31:14 -04:00
Joseph Doherty 563cf44c60 feat: OptionsValidatorBase<TOptions> 2026-06-01 09:29:46 -04:00
Joseph Doherty d18c121033 feat: Checks primitives + ValidationBuilder 2026-06-01 09:28:19 -04:00
Joseph Doherty a104372eac chore: scaffold ZB.MOM.WW.Configuration solution 2026-06-01 09:25:26 -04:00
Joseph Doherty 80e4d59209 plan(config): correct git layout — library committed to outer repo, no nested .git
The sibling libs (Auth/Theme/Health/Telemetry) are tracked as regular files in
the outer scadaproj repo, not separate git repos. Remove the git-init/nested-repo
instructions; all commits target the outer repo on feat/zb-mom-ww-configuration.
2026-06-01 09:23:08 -04:00
Joseph Doherty 229b82efbc plan(config): ZB.MOM.WW.Configuration implementation plan (9 tasks, TDD)
Folds the approved design into the sibling combined-doc format and adds the
phased, bite-sized TDD implementation plan: normalization docs (T1-2), library
scaffold + 4 public types via TDD (T3-7), pack + register (T8-9). Co-located
.tasks.json for executing-plans resume.
2026-06-01 09:18:23 -04:00
Joseph Doherty 18e4b70572 docs(plans): design ZB.MOM.WW.Configuration shared startup-options-validation library
Approved brainstorming design for the Config + validation normalization pass
(Tier-2 candidate in upcoming.md). Scope: startup options validation only,
single package ZB.MOM.WW.Configuration, Approach A (lightweight base + rule
primitives + DI/startup helpers). Full pass = components/configuration/ docs +
built library.
2026-06-01 09:10:35 -04:00
Joseph Doherty a09cc02d46 Merge feat/zb-mom-ww-audit: Audit normalization component + ZB.MOM.WW.Audit (0.1.0)
# Conflicts:
#	CLAUDE.md
#	components/README.md
2026-06-01 09:09:44 -04:00
Joseph Doherty 88c557dee8 fix(telemetry): identical resource across all 3 signals (symmetric OTLP trigger + deterministic service.instance.id)
Fix 1 — symmetric OTLP trigger: ZbSerilogConfig.ApplyOpenTelemetryExport now activates only
when options.Exporter == ZbExporter.Otlp, matching the core OTel metrics/traces path. The
previous fallback that also triggered on a bare OtlpEndpoint is removed; OtlpEndpoint is the
address to use when Otlp is selected, not an independent enable.

Fix 2 — deterministic service.instance.id: ZbResource.InstanceId (MachineName:ProcessId) is
a new public property that produces a stable, process-unique id without a random GUID.
ZbResource.Configure passes autoGenerateServiceInstanceId:false + serviceInstanceId:InstanceId
so metrics and traces never get a random auto-generated id. ZbSerilogConfig.BuildResourceAttributes
adds service.instance.id from ZbResource.InstanceId so the Serilog OTLP log sink carries the
exact same value — all three signals now share an identical resource for cross-signal joins.

Tests: +2 in ZbResourceTests (InstanceId determinism, no-GUID check), +2 in RedactionTests
(service.instance.id parity assertion in BuildResourceAttributes, symmetric OTLP trigger tests).
Total: 9 + 14 = 23 tests, all green.
2026-06-01 08:26:09 -04:00
Joseph Doherty 8311912f40 feat(telemetry): pack ZB.MOM.WW.Telemetry 0.1.0 + README/CLAUDE + register observability component in indexes
- NuGet metadata: expanded Description and PackageTags on both library csproj files
  (opentelemetry;observability;metrics;tracing;prometheus;otlp;... / serilog;logging;...)
- Full dotnet test: 7 (Telemetry) + 12 (Serilog) = 19 tests, all green
- dotnet pack: ZB.MOM.WW.Telemetry.0.1.0.nupkg + ZB.MOM.WW.Telemetry.Serilog.0.1.0.nupkg
  (artifacts/ gitignored, not committed)
- ZB.MOM.WW.Telemetry/README.md: overview, 2 packages, unifying hinge prose,
  exporter options, OTel signals + trace-log correlation, test/pack commands, status
- ZB.MOM.WW.Telemetry/CLAUDE.md: package responsibilities, consumer matrix,
  build/test/pack commands, status + pointers to components/observability/
- components/README.md: Observability row added to component registry table
- CLAUDE.md: Telemetry row added to component-normalization table; intro count
  updated to four shared libs; observability prose paragraph added (MxGateway
  logging adoption noted)
- upcoming.md: Observability item ticked done, pointing at components/observability/
  and ZB.MOM.WW.Telemetry; MxGateway MEL->Serilog adoption noted
- components/observability/README.md: status updated to Built @ 0.1.0, library
  build/pack commands added, MxGateway adoption row updated
2026-06-01 08:20:05 -04:00
Joseph Doherty f569d537d1 fix(telemetry.serilog): don't set process-global Log.Logger in AddZbSerilog (multi-host safe)
Remove the Stage-1 bootstrap-logger line (Log.Logger = new LoggerConfiguration()
.WriteTo.Console().CreateBootstrapLogger()) from AddZbSerilog. A shared library must
not mutate process-global state: when multiple hosts are built in one process (integration
tests, Aspire multi-host, parallel test runs) the second call throws "The logger is
already frozen".

AddSerilog is now called with preserveStaticLogger: true so Serilog.Extensions.Hosting
leaves the static Log.Logger entirely untouched. The DI-registered application logger is
the only artifact AddZbSerilog produces.

Apps that want a pre-Build() bootstrap logger should set Log.Logger themselves in
Program.cs before calling AddZbSerilog — that decision belongs to the application.

Three new regression tests in MultiHostTests verify: two hosts build in the same process
without throwing; Log.Logger is not mutated; each host gets its own independent DI ILogger.

Docs (SPEC.md §5 and shared-contract ZB.MOM.WW.Telemetry.md) updated: the "two-stage
bootstrap" framing is replaced with the correct description — library registers only the
DI application logger; optional Stage-1 bootstrap is the app's responsibility.
2026-06-01 08:13:35 -04:00
Joseph Doherty f1240c0bd4 refactor(telemetry.serilog): review fixes (thread-safe redactor, bootstrap logger, minlevel ordering, test coverage) 2026-06-01 07:48:57 -04:00
Joseph Doherty 37fb84f477 feat(telemetry): core review fixes (Prometheus+OTLP coexistence, ServiceName validation, null guards) + contract overload note
- Fix #1: Prometheus exporter always wired for metrics; OTLP is additive overlay
  when Exporter == ZbExporter.Otlp so /metrics + MapZbMetrics work in all modes.
- Fix #2: BuildOptions throws ArgumentException when ServiceName is null/whitespace.
- Fix #3: AddZbTelemetry(IHostApplicationBuilder) guard: ThrowIfNull(configure)
  added alongside existing ThrowIfNull(builder).
- Fix #6: Contract doc adds IServiceCollection convenience overload signature.
- Tests: +3 new tests (OtlpExporter still serves /metrics, empty ServiceName throws,
  whitespace ServiceName throws). Total: 7 passed (was 4).
2026-06-01 07:43:47 -04:00