From 10ca024a2c8cdf36475f7c60f8a917c3457bfe1d Mon Sep 17 00:00:00 2001 From: sammiller Date: Sun, 25 Oct 2026 16:51:23 +0000 Subject: [PATCH] docs(maintenance): record MR-12 producer pipe ownership repair Co-Authored-By: OpenAI Codex --- acceptance-register.md | 11 +++- delivery-graph.json | 3 +- ledger/ledger.jsonl | 1 + maintenance/2026-10-24-repair-list.md | 2 +- maintenance/2026-10-25-mr12-validation.md | 78 +++++++++++++++++++++++ 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 maintenance/2026-10-25-mr12-validation.md diff --git a/acceptance-register.md b/acceptance-register.md index ba1442e..45c94f7 100644 --- a/acceptance-register.md +++ b/acceptance-register.md @@ -36,7 +36,7 @@ Real-world validation that cannot run at development stage, and the human prereq | AR-18 | A Windows node identifies a running WSL VM's worker process (`vmmem` or `vmmemWSL`) as one `GUEST_VM` consumer in unmanaged load, never controlled, and never presents the VM's Linux processes as its own (ArcForges-Design 03 §1, §2, §9; A-L01, AD-0018 §2). Procedure: on Windows 11 with WSL2 and one distribution installed, (1) start the distribution outside ArcForges (`wsl -d -e sleep 600`); (2) run a native node; (3) `arcforges ps --label guest --json` lists the VM worker with label `PROCESS_LABEL_GUEST_VM`, no task, and a memory figure close to Task Manager's for that process, and no Linux process of the distribution appears; (4) allocate about 2 GiB inside the distribution (for example `python3 -c "b=bytearray(2<<30); import time; time.sleep(60)"`): `arcforges resources --json` shows unmanaged memory rising and the memory budget falling by about that amount within about 10 s; (5) a task running `wsl.exe -d -e sleep 30` has every process flagged `crossesVmBoundary`, and `ps --label managed` lists only Windows processes of its job; (6) run `ArcForges.ClosedLoop.Tests -method *ATaskThatRunsWsl*` there and keep the VM workers it prints | WSL is not installed on the workstation, so no VM worker runs there; the `crosses_vm_boundary` flag itself is proven with the inbox `wsl.exe` (AF-RES-01) | A Windows 11 machine with WSL2 and one distribution (user-provided; installing WSL is the user's step) | AF-RES-01 | Open | | AR-19 | Successful Windows process adoption (ArcForges-Design 04 §10; `ResourceService.AdoptProcess`): a same-user process outside every job is adopted into the root job and a nested adoption job (the root job's limits apply, V-W01), appears as an `ADOPTED` task with label `ADOPTED`, its existing children follow, it can be terminated through `process.terminate`, and it ends `LOST` ("no exit status: the node is not the adopted process's parent") when it exits; after a node restart an adoption job the node cannot reopen (L-14) ends the task `LOST` through `container_missing`. Procedure: on a Windows 11 machine where a process created through WMI `Win32_Process.Create` is outside every job (check with `IsProcessInJob`), run `Node.Platform.Windows.Tests -method *AdoptionTests.AProcessOutsideAnyJobIsAdoptedWithItsChildrenAndTerminated` and `Node.Core.Tests -method *AdoptionLifecycleTests.AnAdoptedProcessThatEndsByItselfEndsItsTaskLostWithoutAnExitStatus` and `-method *AdoptionLifecycleTests.AnAdoptionContainerTheNodeCannotReopenAfterARestartIsLost`; none may skip with the "already in a job" reason | On the workstation every user process, a WMI-created one included, is already in a job (ArcForges-Design register L-30), so 04 §10 refuses every candidate and the capability `adopt` reports `LIMITED`; the refusal path is proven there (AF-SEC-01) | A Windows 11 machine or VM where a WMI-created process is outside every job (user-provided) | AF-SEC-01 | Open | | AR-20 | MR-07: establish the cause of the Windows supervisor test's unexplained node stop exit 75 and verify a discriminating repair if required | The historical failure has not recurred with the retained diagnostics; non-recurrence cannot establish its cause or prove a fix | A captured native Windows recurrence with verbose transport/descriptor/node/supervisor evidence, or a proven causal reproduction; the Windows MCP venue is already available, no new human provisioning | AF-NODE-01 | Open; unresolved observation, not a passed check or task block | -| AR-21 | MR-11: a non-elevated Windows node and an elevated client of the same user: user-SID pipe ownership succeeds, but an unidentifiable elevated peer is refused before API execution | Existing automated ownership tests do not create two processes at different integrity levels; owner deferred full end-to-end testing on 2026-10-15 (ledger 30) | User-performed Windows 11 cross-integrity run at final end-to-end acceptance; no automatic elevation or re-request now | AF-NODE-01 | Open; deferred validation, no task block | +| AR-21 | MR-11: a non-elevated Windows node and an elevated client of the same user: user-SID pipe ownership succeeds, but an unidentifiable elevated peer is refused before API execution | Existing automated ownership tests do not create two processes at different integrity levels; owner deferred full end-to-end testing on 2026-10-15 (ledger 30) | User-performed Windows 11 cross-integrity run at final end-to-end acceptance; no automatic elevation or re-request now | AF-DP-01, AF-NODE-01 | Open; deferred validation, no task block | ## 2. Human prerequisites @@ -127,6 +127,15 @@ User-performed procedure at final end-to-end acceptance (not run in this mainten ` node stop --test-mode`. Preserve process identities, endpoint descriptor and logs before disposing only this temporary environment. No managed privileged task, second account, device-policy change or real credential cleanup is needed. +Producer component check added by MR-12: DesktopPlatform's `LocalRpcPipeOwnershipTests` uses the same user-SID rule and explicit +user-owned parent-child and service listeners. Its current-token Windows run passed 4/4; this does not prove the elevated branch. +At final end-to-end acceptance, from a matching built DesktopPlatform checkout, in the ordinary and then the user's elevated shell, +set `ARCFORGES_LOCALRPC_OS_STREAMS=1` (never the OS secret-store opt-in) and run +`dotnet test --project src/BuildingBlocks/ArcForges.LocalRpc/Tests/ArcForges.LocalRpc.Tests.csproj -c Release --no-build --filter-class ArcForges.LocalRpc.Tests.LocalRpcPipeOwnershipTests --minimum-expected-tests 4 --output Detailed`. +Keep the token-owner relation printed by the tests and require all four tests to pass without skips. Unset that local opt-in afterwards. +This tests the producer in each token; only the separate ordinary-node/elevated-client procedure above tests the actual pair. +ArcForges' adoption of the producer version is MR-43. See [MR-12 evidence](maintenance/2026-10-25-mr12-validation.md). + Residual risk: no retained run proves the actual ordinary-node/elevated-client peer-resolution/refusal combination. Close only with that two-process evidence and review of the observed result. AR-02 second-account isolation and the deferred positive auto-start leg remain separate and open; this entry neither proves nor changes them. diff --git a/delivery-graph.json b/delivery-graph.json index 2740a87..2136913 100644 --- a/delivery-graph.json +++ b/delivery-graph.json @@ -582,7 +582,8 @@ ], "future_validation": [], "acceptance_register": [ - "AR-02" + "AR-02", + "AR-21" ], "human_prerequisites": [], "risks": [ diff --git a/ledger/ledger.jsonl b/ledger/ledger.jsonl index b931185..bccfa7a 100644 --- a/ledger/ledger.jsonl +++ b/ledger/ledger.jsonl @@ -132,3 +132,4 @@ {"at": "2026-10-25T16:20:01Z", "task": "AF-CLD-05", "kind": "note", "text": "MR-08 future obligation assigned in acceptance while task stays planned: on the real signed remote-to-node path, REMOTE cancel_task of a nonterminal ADOPTED task must be denied as process.terminate before any stop, leave the process running and audit denial; medium risk, step-up and approval cannot bypass REMOTE DENY. The delivered local gate is already correct; real remote integration is not implemented or claimed verified in this maintenance pass."} {"at": "2026-10-25T16:22:16Z", "task": "AF-AI-01", "kind": "note", "text": "MR-09 assigned, not implemented: re-read ledger 88, current ClientSessions/empty AgentRoots and Design 11 section 1 at main c36bf89. AF-AI-01 acceptance now requires registered-root verified CLI descendants to stay AGENT despite pty/console, with effective permission/approval coverage on native Windows and Linux (F-A02). Added the security Design reference and notes describing the pre-registration HUMAN consequence and residual missing-root/escaped-ancestry same-user bypasses; no hostile-agent isolation claim, no real-client validation claimed, task stays planned. No feature implementation or task activation."} {"at": "2026-10-25T16:26:12Z", "task": "AF-NODE-01", "kind": "note", "text": "MR-11 deferred validation AR-21, not a new runtime repair: current ArcForges main 5eaee92 retains user-SID pipe owner check introduced 3c323b2 and three PipeOwnershipTests, rejecting other user/group/SYSTEM/null owners. Retained native Windows CI at reviewed head 8fe744f passed Client.Tests 102/102 without skips; no fresh cross-integrity evidence claimed. Client pipe ownership and node peer resolution are separate: current NodeClient refuses after three dropped hellos with AccessDenied and elevated-client guidance. Actual non-elevated-node/elevated-client combination remains unproven and follows owner ledger-30 final end-to-end deferral, without re-asking or elevating here. AR-21 now records evidence, isolated test-mode procedure, expected fail-closed peer refusal and closure limits, linked from AF-NODE-01; task remains delivered, no task block. MR-12 DesktopPlatform, AR-02 second-account and positive auto-start remain separate. No real credential-store operation or privileged managed task."} +{"at": "2026-10-25T16:51:13Z", "task": "AF-DP-01", "kind": "note", "text": "MR-12 producer repair fixed: DesktopPlatform#200 merged e02ebe455d4fe2c06b19dabc0f3179a43ab21aa6 at reviewed head 66ffbc7c6ec38dfa52e0f909df48f9122ffe1668, tree 46768fc94d3f45383f8d008d1e433aebd5b47cb2, base 49e2dbe. Client checks connected pipe owner against token User before HTTP/2; foreign/group/missing SIDs fail closed, failed queries/refusals dispose the stream. Parent-child listeners set explicit user-SID owner/protected user-only DACL while preserving first-instance binding; service ACL/peer-resolution policy and public API unchanged. Native Windows author ownership 4/4, existing ACL 2/2, generated-call real-pipe 1/1, routine LocalRpc 947 passed/57 skips; both affected AOT probes compiled without warnings and native service-mode probe passed. Actual token default owner equals user SID; no cross-integrity or second-account proof, no elevation or real credential-store operation. Initial CA2000 build diagnosed/fixed structurally without suppression; first formal head 16b33c0b failed both legs on missing new-test provenance entry, correctly recorded as failed/incomplete. Final inventory-only correction classifies that one authored path, no policy/pin/receipt change. Fresh final native Windows full 17 steps/532 s and Linux full 24 steps/406 s passed, all results/same head-tree-base/live required successes/owner-skipped checks read before merge. Formal LocalRpc Windows 947 passed/57 skips, Linux 946 passed/53 skips; 21 local candidate packages verified, not a public publication claim. Full diff directly self-reviewed by OpenAI Codex alone per owner instruction, no blockers (6400304577). Clean DesktopPlatform primary fast-forwarded, worktree retained. Main-push Publish NuGet run 40213555173 at e02ebe4 observed in progress, publication not yet claimed successful; consumer adoption remains MR-43 with normal published-cohort admission. AR-21 producer procedure/AF-DP-01 link added while task remains delivered; evidence maintenance/2026-10-25-mr12-validation.md. MR-16 blocked and recovery proposal retained; no later feature activation, LAN/cloud MCP or database migration."} diff --git a/maintenance/2026-10-24-repair-list.md b/maintenance/2026-10-24-repair-list.md index 1633a3d..5c1f32f 100644 --- a/maintenance/2026-10-24-repair-list.md +++ b/maintenance/2026-10-24-repair-list.md @@ -38,7 +38,7 @@ managed tasks, no option that disables protection to enable elevation. The stop | MR-09 | An agent CLI with a pseudo-terminal is classified HUMAN until agent roots are registered (documented bypass, Design 11 §1). | 88 | Plan | AF-AI-01 acceptance names agent-root identification (F-A02) but not this consequence | assign: AF-AI-01 record names the obligation | assigned: AF-AI-01 acceptance now requires registered-root CLI descendants with a pty/console to remain AGENT, including effective permission/approval coverage on Windows and Linux (F-A02); notes name the current missing-root consequence and residual same-user bypasses, Design 11 reference added; task stays planned, no implementation or real-client validation claimed; ledger AF-AI-01 note | | MR-10 | The interim origin trusted a self-declared client kind. | 59 (1), 64 | ArcForges | AF-SEC-01 verifies client kinds against the peer's executable (ArcForges#31; ledger 87) | resolved | resolved: ArcForges#31 (merge 2180053); `ClientSessions` assigns the origin from the client kind verified against the peer's executable (`ClientImages.Verifies`); ledger AF-SEC-01 delivered | | MR-11 | Cross-integrity case: a non-elevated node with an elevated same-user client (resolution and acceptance entry). | 20, 30 | ArcForges, Plan | ArcForges.Client owner-SID check fixed in AF-NODE-01; no acceptance-register entry *(confirm)* | fix: test if missing; acceptance-register entry for the real elevated case (deferred validation) | deferred validation (AR-21): ArcForges.Client user-SID repair and three existing ownership tests confirmed (retained Client.Tests 102/102 at 8fe744f); real non-elevated-node/elevated-client peer identification/refusal remains unproven, owner final end-to-end deferral retained; procedure and AF-NODE-01 register link added, no elevation performed; DesktopPlatform MR-12 separate; ledger AF-NODE-01 note | -| MR-12 | DesktopPlatform `LocalRpcClientChannel` CurrentUserOnly check refuses an elevated same-user client (the defect ArcForges.Client fixed locally). | 30 | DesktopPlatform | *(confirm in DesktopPlatform main)* | fix in DesktopPlatform; adoption by ArcForges recorded separately (MR-43) | open | +| MR-12 | DesktopPlatform `LocalRpcClientChannel` CurrentUserOnly check refuses an elevated same-user client (the defect ArcForges.Client fixed locally). | 30 | DesktopPlatform | *(confirm in DesktopPlatform main)* | fix in DesktopPlatform; adoption by ArcForges recorded separately (MR-43) | fixed in producer: DesktopPlatform#200 (merge e02ebe4), reviewed head 66ffbc7; explicit user-SID client check and parent-child pipe owner, foreign/group/missing owners denied; native Windows full 17 steps/532 s and Linux full 24 steps/406 s passed; normal NuGet publication run 40213555173 started, not yet claimed successful; consumer adoption MR-43 separate; AR-21 real cross-integrity validation remains deferred; [evidence](2026-10-25-mr12-validation.md); ledger AF-DP-01 note | | MR-92 | Lost `JobRemoved` signal: `SystemdManager`'s handler looked for the job's waiter and recorded the result only when it found none, while `WaitJob` registers its waiter and then reads the record; a wait registering in between missed the end and ran to its timeout although the job had finished (seen as a 10 s adoption refusal in `AdoptionTests`; an attempt's 90 s start wait could record 'did not start' for a running attempt). | found in MR-76 (Linux leg at bbd4ddb) | ArcForges | live-manager probe: 68 of 20,800 waits missed on main, 0 of 28,800 with the fix | fix | fixed: ArcForges#40 (merge 64e9f7c); ledger AF-EXEC-02 note | | MR-95 | Timeouts and CPU-time limits never fire for an attempt recovered STARTING: the supervisor's timeout and CPU-time checks require `attempt.StartedTimestamp`, which only the change to RUNNING sets, and recovery reads `row.StartedAt`, so a command that a recovered launch starts (during the node gap or after) runs without `timeout_ms` or a CPU-time limit, contrary to 04 section 6 and section 9. | found in MR-89 review (ArcForges#42) | ArcForges | `ExecutionSupervisor.cs` timeout/CPU check and recovery (lines cited in #42's review record) | fix: give a recovered STARTING attempt a start time once its command is known to run; fault test with a restart in a launch step | open | | MR-99 | The persisted attempt states diverge from SM-ATTEMPT: `RecordFinished` writes FINISHED from whatever state the attempt is in, so FINALIZING is never persisted; timeout, CPU-limit, output, disk and shutdown stops persist no STOPPING (SM-ATTEMPT names timeout and CPU limit as RUNNING -> STOPPING triggers), so a crash during such a stop recovers the attempt as RUNNING; STARTING is persisted at admission, before the container exists, and CREATED is never persisted. The model test checks pairs and allows every state -> FINISHED, so it catches none of this. | found in MR-04 (worker and review) | ArcForges, Design | `ExecutionSupervisor.cs` RecordFinished, Stop, admission; `TaskLifecycleModelTests` | decide per divergence: persist the specified states (code) or amend the triggers (Design, reviewed); tighten the model test | open | diff --git a/maintenance/2026-10-25-mr12-validation.md b/maintenance/2026-10-25-mr12-validation.md new file mode 100644 index 0000000..81a8ed3 --- /dev/null +++ b/maintenance/2026-10-25-mr12-validation.md @@ -0,0 +1,78 @@ +# MR-12: LocalRpc named-pipe user ownership + +The producer repair is [DesktopPlatform#200](https://github.com/ArcForges/DesktopPlatform/pull/200), reviewed head +`66ffbc7c6ec38dfa52e0f909df48f9122ffe1668`, tree `46768fc94d3f45383f8d008d1e433aebd5b47cb2`, +base `49e2dbe9dcb0d268e16cbeca237f5f4284d50b9a`. + +## Repair and scope + +The client previously used PipeOptions.CurrentUserOnly, which checks the token's default owner rather than its user SID. An elevated +token's default owner can be BUILTIN\Administrators, so it refuses a service pipe owned by the same user's SID. The connected server +handle's owner is now checked against WindowsIdentity.User before returning a stream to HTTP/2. Foreign users, group owners and +missing SIDs fail closed. A query or refusal failure disposes the pipe and remains a pre-stream LocalRpcConnectException; no request +bytes have been sent. No new public API, dependency or native import was added. + +A parent-child listener previously also relied on the token's default owner. It now explicitly sets the user-SID owner and protected +user-only DACL on every instance, retaining FirstPipeInstance. This prevents the stricter client from refusing an elevated parent's +own listener. Service-mode ACLs, remote rejection and peer-resolution policy are unchanged. The raw ACL probes no longer apply a +second, different default-owner check; their server access-list/owner assertions remain. Current README and wrong-user procedures +name the explicit user-SID rule. No current Design contract change was required: the existing same-user boundary is restored. + +The newly authored test file is explicitly classified as first-party in the existing provenance inventory. No reused record, historical +receipt, policy, pin, dependency admission or verification script was changed. ArcForges's use of a new producer version is separate +MR-43; this repair does not claim consumer adoption. + +## Author evidence, native Windows through MCP + +- Locked affected restore; Release build with zero warnings/errors; changed-file format verification. +- LocalRpcPipeOwnershipTests: 4/4. SID input cases deny foreign/group/SYSTEM/null values. Real parent-child and service listeners each + carry bytes over three instances after the user owner check; a default-owner pipe is accepted only when that owner is the user. +- Existing parent-child and service ACL checks: 2/2, unchanged assertions. +- NamedPipeCarriesGeneratedCallsAndSurvivesMalformedFramesOnTheRealOsStream: 1/1 using real pipes and generated RPC calls. +- Routine LocalRpc suite: 947 passed, 57 documented platform/local-opt-in skips, zero failures (1004 total). +- Locked restores and Native AOT publish of LocalRpcAotTests and LocalRpcServiceAotTests passed without warnings. The native service + probe then passed real Windows named-pipe, same-process peer identity, raised deadline and stream-lane/heartbeat checks. + +These runs used a token whose default owner equals its user SID. The elevated default-owner branch and an actual ordinary-node / +elevated-client pair were not exercised. SID input cases are unit coverage, not substitute OS tokens or proof of cross-user denial. +AR-21 retains the real cross-integrity procedure and now names the producer's four-test class in each user-provided token. AR-02 +second-account isolation remains separate. There was no elevation or real credential-store operation. + +## Failures diagnosed and retained + +The first author build failed CA2000 after adding token lifetime in the pipe-transfer method. Keeping that lifetime inside the +owner-query helper resolved the warning; the analyzer remains enabled. The successful Release build above followed that source fix. + +At initial pushed head `16b33c0b6d16a17027c3f78c023ba2a8fd192df5`, Linux provenance-policy failed on the missing new-test inventory +entry (21 s leg), and Windows native-stage reached the same assertion (61 s leg). Both retained summaries are failed/incomplete; +subsequent steps did not pass. Exact tracked-minus-classified comparison found only the new test file, which the follow-up registered. +No runtime source changed after its author checks. Both final-head formal legs were started after the old leg on that host had ended; +the residual Linux dotnet processes were reusable MSBuild workers, not another build/test run. No old status was reused. + +A transient Windows MCP transport error during record preparation was recovered by a read-only log/branch inspection: the branch +creation had not happened and the background verification continued. No test or verifier was re-launched for that tool error. + +Author logs: Windows `C:\MyFile\Projects\dev\logs\mr12-author*` and `mr12-extra*`; first failed formal wrappers `mr12-formal*`, +final wrappers `mr12-final*`. Both hosts retain their own summary and step logs; no build artifacts were moved between hosts. + +## Final post-push verification and merge + +Both hosts fetched the final head above and ran their own complete profile through native MCP using `python eng/verify.py run-and-report`: +Windows 17/17 steps, 532 s; Linux 24/24 steps, 406 s. Every selected result passed, with matching head/tree/base. Formal LocalRpc totals +were Windows 947 passed / 57 documented skips (1004 total), Linux 946 passed / 53 documented skips (999 total; platform-specific theory +rows), zero failures. Windows verified all 21 candidate packages at local-only version 1.0.0-ci.1.1; that is not a public NuGet version. + +Summaries retained on their own hosts under DesktopPlatform/.git/arcforges-verify/logs: +- Windows: `20261025-163937-66ffbc7c6ec3-win/summary.json`. +- Linux: `20261026-003932-66ffbc7c6ec3-linux/summary.json` (the host's local date had advanced to October 26). + +Complete summaries/all selected results, live exact-head required success statuses, owner-skipped hosted checks, head/base and freshly +fetched main were read before merging. Full nine-file direct self-review by OpenAI Codex alone, comment 6400304577, had no blocking +finding; no independent reviewer is claimed under the owner's no-other-model/no-agent instruction. +DesktopPlatform#200 merged as `e02ebe455d4fe2c06b19dabc0f3179a43ab21aa6`; its clean primary checkout was fast-forwarded and the repair +branch/worktree retained. No work in other repositories was discarded. + +The normal main-push [Publish NuGet run 40213555173](https://github.com/ArcForges/DesktopPlatform/actions/runs/40213555173) was observed +in progress at merge commit e02ebe4, run 148/attempt 1, started 2026-10-25T16:49:54Z. Publication is not yet claimed successful here. +MR-43 must use a successfully published producer cohort with the normal admission evidence; no package was reinstalled or downloaded +for post-publication verification. AF-DP-01 remains delivered, with AR-21's remaining real cross-integrity validation linked.