feat(prd-023e): app-scoped isolated lanes per desktop app#9
Open
thenotoriousllama wants to merge 6 commits into
Open
feat(prd-023e): app-scoped isolated lanes per desktop app#9thenotoriousllama wants to merge 6 commits into
thenotoriousllama wants to merge 6 commits into
Conversation
Foundation modules for app-scoped isolated lanes; no router/dashboard wiring yet (wave 2-3). - app-targets.ts: DesktopAppLane model + DESKTOP_APP_LANES table (claude-native app-scoped-proxy, claude-legacy-gateway config-only, codex-desktop config-only) with getLaneById/laneOwnsHost/laneForApp/ lanesForHost. Egress exclusivity: api.openai.com never in the claude lane, api.anthropic.com never in the codex lane. - os-proxy.ts: real WindowsOsProxyAdapter + MacosOsProxyAdapter (snapshot/apply/restore via reg query/networksetup), createOsProxyAdapter factory; noopOsProxyAdapter unchanged. Hard-rollback snapshot seam. - rival-apps.ts: detectRunningRivalApps (ChatGPT/Codex), formatRivalAppWarning, assertNoRivalAppsRunning(override?) — the global-proxy block gate. - app-launch.ts: openClaudeAppWithProxy / launchOrRestartClaudeAppWithProxy via Electron --proxy-server (per-app, never system proxy); _internals testability seam extended. Tests: 47 new (app-targets 13, os-proxy 18, rival-apps 9, app-launch 7). typecheck clean; full desktop suite 12 files / 89 tests pass.
- 4 new dashboard endpoints: claude-desktop/revert (legacy gateway cleanup via app-session), codex-desktop/revert, codex-desktop/stop (per-lane, never touches claude-native state), server/kill (closes only owned runtime handles, never user/OS processes). - Rival-app global-proxy block in dashboardClaudeNativeStart: refuses native start with 409 rival_apps_running when ChatGPT/Codex desktop is running, unless the caller passes rivalOverride:true (the explicit override path). - Swap noopOsProxyAdapter -> createOsProxyAdapter() in native uninstall so real snapshot/restore runs on Windows/macOS (hard-rollback seam). - DesktopActionResult/Category typed DTO for safe action responses. Tests: server-router 30 -> 35 (codex stop noop+isolation, server kill, rival-app block 409 + override, revert endpoints). typecheck clean; full suite 93 files / 815 tests pass.
- DashboardRuntime.lanes: Partial<Record<LaneId, LaneRuntimeState>>
(per-lane running/startedAt/proxySnapshot/lastError). Existing flat
fields kept; router depends on them.
- LaneRuntimeState, DesktopLaneStatusDto, DesktopAppsStatusDto typed DTOs.
- buildDesktopLaneStatuses(runtime): iterates DESKTOP_APP_LANES, derives
running from lane state or legacy fields (claudeNativeTransport /
codexProxy). Wired into the settings payload as lanes.
- Desktop Apps UI: Revert legacy (claude-desktop/revert), Revert Codex
+ Stop Codex proxy (codex-desktop/{revert,stop}), Kill server
(server/kill, warns browser may disconnect). Lanes render as distinct
Claude-native / Claude-legacy / Codex sections (AC-023e-1).
Tests: server-dashboard 11 (lane status builder, claude-native running
from transport, codex running from proxy, DTO shape, rendered buttons).
typecheck clean; full suite 94 files / 821 tests pass.
…(wave 4) New tests/desktop-lane-isolation.test.ts (16 tests) tying together the lane primitives, rival-app detector, OS-proxy factory, and per-lane runtime status: - egress exclusivity (AC-023e-5/27): claude lane never owns openai, codex lane never owns anthropic; lanesForHost resolution. - rival-app block (AC-023e-6/24): detectRunningRivalApps + assert- NoRivalAppsRunning (throws w/ 'blocked', override path no-throw, execSync-failure safe-default). - per-lane isolation (AC-023e-11..13/28): stopping one lane's runtime does not affect the other; both can run independently; codex never reports native-interception. - OS proxy factory (AC-023e-2..3): noop contract + platform-correct instance selection; no real OS mutation. ESM-safe: vi.mock w/ importOriginal (transitive spawn bind requires real spawn preserved); platform override + restore in afterEach. Full suite: 94 files / 837 tests pass.
…ation The macOS networksetup interpolation was entirely unquoted, and the Windows reg add + read-path host (\S+ regex) accepted arbitrary values. A crafted or corrupted host read from OS output or an external snapshot could inject shell arguments. Add assertSafeProxyHost (strict [A-Za-z0-9._:-] charset) and assertSafeProxyPort (integer 0..65535) guards before every interpolation point in WindowsOsProxyAdapter.apply/restore and MacosOsProxyAdapter.apply/restore. The surrounding try/catch swallows the throw, so an unsafe value fails closed (command not executed) rather than injecting. PRD-023e security close-out (Critical finding). Verified: os-proxy 18 tests + full suite (94 files / 837 tests) green.
quality-worker-bee close-out (report authored inline after agent timeout;
evidence captured from green gates + file:line inspection).
Verdict: VERIFIED-WITH-WARNINGS. The lane/isolation contract is
implemented, tested (94 files / 837 tests), and security-remediated.
AC tally: 11 MET, 1 PARTIAL, 3 UNMET.
- MET: lanes distinct (e-1), per-app-only proxy (e-2/3), egress
exclusivity (e-5), rival-app block before mutation (e-6), per-lane
stop isolation (e-11..13), codex never native (e-12), gates (e-15).
- PARTIAL: hard-rollback snapshot seam exists (real OsProxyAdapter
wired, hardened) but router doesn't yet populate runtime.lanes (G3).
- UNMET: Windows launch-options qa/ note (e-4/14, G1) and committed
real-Anthropic-turn observation (e-9/10, G2) — both documentation /
investigation deliverables the original plan scoped as 'later', not
safety-contract code.
Three honest gaps documented with recommendations; none block the
safety guarantee ('a real Claude turn routes without putting OpenAI
desktop traffic at risk').
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements PRD-023e — app-scoped isolated lanes per desktop app: the safety contract that makes "a real Claude Desktop turn route through rflectr without putting OpenAI desktop traffic at risk" an enforceable, tested guarantee rather than an aspiration. This is the layer the original mission identified as the missing piece between "the product path works" and "safe to ship."
Built via the Beekeeper dispatch:
typescript-node-worker-bee(implementer, 4 waves) →security-worker-bee(close-out) →quality-worker-bee(QA).What landed (6 commits)
Wave 1 — lane primitives (
feat(desktop-interception): PRD-023e lane primitives)app-targets.ts:DesktopAppLanemodel +DESKTOP_APP_LANEStable (claude-native = app-scoped-proxy preferred; claude-legacy-gateway + codex-desktop = config-only).laneOwnsHost/lanesForHostenforce egress exclusivity —api.openai.comcan never satisfy the Claude lane.os-proxy.ts: realWindowsOsProxyAdapter+MacosOsProxyAdapter(snapshot/apply/restore viareg/networksetup),createOsProxyAdapterfactory. Hard-rollback seam.rival-apps.ts:detectRunningRivalApps(ChatGPT/Codex),assertNoRivalAppsRunning(override?)— the global-proxy block gate.app-launch.ts:openClaudeAppWithProxyvia Electron--proxy-server(per-app, never a system proxy switch); refusesshell:AppsFolderpackaging rather than falling back.Wave 2 — router (
feat(server): PRD-023e dashboard endpoints + rival-app block)claude-desktop/revert,codex-desktop/revert,codex-desktop/stop(per-lane, never touches claude-native state),server/kill(closes only owned runtime handles).dashboardClaudeNativeStart: refuses with 409rival_apps_runningwhen ChatGPT/Codex desktop is running, unless the caller passesrivalOverride: true(explicit override path).createOsProxyAdapter()swap in native uninstall (wasnoopOsProxyAdapter).Wave 3 — dashboard (
feat(dashboard): PRD-023e per-lane state + DTOs + controls UI)DashboardRuntime.lanes(typed per-lane state),LaneRuntimeState,DesktopLaneStatusDto,buildDesktopLaneStatuses.Wave 4 — integration tests (
test(desktop): lane isolation + rival-app block integration)tests/desktop-lane-isolation.test.ts(16 tests) tying the lane primitives, rival-app detector, OS-proxy factory, and per-lane runtime status together.Wave 5a — security (
security(os-proxy): harden proxy host/port)networksetupinterpolation was entirely unquoted; a crafted host read from OS output could inject shell args. AddedassertSafeProxyHost/assertSafeProxyPortguards before every interpolation; fails closed.Wave 5b — QA (
docs(qa): PRD-023e quality close-out)library/.../prd-023-desktop-apps-native-controls/qa/2026-06-30-prd-023e-quality-closeout.md.Verification
npm run typechecknpm testAC tally (full matrix in the QA report)
11 MET — lanes distinct, per-app-only proxy, app-scoped-or-nothing refusal, egress exclusivity, rival-app block (fires before mutation), per-lane stop isolation, codex-never-native, gates green.
1 PARTIAL — hard-rollback snapshot seam exists and is hardened, but the router doesn't yet populate
runtime.lanes(lanes work via fallback to legacy fields).3 UNMET (documentation/investigation deliverables, none block the safety contract):
qa/investigation note (AC-023e-4/14): the mechanism is implemented; the write-up is the gap.Recommended follow-ups (not in this PR)
RFLECTR_LIVE_DESKTOP=1) to regression-protect the real-turn observation. This fully closes the original mission.runtime.lanesin the router start/stop handlers.