Repository navigation
Description
Related to #424.
A controlled native desktop acceptance test found that a session-level external-folder approval survives a normal desktop restart when the app reattaches to its shared agent daemon. The Folder access dialog currently promises that grants expire when the app restarts.
Reproduction
Tested the native Electron app and shared biorouterd agent built from aca71f458145bf28a238367a7bacb820f65e5782, with a fresh isolated profile, synthetic files only, and no PHI or real database. The test driver used a deterministic loopback provider to request ordinary Developer reads; the actual model drove the app UI and clicked the normal permission cards.
- Open a chat with Project A as its workspace.
- Request a read of a synthetic sentinel in external Project B and approve read-only access for this chat using the normal card.
- Confirm Folder access lists Project B and states: “Grants expire when revoked, the chat is deleted, or the app restarts.”
- Quit Electron normally with Cmd+Q. The shared daemon remains running, which is expected for shared-agent operation.
- Restart Electron with the same isolated profile and reopen the same chat.
- Folder access still lists Project B. A new read of its sentinel succeeds without another approval card.
This is an observed expiry/lifetime mismatch. The read-only scope remained enforced in the earlier write-upgrade denial control; this report does not claim a write-scope bypass.
Source localization
The shared-daemon Electron detach path closes the local proxy and unreferences the daemon. Reattachment reads the retained daemon proof and maps the new per-launch renderer proof to it. FolderAccessManager keeps session grants in daemon memory keyed by chat ID and currently removes them on explicit revoke or chat deletion; no desktop-client lifetime is associated with the grant. A fresh Electron launch therefore reuses the surviving daemon grant.
Recommended fix preserving normal UX
Bind desktop-created session grants to an authenticated native-client lifetime. Revoke that client's grants on a normal detach and retire stale grants when its desktop profile starts a new native-client generation, so crash/restart also cannot silently preserve them. Keep the shared daemon, CLI activity, and grants belonging to other active clients running. The next external access should show the existing scoped approval card and resume after approval; ordinary in-workspace file operations should be unchanged. Do not broaden trust to model arguments or renderer-supplied owner IDs and do not change the expiry promise to hide the mismatch.
Acceptance controls
- Normal Cmd+Q and same-profile restart remove the previous desktop client's grants; the next external read prompts and resumes after approval.
- A crash/restart or stale client generation cannot resurrect the prior grant.
- Closing one desktop client does not revoke another active client's grants or stop shared/CLI work.
- Read-only, Allow once, Deny, Stop, explicit revoke, and chat deletion retain their existing behavior.
- An untrusted request cannot claim another client's lifetime or revoke its grants.
Raw isolated UI/session receipts are retained in the assessment evidence directory under ui-native-aca-restart-{grant-retained,read-without-new-consent}-fail-01.json and session-native-aca-restart-retained-grant-fail-01-20261024_10.json. This issue records a native runtime failure, not a source-only prediction. The corrected behavior has not yet been tested.
Activity
Broccolito commentedon Oct 24, 2026
The correction preserves the managed desktop promise that folder grants end on app quit/restart. Managed shared desktop grants are being bound to the authenticated main client's live private connection, with owner-specific revocation, retained-capability checks, pending-request cancellation, and fresh approval after reconnect. Other active clients and the shared daemon remain usable.
Deployment boundaries are explicit:
Related requirement: #424; native restart defect: #455. Feature documentation and the self-test workflow include separate headed desktop quit/restart, abrupt disconnect, reconnect, and two-client acceptance gates. The original actual native failure remains evidence. Source changes and focused automated checks are in progress; final native artifact acceptance and current-head CI have not yet passed, and this comment does not close either issue.
Broccolito commentedon Oct 24, 2026
Issue #455: Expire managed-desktop folder grants when the native client ends
Issue · Host PR #454 · merged main source
Historical desktop/browser/CLI observations used f227 artifacts, accepted via the reviewed production-equivalence comparison; exact-main CI/build/smoke is attributed separately below.
Problem. Normal desktop quit/reopen reused a surviving shared daemon and incorrectly retained the old client's external-folder grant.
Fix and UX. Server-minted native-client lifetime requires trusted proof and matching daemon instance over the verified private Unix socket. Lifetime end revokes that owner's session/once capabilities and pending waits; explicit owner context propagates through spawned work. Other clients/shared work are preserved. Reopening requests fresh consent and can resume; unproven shared backends do not silently become ownerless managed grants.
Validation. Final actual native L1 normal quit/reopen expired the grant while the same daemon survived; L2 quit during pending write cancelled it and required fresh consent; L3 forced verified Electron-main termination required renewed consent; L4 kept a same-chat grant usable in a second/surviving real window. Native P1 also required fresh Allow once after full app/backend relaunch. Owner/once/pending regressions, native-core 2, mounted-server 5 (one socket integration plus four profile guards), pending-approval 31 and UI 72 passed. Other-client isolation is software evidence, not an extra two-native-app experiment. Current Rust/UI CI is separately linked; the original ACA extra-read failure remains preserved.
Limits. No actual all-windows/tray or native Delete Chat UI case was performed. Browser/manual external-backend grants are backend-lifetime; browser reload preserved access and explicit Revoke reprompted, while optional backend-restart case was not run. Windows uses its owned-daemon path, not shared Unix transport; no Windows GUI lifetime acceptance is claimed. Revocation cannot undo completed edits/in-flight OS effects. A separate shutdown acknowledgment warning remains recorded: one normal stop returned 1 despite daemon absence; three CLI and one Electron follow-up cycle returned 0. Its cause is unresolved and no fix is claimed.
Recommendation. Preserve exact-owner revocation and mode-aware lifetime wording; expire one native client without terminating unrelated shared work.
Merged-main verification. PR454 is merged at
0580110; main Rust CI, frontend CI and related checks finished 20 successes/one skipped (17 push successes plus three landing/deployment checks; these are not security-test counts). Normal main build manifest SHA256:38cc2979c7da104cb623975bac7f6922a085e834a1be5c18e961d7f26ea811fc; shipping receipt:e6e1d8957d8d0b22a9aae814011a1e028c5d279848ffabbf4c6d0c4edf3c0112. Actual main01 smoke separately recorded four pairs—three reads/one typed Deny—and three unchanged canaries; it does not rerun/recount the historical 81-pair cohort.Submission record. Manuscript v1.1 local Supplementary Data S1: Supplementary Data S1 - BioRouter Security Audit - 2026-10-24.zip; manifest SHA256 4f0c0c110ca0b07a29d5281870227f7b36c744a0f37ccb499b8ef1283dea116e. This is a user/submission package, not a claim of public upload. Public PR/source/CI links above provide the public implementation proof.