Commit Graph

101 Commits

Author SHA1 Message Date
Joseph Doherty 4b68729a8a fix(ci): find the docker host without ip in the act_runner job image (#860)
catthehacker/ubuntu:act-latest ships no iproute2, so fixture_host() fell back to 127.0.0.1 inside the
job container and every published-port fixture timed out (QuestDB is the first one probed over the
network). Read the default gateway from /proc/net/route in plain bash, warn when none is found, and log
the chosen host. Verified in that image on a user-defined network: gateway parsed, published port 200.

Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
2026-11-11 10:12:49 -05:00
Joseph Doherty b57fb471d5 docs(ci): #818 floor-raise condition uses the post-review counts (9/12, +8), e2e 35 -> 39; hosted leg blocked on #860
The persistent NAT PKI (ae91e6b7) re-verified 2026-11-11: same thumbprint
1FBF8791... across --force-recreate on 10.100.0.35, matching the committed
opc-plc-nat-server.der; NAT class 7/7 against the shared stack and against a
CI-style throwaway container + docker-exec cert hand-off. MIN_EXECUTED stays
at 440: no hosted v2-integration run has reached the test step (#860).

Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
2026-11-11 10:10:02 -05:00
Joseph Doherty c3cf11c5e4 ci(questdb): CI keeps its throwaway QuestDB but reads the image pin from the shared fixture's compose file (#841)
Decision: CI stays hermetic (no dependency on the 10.100.0.35 box or its
state), but must run the same engine. integration-fixtures.sh now takes the
questdb/questdb:<tag> line from the #822 fixture compose file (10.0.1 today,
same as the docker-dev overlay) and fails 'up' if it is missing;
CI_QDB_IMAGE overrides for a trial. Documented in scripts/ci/README.md.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-11 03:50:12 -05:00
Joseph Doherty 9d91c41847 docs: MIN_EXECUTED raise is 448 after the #816 review tests; fixture-label note reflects reality 2026-11-11 01:54:01 -05:00
Joseph Doherty ae91e6b7f7 feat(opcuaclient): opt-in server-certificate host-name check; NAT fixture keeps its PKI (#816, #818)
#816 finding: the SDK checks that the server certificate names the host only
when a session is created with checkDomain:true. The driver, the AdminUI
browser and Client.Shared all called the 8-argument
DefaultSessionFactory.CreateAsync, which forwards checkDomain:false — so no
host-name check ever ran (why a trusted NAT cert for *.invalid browsed fine
over an IP in #813). When enabled the SDK (Session.OpenAsync ->
ValidateDomains -> FindDomain) compares ConfiguredEndpoint.EndpointUrl.IdnHost
— the CONFIGURED host, because OpcUaEndpointSelection/EndpointSelector
rewrote the advertised one — against the cert's DNS/IP SANs; secured
sessions only, and the auto-accept handler accepts a mismatch.

Policy: certificateValidation.validateServerHostName, tri-state. Unset =
OpcUaCertificateValidationOptions.DefaultValidateServerHostName = false, the
pre-#816 behaviour, so nothing changes on upgrade; true = validate the
configured host; false = explicit NAT/port-publish opt-out. Threaded into the
driver, the AdminUI browser (same JSON) and the AdminUI form (a select that
keeps "unset" unset), Client.Shared ConnectionSettings.ValidateServerHostName
and the CLI --validate-host-name. A driver refusal names the opt-out.

#818: the NAT fixture's /app/pki is on the opc-plc-nat-pki volume (verified:
same thumbprint across --force-recreate), its PUBLIC cert is committed as
Docker/nat/opc-plc-nat-server.der, and NatPublishedEndpointBrowseTests run
every secure browse on a throwaway PKI (internal browser seam): refusal with
nothing trusted, positive with the cert trusted (unset/false host check), and
a BadCertificateHostNameInvalid refusal with the check on. The trusted cert is
OPCUA_NAT_SERVER_CERT or the committed one (skip on thumbprint mismatch);
OPCUA_NAT_SERVER_CERT_TRUSTED is retired. CI exports its throwaway NAT
container's public cert. ServerHostNameValidationTests pin the driver against
the plain opc-plc. MIN_EXECUTED not raised (needs a hosted run): +6 in
v2-integration (440 -> 446), documented in scripts/ci/README.md.
2026-11-11 01:33:00 -05:00
Joseph Doherty ac1b33555a test(infra): port-NAT opc-plc fixture for the NAT browse tests; wire OPCUA_NAT_ENDPOINT into CI (#813)
Docker/nat/docker-compose.yml (stack /opt/otopcua-opcuaclient-nat on 10.100.0.35,
project: lmxopcua label): opc-plc 2.14.10 advertising --ph=otopcua-opc-plc-nat-private.invalid
on internal 50000, published on 50010, so every advertised URL is unreachable on host AND
port. Deployed; NatPublishedEndpointBrowseTests 3/3 against opc.tcp://10.100.0.35:50010
(and the secure case positive once, with the fixture cert temporarily trusted).

CI: integration-fixtures.sh starts a throwaway otopcua-ci-opcplc-nat (50110->50000) and
exports OPCUA_NAT_ENDPOINT; v2-e2e.yml points it at the shared stack. infra/README.md,
scripts/ci/README.md and the fixture README document it.
2026-11-10 22:53:01 -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
Joseph Doherty a5bed28765 fix: preserve baseline OPC UA aggregate algorithms under SDK 2.0 2026-11-10 10:24:19 -05:00
Joseph Doherty 2a8e2981fe feat(scripting): ScriptWorker isolation on Windows: restricted worker identity, pipe ACL, firewall + PeCacheDir installer (lmxopcua#780)
v2-ci / unit-tests (push) Has been cancelled
Owner decisions §14 #5/#6 as built (design §12.5):

- Scripting:Worker:Identity (default unset, Windows only, fail-closed validation): each worker
  starts under a restricted token derived from the host's. Every privilege but SeChangeNotify is
  removed; the restricting SIDs are NT SERVICE\OtOpcUaScriptWorker's SID + Everyone + Users +
  RESTRICTED; the token and default DACLs are host + SYSTEM + identity + RESTRICTED. The worker
  is created suspended, put in its Job Object before it runs, inherits only its stdio pipes, gets
  an ACL'd work dir as TEMP, and runs with DOTNET_EnableDiagnostics=0. The identity is granted on
  the host's window station and desktop, because USER32 init made coreclr.dll fail with
  ERROR_DLL_INIT_FAILED on windev. The logon SID is not used because the host's process DACL
  gives it PROCESS_VM_READ.
- The worker pipe on Windows always has an explicit protected DACL: host account + identity
  only. There is no owner ACE, so an elevated host's Administrators gets no access.
- The worker treats access denied on its parent-pid poll as alive; the Job Object covers that case.
- scripts/install/Install-ScriptWorkerIsolation.ps1 + ScriptWorkerIsolation.psm1 are idempotent
  and support -WhatIf, -Uninstall and -PlanOnly. They create the disabled anchor service, the
  PeCacheDir ACL (protected, no Users, key and markers host-only, pe\ Modify for the identity),
  read+execute on script-worker\, a ReadData deny on the install root, the outbound firewall
  block on the worker exe, and optionally the host-service env.
- Tests: the dry-run harness scripts/install/tests/Test-ScriptWorkerIsolation.ps1 (46
  assertions, mutation-checked); ScriptWorkerIdentityTests; WorkerIsolationTests, which cover
  the pipe ACL, token access and a real restricted worker running a script, all green on windev.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-10 08:46:23 -05:00
Joseph Doherty 94926eb0d7 ci: fixture-backed integration workflow and guarded fleet E2E workflow (lmxopcua#757)
v2-ci / unit-tests (push) Has been cancelled
v2-integration.yml runs the Sql, Historian.SqlServer, Historian.QuestDb and OpcUaClient
integration projects against throwaway SQL Server/PostgreSQL/QuestDB/opc-plc containers
started by scripts/ci/integration-fixtures.sh, with a MIN_EXECUTED=440 floor. v2-e2e.yml is
manual/weekly, probes the shared fleet and skips cleanly when unreachable. v2-ci.yml is
functionally unchanged. Unverified on a hosted runner until first run.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 16:53:44 -05:00