test(validation): ScadaBridge consumer matrix uses a production-length API key; SDK 2 consumer run (ScadaBridge#125)
v2-ci / unit-tests (push) Waiting to run

The consumer matrix minted a 55-character otua_matrix_ key, which stays
under SDK 2's 64-byte RsaEncryptedSecret threshold and so could not show
lmxopcua#880 from the consumer side. It now uses the ApiKeySecret.Generate
shape (65 characters), as opcua-sdk-compatibility already does.

README records the 2026-11-12 run with ScadaBridge on SDK 2.0.1
(a2e9d718): matrix 11/11 x3 modes on 83ed880e and 412da27c, ACL off and
on; 8/8 trust controls fail closed; failover 8/8; a mutation with the SDK
default UserName handler fails every baseline leg with
BadIdentityTokenInvalid.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
Joseph Doherty
2026-11-12 22:22:45 -05:00
parent f3178c5f5d
commit 2705f16670
2 changed files with 23 additions and 2 deletions
@@ -45,7 +45,11 @@ roundtrips, durable hierarchical browse including the native alarm, data quality
and timestamps on raw and UNS subscriptions, alarm filter/ConditionRefresh,
disconnect/reconnect, and (ScadaBridge#125) a Server-object alarm subscription on
the reconnected session whose ConditionRefresh must replay the active native
condition (11 cases per trusted leg). History
condition (11 cases per trusted leg). The API key has the production
`ApiKeySecret.Generate` shape and length, 65 characters (lmxopcua#880): the
55-character `otua_matrix_` key used before stayed under SDK 2's 64-byte
`RsaEncryptedSecret` threshold, so it could not show whether an SDK 2 consumer
still sends the password in a format an SDK 1.5 server decrypts. History
and Part 9 acknowledgment are not exposed by the inspected consumer interfaces;
the parent Shared/CLI matrix covers those application services instead.
@@ -97,3 +101,16 @@ legs (11/11 cases each). Loss-to-backup timings are the same on both versions:
waits out a connect timeout against the silent primary. `rig-failover.sh` passed
9/9 against the docker-dev pair (image SDK 2.0.1), central-1 killed, about 20 s
to central-2.
Run 2026-11-12 (ScadaBridge#125) with the consumer itself on SDK **2.0.1**
(ScadaBridge `731d1ed7`, on main as `a2e9d718`; the DCL sends UserName passwords in the
legacy RSA format), 65-character key, against the SDK 1.5.378 baseline
(`83ed880e`) and master `412da27c` (SDK 2.0.1): matrix 11/11 in None, Sign and
SignAndEncrypt on both with the ACL off and on, all 8 trust controls failed
closed (0x80130000 / 0x801A0000), failover 8/8 legs. `kill` reaches the backup
in about 20 s as before; `freeze` is now detected in about 15 s (SDK 2
keep-alive; about 20 s on the SDK 1.5 client) and reaches the backup in 81–125 s.
A mutation run with the consumer's UserName identity reverted to the SDK 2
default handler fails every trusted leg against the baseline with
`BadIdentityTokenInvalid` (0x80200000) and passes against master, which is
lmxopcua#880 reproduced from the consumer side.
@@ -16,7 +16,11 @@ SECURITY = {"None": "none", "Sign": "sign", "SignAndEncrypt": None}
def leg_env(mode):
env = os.environ.copy()
env["OPCUA_MATRIX_PASSWORD"] = "otua_matrix_" + secrets.token_urlsafe(32)
# The production ApiKeySecret.Generate shape and length, 65 characters (lmxopcua#880, ScadaBridge#125): the
# 55-character key used before stayed under SDK 2's 64-byte RsaEncryptedSecret threshold, so it could not show
# whether an SDK 2 consumer still sends the password in a format an SDK 1.5 server decrypts.
env["OPCUA_MATRIX_PASSWORD"] = "otua_" + secrets.token_hex(8) + "_" + secrets.token_urlsafe(32)
assert len(env["OPCUA_MATRIX_PASSWORD"]) == 65
for name in ("SCADABRIDGE_PROBE_PKI", "SCADABRIDGE_PROBE_SERVER_CERTS", "OPCUA_MATRIX_SERVER_SECURITY"):
env.pop(name, None)
if SECURITY[mode]: