From 48813bba6fda28ca63b5edc533813682694b3bf0 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 9 Nov 2026 14:49:20 -0500 Subject: [PATCH] fix(localdb): sync endpoint enforces the configured ApiKey (scadaproj#36) LocalDbSyncService never read request headers, so setting LocalDb:Replication:ApiKey on both nodes left the passive endpoint open (LocalDb-001). It now requires Authorization: Bearer (constant-time) before the handshake and before newest-wins takeover, refusing anything else with Unauthenticated. A blank key keeps the old accept-all behaviour and logs a startup warning. Wire-compatible: the initiator has always sent the header when the key is set; ScadaBridge/OtOpcUa interceptors use the same key. LocalDb 0.3.5 -> 0.3.6 (unpublished). Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV --- ZB.MOM.WW.LocalDb/CLAUDE.md | 3 + ZB.MOM.WW.LocalDb/Directory.Build.props | 2 +- ZB.MOM.WW.LocalDb/README.md | 20 +++- .../LocalDbSyncService.cs | 65 +++++++++- .../ReplicationOptions.cs | 10 +- .../GrpcAdapterTests.cs | 113 ++++++++++++++++++ code-reviews/LocalDb/findings.md | 22 +++- code-reviews/README.md | 11 +- 8 files changed, 228 insertions(+), 18 deletions(-) diff --git a/ZB.MOM.WW.LocalDb/CLAUDE.md b/ZB.MOM.WW.LocalDb/CLAUDE.md index 2d11a13..d1e1903 100644 --- a/ZB.MOM.WW.LocalDb/CLAUDE.md +++ b/ZB.MOM.WW.LocalDb/CLAUDE.md @@ -6,6 +6,9 @@ Shared embedded SQLite cache + 2-node LWW sync library for the **ZB.MOM.WW SCADA **`0.3.5` (unpublished, scadaproj#26):** `Microsoft.Data.Sqlite` 10.0.7 -> 10.0.13 (efcore#39009 pool fault, which a LocalDb consumer is exposed to); `SQLitePCLRaw.lib.e_sqlite3` transitive pin 2.1.12 -> 2.1.13 (10.0.13 floors 2.1.13). 216/216; no wire/schema change. +**`0.3.6` (unpublished, scadaproj#36–#39):** code-review fixes, all wire-compatible (no proto, `lib_schema_version` or on-disk schema change). +- **#36 LocalDb-001:** the passive `LocalDbSyncService` now enforces `LocalDb:Replication:ApiKey` itself (Bearer, constant-time, checked before the handshake and before newest-wins takeover; `Unauthenticated` otherwise). Blank key = unchanged behaviour plus a startup warning. ScadaBridge's and OtOpcUa's own `LocalDbSyncAuthInterceptor`s use the same key and header, so they keep working; they are now redundant except for their fail-closed-when-blank rule. + ## Index history (migrated from the scadaproj root CLAUDE.md) > Moved verbatim out of the scadaproj root `CLAUDE.md` on 2026-09-03 so it loads only when needed. **This file is now the authoritative place for this history — append new entries here and keep the root index row short.** diff --git a/ZB.MOM.WW.LocalDb/Directory.Build.props b/ZB.MOM.WW.LocalDb/Directory.Build.props index 58c45ca..4552f01 100644 --- a/ZB.MOM.WW.LocalDb/Directory.Build.props +++ b/ZB.MOM.WW.LocalDb/Directory.Build.props @@ -5,7 +5,7 @@ enable enable latest - 0.3.5 + 0.3.6 true true