Commit Graph

51 Commits

Author SHA1 Message Date
Joseph Doherty e5b26f3772 test(validation): #880 cross-version matrix evidence, fixed and mutated candidate
v2-ci / unit-tests (push) Has been cancelled
The baseline is 83ed880e (SDK 1.5.378.182). The candidate is the #880 change (on da948e5f), run twice.
Fixed: every SDK 2 client (Shared, OpcUaClient driver, CLI) logs in to the SDK 1.5
server with a 65-char key. A wrong 65-char key gets BadIdentityTokenRejected. In the
control, the SDK default handler is refused by 1.5 with BadIdentityTokenInvalid and
admitted by SDK 2.
Mutation (the factory reverted to the SDK default handler): the SDK 2 Shared connect,
the driver connect and all three CLI calls fail against 1.5 with
BadIdentityTokenInvalid, and the wrong-key case gets Invalid instead of Rejected. This
is #880 reproduced.
In both runs the UNS cases fail against the master server for every client SDK. That
is a fixture gap since #808 and is tracked as #888.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 11:14:15 -05:00
Joseph Doherty 2ad7213046 test(validation): SDK compatibility matrix uses the production 65-char API key, adds #880 format cases
The cross-version matrix (#782) used a 55-char 'otua_matrix_' key, which stayed under
SDK 2's 64-byte RsaEncryptedSecret threshold and hid #880. It now uses the
ApiKeySecret.Generate shape (65 chars, random per run, never recorded). Every Shared
leg adds production-length-API-key-wrong-secret-reaches-key-check, which must get
BadIdentityTokenRejected rather than a format failure. SDK 2 clients also add
sdk2-default-RsaEncryptedSecret-control, the negative control: the SDK's default
handler must be refused with BadIdentityTokenInvalid by a 1.x server and admitted
by an SDK 2 server. The runner passes the server SDK version from ready.json.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 11:12:27 -05:00
Joseph Doherty be28aa4432 fix(opcua-client): send UserName passwords in the legacy RSA format so 65-char API keys log in to SDK 1.5 servers (#880)
SDK 2.0.1's UserNameIdentityTokenHandler encrypts a password longer than 64 bytes
(a private RsaEncryptedSecretPasswordThreshold, no setting) as an RsaEncryptedSecret
with EncryptionAlgorithm=null. SDK 1.5.378 (.106 and .182) decrypts only the legacy
format under RSA policies; its SecurityPolicies.Decrypt answers a null algorithm with
BadIdentityTokenInvalid ("Unexpected encryption algorithm") before any key check.
Every production API key is 65 chars, so no SDK 2 OtOpcUa client could use one
against QA (b8b4c18f), a rollback target or an older third-party server.

LegacyRsaUserNameIdentityTokenHandler (Driver.OpcUaClient.Session, plugged in through
the public UserIdentity(IUserIdentityTokenHandler) ctor) always uses the legacy
format, RSA-OAEP(len|password|serverNonce) under the server certificate, under an RSA
policy. That is what SDK 2 sends for passwords of 64 bytes or less and what SDK 1.5
clients always send. The legacy format has no size limit (the plaintext is
length-framed over as many RSA blocks as it needs), and an SDK 2 server reads both
formats. Policy None, the ECC policies and a missing certificate go to the SDK
handler unchanged.

OpcUaUserIdentityFactory.UserName is now the one UserName identity the driver, the
AdminUI browse and Client.Shared (CLI/UI) present. Client.Shared references .Session
for it.

Tests: handler unit tests (all three RSA policies, the 1.5-required algorithm URI,
SDK 2 server-side decrypt, the session Copy() keeps the type, None unchanged, and an
SDK control pinning the 64/65-byte switch); a Client.Shared identity test; and an
SDK2<->SDK2 integration test where a 65-char key is admitted in both formats and a
wrong 65-char key reaches the key check (BadIdentityTokenRejected). The #800 Windows
gate's SDK 2 CLI 65-char case now gates against the 1.5 server too.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 11:12:27 -05:00
Joseph Doherty ac4c4d40dc test(validation): #800 Windows service gate harness + windev results (SDK 1.5 -> 2.0.1 in place, rollback)
v2-ci / unit-tests (push) Has been cancelled
Adds scripts/validation/opcua-windows-service/: a phased PowerShell gate (Invoke-ServiceGate.ps1)
that installs the known-good build as the real OtOpcUaHost service via Install-Services.ps1, seeds
LocalDb through the production cache API (Seeder, built against the baseline tree), upgrades in
place with config/PKI/LocalDb carried with their ACLs, starts/stops through the SCM, runs a strict
SDK 2 probe matrix (Sign + SignAndEncrypt, valid/wrong/unknown/disabled/anonymous keys, untrusted
client and server), exercises the ScriptWorker isolation, rolls back and cleans up.

windev 2026-11-12 (efb9197a -> 438484fd -> efb9197a): app cert CEF9D91F... reused byte-for-byte
with identical PKI SDDL on every start, all gating checks pass, 0 secrets in logs. Findings filed:
#879 (None endpoint cannot be dropped), #880 (SDK 2 client 65-char key vs SDK 1.5 server:
rollback/QA risk), #883 (Install-ScriptWorkerIsolation.ps1 fails on pwsh 7 and 5.1; runtime
isolation proven with the patch in the evidence directory).

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 10:28:49 -05:00
Joseph Doherty 0f001bccdb test(validation): pair-gate helper for the #801 revocation/expiry/ACL checks on docker-dev
v2-ci / unit-tests (push) Has been cancelled
Console helper that mints and revokes OPC UA API keys through the same IApiKeyAdminService the AdminUI
/apikeys page calls, edits NodeAcl grants in the rig ConfigDb, and holds one OPC UA session open per node
(Client.Shared) with a subscription plus a read/write every 3 s, so a revocation, expiry or grant removal
can be observed landing on an ALREADY-OPEN session on both pair nodes next to a non-revoked control key.
Credentials go to a 0600 file and are never printed. LOCAL docker-dev only.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 10:22:33 -05:00
Joseph Doherty f607ee7502 test(historian): #801 real-AVEVA history gate over the SDK wire (env-gated)
AvevaGatewayHistoryLiveGateTests boots the server in-process, wires the production
Gateway backend (HistorianBackendRegistry -> GatewayHistorianDataSource) to a real
HistorianGateway and drives HistoryRead over a real SDK session: raw (all, paged,
reverse, bounds), modified, the ten served aggregates and seven refused ones, AtTime,
events (all, 1-event pages, reverse), a missing tag, HistoryServerCapabilities and
node metadata, and a HistoryUpdate that must be refused (sandbox-bound node only).
Every wire answer is compared with the backend's direct answer; the run is dumped as
JSON for cross-SDK and SQL comparison. Skips unless HISTGW_GATEWAY_ENDPOINT and
HISTGW_GATEWAY_APIKEY are set; read-only by construction.

scripts/validation/aveva-history-801: run/compare instructions, the SDK 1.5.378
baseline port patch (API spelling only), and the diff / SQL / aggregate / histgw-cli
comparison scripts.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 10:02:21 -05:00
Joseph Doherty 438484fdca test(validation): ScadaBridge consumer matrix — failover suite, UNS subscribe, ConditionRefresh after reconnect (ScadaBridge#125)
v2-ci / unit-tests (push) Has been cancelled
Probe 'failover <primary> <backup> <mode> <dir>' drives ScadaBridge's own DataConnectionActor
(production primary/backup machine, real DataConnectionFactory, default DataConnectionOptions,
FailoverRetryCount 3) against two servers; run.py loses the primary (SIGKILL or SIGSTOP) once the
probe reports a steady primary, and the probe requires Bad quality on loss, "Connected to backup",
the backup's own value, a fresh ConditionRefresh replaying the native condition, and a write via the
backup. rig-failover.sh runs the same probe against the local docker-dev central pair.

Matrix legs gain a UNS subscription case and a Server-object alarm subscription on the reconnected
session whose ConditionRefresh must replay the active condition (11 cases). run.py gains --versions,
--suites, --failover-modes and --failover-loss.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
2026-11-12 09:28:01 -05:00
Joseph Doherty 135099d722 docs(validation): scadabridge-sdk-compatibility README — #121 fixed, Sign leg, trust controls, ACL switch
v2-ci / unit-tests (push) Waiting to run
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-12 07:01:24 -05:00
Joseph Doherty ea83fc58b2 test(validation): ScadaBridge consumer matrix — provisioned client identity, Sign leg, trust negative controls (ScadaBridge#121)
Probe 'prepare' provisions the consumer identity via OpcUaApplicationIdentity.EnsureApplicationCertificateAsync
and exports the public DER; runner installs it in the server trusted store before start. Adds Sign
(OPCUA_MATRIX_SERVER_SECURITY=sign), untrusted-client / untrusted-server fail-closed controls per secure
mode, and OPCUA_MATRIX_ACL=off. Autoaccept stays false everywhere.
2026-11-12 07:01:14 -05:00
Joseph Doherty 3d1b86b451 fix(acl): authorize scripted alarm commands at their owning equipment's scope (#797)
Scripted condition NodeIds are opaque ScriptedAlarmIds, so splitting them as a
topology path could never match an Equipment grant: a narrow equipment grant was
denied while a Cluster grant passed.

- OtOpcUaNodeManager binds each LIVE scripted condition instance to the
  EquipmentId it was materialised under (only when it hangs under that folder);
  dropped on remove / drop-recreate / equipment-subtree removal / rebuild; a
  same-id re-materialise under another equipment re-parents and re-binds.
- DataPlaneAuthoritySnapshot carries EquipmentScopePaths (EquipmentId ->
  [uns, Area, Line, Equipment]) decoded from the artifact by the same
  AclScopePathResolver call that plants Equipment grants.
- DataPlaneAuthorizer.AuthorizeScriptedCondition re-reads key, grant and
  generation per call (uncached) and evaluates at the owner path; unbound,
  replaced or unplaceable owners deny under every grant. No id-split or
  realm-root fallback.
- Native conditions (RawPath) and the ACL-off role gate are unchanged.
  NodeIds and AlarmCommand.AlarmId are unchanged.

Sdk2OpaqueScriptedScopeBaselineTests deliberately flipped: it pinned the denial
and now pins owner-grant allow / sibling deny / cluster allow. Follow-up #808
(folder ids + Server-object event visibility for narrow UNS grants).
2026-11-10 21:23:57 -05:00
Joseph Doherty da4e27003e docs: record completed Linux OPC UA lifecycle diagnostic
v2-ci / unit-tests (pull_request) Waiting to run
2026-11-10 20:28:11 -05:00
Joseph Doherty 1e4b017f6e docs: retain verified Linux rehearsal and diagnostic gate
v2-ci / unit-tests (pull_request) Failing after 1h6m11s
2026-11-10 18:25:37 -05:00
Joseph Doherty 681e1c1a9b fix: bind private temp storage for read-only late-slope runs 2026-11-10 18:18:59 -05:00
Joseph Doherty 6591cf1a05 docs: prepare private native retention handoff 2026-11-10 18:03:13 -05:00
Joseph Doherty d8a6959bcf test: verify the mapped CLR inode in late-slope diagnostics 2026-11-10 18:02:33 -05:00
Joseph Doherty f4054f6201 test: enforce late-slope restart and shutdown deadlines 2026-11-10 18:01:41 -05:00
Joseph Doherty 613369fc54 test: add immutable Linux late-slope diagnostic tooling 2026-11-10 17:59:55 -05:00
Joseph Doherty 7023285a3e docs: retain late-slope guard control receipts 2026-11-10 17:54:43 -05:00
Joseph Doherty cfd3cdcd13 test: compare native keychain deletion timing 2026-11-10 17:52:51 -05:00
Joseph Doherty 5ea6a23f1b test: implement fixed Linux late-slope diagnostic mode 2026-11-10 17:47:31 -05:00
Joseph Doherty 1298ecc194 test: reproduce native import retention without managed caller 2026-11-10 17:42:23 -05:00
Joseph Doherty 327bbdaab9 docs: record reviewed SDK2 ownership regressions and diagnostic design 2026-11-10 17:33:06 -05:00
Joseph Doherty 2e4905f112 test: record partial native bridge import control 2026-11-10 17:31:50 -05:00
Joseph Doherty 69dcc431ec test: record native Apple keychain negative control 2026-11-10 16:56:31 -05:00
Joseph Doherty 3e136d7367 test(opcua): distinguish baseline close semantics and terminal ownership 2026-11-10 16:51:36 -05:00
Joseph Doherty af71fb11ba test: isolate native certificate reload retention on macOS 2026-11-10 16:37:51 -05:00
Joseph Doherty 235196beb2 test: add bounded SDK2 close ownership controls 2026-11-10 16:26:26 -05:00
Joseph Doherty 10adbd57b6 test(opcua): preserve original canonical adapter partial ownership proof 2026-11-10 16:08:47 -05:00
Joseph Doherty 04d5513af2 test(opcua): bind saturation artifacts to gateway 1.0 and module identities
v2-ci / unit-tests (pull_request) Has been cancelled
2026-11-10 15:03:21 -05:00
Joseph Doherty d7891372f2 test(opcua): prove saturation observer runtime event delivery 2026-11-10 14:55:46 -05:00
Joseph Doherty 54a4ff2149 test(opcua): add fixed candidate saturation diagnosis with observer barriers 2026-11-10 14:55:46 -05:00
Joseph Doherty ab35ddd3c2 test: retain completed two-hour client lifecycle evidence
v2-ci / unit-tests (pull_request) Has been cancelled
2026-11-10 14:54:38 -05:00
Joseph Doherty f2a8ce1305 docs: record landed historian upgrade and support migrated replays 2026-11-10 14:44:30 -05:00
Joseph Doherty dc2a60f8fc test: record published HistorianGateway 1.0 package verification 2026-11-10 14:41:38 -05:00
Joseph Doherty 215e018a48 docs: preserve historian TLS verification command exits 2026-11-10 14:36:26 -05:00
Joseph Doherty 35a4612630 test: validate historian TLS migration against isolated source 2026-11-10 14:35:35 -05:00
Joseph Doherty 923bc737a3 test: initialize immutable historian TLS fixture options 2026-11-10 14:27:52 -05:00
Joseph Doherty ef21571feb test: prepare HistorianGateway 1.0 consumer TLS migration 2026-11-10 14:24:07 -05:00
Joseph Doherty 12dffb9254 docs: retain reviewed memory investigation and prospective gate limits
v2-ci / unit-tests (pull_request) Waiting to run
2026-11-10 14:21:57 -05:00
Joseph Doherty 6f793d2521 test(opcua): package cross-platform lifecycle memory controls 2026-11-10 14:18:10 -05:00
Joseph Doherty a1f5028fe6 test: align Linux replay guard with reviewed production source
v2-ci / unit-tests (pull_request) Has been cancelled
2026-11-10 14:03:37 -05:00
Joseph Doherty a7efb39685 docs: record Astra source verdict and preserve failed lifecycle scope
v2-ci / unit-tests (pull_request) Has been cancelled
2026-11-10 13:33:01 -05:00
Joseph Doherty ff44167926 test: record actual ScadaBridge consumer and baseline authorization gates 2026-11-10 13:17:13 -05:00
Joseph Doherty 8d75ac8c6c test: add isolated Linux server lifecycle soak launcher 2026-11-10 13:10:20 -05:00
Joseph Doherty 02ea81e1cf test: add isolated actual Host publish smoke probe 2026-11-10 12:51:24 -05:00
Joseph Doherty 47a29dee2b test: prove peer provenance and repeated alarm authority with baseline scope oracle 2026-11-10 12:50:19 -05:00
Joseph Doherty 682b73bfe8 test: add production client lifecycle soak harness 2026-11-10 12:44:04 -05:00
Joseph Doherty 7b353d7bae test: record cross-version wire compatibility and warmed metrics 2026-11-10 12:26:54 -05:00
Joseph Doherty 2d75cecbb9 test: add isolated cross-version application compatibility harness 2026-11-10 12:18:46 -05:00
Joseph Doherty 8c422b9ccc test: expand original SDK aggregate edge vectors 2026-11-10 11:04:53 -05:00