Commit Graph

4 Commits

Author SHA1 Message Date
Joseph Doherty f679d5c749 feat(cluster): manual central failover service — graceful Leave of the oldest Up member
Admin-triggered failover of the central pair. IManualFailoverService is declared in
CentralUI (plain strings, so that project stays Akka-free); AkkaManualFailoverService
implements it in the Host and is registered only in the Central branch.

- Leave, never Down: singletons hand over instead of being killed.
- Target = oldest Up member with the Central role, mirroring ActiveNodeEvaluator, so
  the node acted on is exactly the one hosting the singletons (never the leader,
  whose address-ordered definition diverges from singleton placement after a restart).
- Peer guard: returns null when fewer than 2 Up Central members — failing over a lone
  node is an outage, not a failover.
- Audited BEFORE the Leave is issued via ICentralAuditWriter: the acting node can be
  the one that goes away, and an audit written after could be lost to the shutdown it
  describes. Best-effort — audit failure never blocks the failover.

New audit taxonomy: AuditChannel.Cluster + AuditKind.ManualFailover (operator-initiated
topology actions are not script trust-boundary crossings, but are exactly what an audit
log exists to attribute). Lock-in tests updated 5->6 channels, 16->17 kinds.

alog.md §4 updated per the lock-in tests' contract. Both tables were already stale --
the Channel row omitted SecuredWrite and the Kind table claimed 10 while the code had
16 -- so they are completed here, not merely appended to.

ManualFailoverTests: 3 real-cluster tests (oldest leaves + survivor takes over, peer
guard refuses with a positive still-running assert, dry-run probe does not perturb).
2026-07-22 06:55:15 -04:00
Joseph Doherty 7b0b9c7365 refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj,
namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated.
ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated.
SQL roles/logins, LDAP domains, CLI command name, and CLI config dir
(~/.scadalink → ~/.scadabridge) also renamed.

Build green; 5 Host.Tests fail awaiting SQL login rename in next commit.
Pre-existing StaleTagMonitor timing flakes unchanged.

Rename script committed at tools/rename-to-scadabridge.sh.
2026-05-28 09:37:45 -04:00
Joseph Doherty 3592e74085 docs(audit): align alog.md + Component-AuditLog.md vocab with M1 enums (#23)
The M1 implementation (Bundle A) committed concrete AuditChannel /
AuditKind / AuditStatus enums that reflect CLAUDE.md's locked
cached-call lifecycle decisions. The older alog.md and
Component-AuditLog.md narratives still used pre-M1 vocabulary
(Success / TransientFailure / PermanentFailure / Enqueued / Retrying /
SyncCall / CachedEnqueued / Attempt / Terminal / Completed). This
commit reconciles both docs to the M1 vocabulary:

  AuditChannel  : ApiOutbound, DbOutbound, Notification, ApiInbound
  AuditKind (10): ApiCall, ApiCallCached, DbWrite, DbWriteCached,
                  NotifySend, NotifyDeliver, InboundRequest,
                  InboundAuthFailure, CachedSubmit, CachedResolve
  AuditStatus(8): Submitted, Forwarded, Attempted, Delivered, Failed,
                  Parked, Discarded, Skipped

Updates:
  - Status column description + worked examples use the new 8 values.
  - Kind table flattened from per-channel groupings to a single flat
    list of the 10 discriminators (no more SyncCall / Cached* /
    Attempt / Terminal / Completed).
  - Cached-call lifecycle examples rewritten to the
    CachedSubmit -> Forwarded -> Attempted... -> CachedResolve shape.
  - Notification lifecycle examples rewritten to
    NotifySend(Submitted) -> NotifyDeliver(Attempted) ->
    NotifyDeliver(Delivered/Parked/Discarded).
  - Inbound API examples split into InboundRequest (success path) and
    InboundAuthFailure (401 path).
  - 'Errors only' UI toggle, audit-error-rate KPI, and payload-cap
    decision (#6 in §16) all switched from 'non-Success' to
    Status IN ('Failed', 'Parked', 'Discarded').
  - Per-site event-rate table in §13.1 renamed to the new kinds.

Pure design correction; no operational behavior change. Per the
goal-prompt invariant #6, alog.md may change when a design correction
is committed before the affected code change — this commit is that
correction, landed ahead of the M1 merge so the merge order reads
design-first, code-second.

No code, test, or infra file changes.
2026-05-20 11:56:34 -04:00
Joseph Doherty fec0bb10ff docs(audit): add centralized audit log design (alog.md)
Validated design for a new append-only AuditLog covering the script
trust boundary: outbound API calls (sync + cached), outbound DB
operations (sync + cached, incl. script-initiated reads), notifications,
and inbound API requests. Layered alongside existing Notifications (#21)
and SiteCalls (#22) operational tables.

Key decisions:
- One row per lifecycle event; strictly append-only.
- Site SQLite hot-path append + best-effort gRPC telemetry + central
  reconciliation pull. Site purge requires ForwardState=Forwarded.
- Cached calls: site emits; one telemetry packet feeds both the
  immutable AuditLog row and the operational SiteCalls upsert.
- Payload: metadata + truncated bodies (8 KB default, 64 KB on errors).
  Headers redacted; SQL parameter values captured by default.
- Audit-write failures never abort the user-facing action.
- Monthly partitioning at central; 365-day global retention.
- New Audit nav group + drill-in links from existing pages.

Deferred to v1.x: hash-chain tamper evidence, Parquet archival,
per-channel retention overrides. Provisional component #23.
2026-05-20 07:21:44 -04:00