cmux NEXT: keep incognito workspaces off the phone

Merged
TE
5d ago
+211-17

Problem

A paired phone listed an incognito window's workspace (its title, folder and terminal titles) and could replay, type into, rename and close its terminals over the shipped-iOS compat RPC (spaces-incognito-audit leak 3). Leo asked for incognito spaces on 2026-10-31. The spec (leo-workspace design/spaces.md, "Incognito") says: "Normal and incognito content never mix", and nothing of the window is kept.

Change

  • PhoneAccessGate holds the app's set of hidden workspace keys (setHiddenWorkspaces / hidesWorkspace, case-insensitive).
  • MobileWorkspaceRows.result leaves hidden workspaces out of the list. locate(surfaceID:) skips them, so terminal replay, input, paste, rename, close and viewport on one of their terminals answer not_found. workspace.close, workspace.action and terminal.create that name a hidden workspace also answer not_found.
  • The app publishes the set each time its incognito windows change (recordIncognitoWorkspaces, through the new PhoneIncognitoHiding). The set covers claimed workspaces still being created and every ephemeral workspace the local daemon flags.

Relay policy (GHSA-9vmv-3hjw-j28c): no v2 socket method is added or allowlisted. This change only narrows what the compat session answers.

Tests

  • Commit 1 adds failing tests only: IncognitoPhoneTests and IncognitoSpaceTests.phonesAreToldToHideIncognitoWorkspaces.
  • Commit 2 adds the fix.
  • The fleet package suite runs red at the test commit and green at the head.
  • The god-file ratchet passes; WindowManager is net zero in lines.

Not covered (residual)

  • The daemon-lane path (a raw socket splice through DaemonLanePolicy) still answers list-workspaces and similar calls for newer iOS builds unfiltered. The fix belongs in the daemon: hide ephemeral workspaces from remote: true clients.
  • Feed and iPhone push items for incognito notifications are not filtered here.
  • There is a short gap before the daemon's ephemeral flag reaches the app for a workspace that no window lists yet.

🤖 Generated with Claude Code

Agent session: agent session