Skip to content

feat(auth): allow passive terminal observation - #9791

Open
juliusmarminge wants to merge 14 commits into
t3code/auth-diagnostics-scopefrom
t3code/auth-terminal-read-scope
Open

feat(auth): allow passive terminal observation#9791
juliusmarminge wants to merge 14 commits into
t3code/auth-diagnostics-scopefrom
t3code/auth-terminal-read-scope

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Sep 4, 2026

Copy link
Copy Markdown
Member

Watching terminal output currently requires terminal:operate, which also allows starting shells, sending input, resizing, clearing, restarting, and closing sessions. A client cannot be granted terminal viewing without those controls.

Add terminal:read and a passive terminal.observe stream. Observation reads an existing session's history and live events without spawning, restarting, resizing, writing, or changing its working directory or environment. Retained exited sessions can be observed; missing sessions return an error. terminal.attach keeps its existing behavior and still requires terminal:operate.

Web, desktop, and mobile use passive observation for read-only grants and disable terminal mutations. Native iOS and Android views guard software and hardware keyboard input, terminal-generated replies, and keyboard focus. Losing operate permission dismisses native input; client callbacks guard input and resize requests too.

Output and metadata subscriptions now require terminal:read; terminal:operate alone no longer grants those subscriptions. New read-only, standard, and administrative grants include the read scope. Existing grants are unchanged, so clients must explicitly pair again to receive it.

Terminal manager tests and ten focused server RPC tests pass. The integrated stack also passes 186 focused web tests and 20 contract, shared, and mobile tests. The manager cases cover history and live output without process changes, observing exited sessions without restarting, and rejecting missing sessions. The RPC cases verify that observers can subscribe while terminal mutations remain denied.

Android development APK builds successfully with the native read-only changes. Web, mobile, and server typechecks passed; Android emulator verification confirms live passive output, no keyboard on tap, and injected input absent from host terminal history.

Before After
Before: terminal operation only After: separate terminal viewing permission

Android client paired with only orchestration:read and terminal:read:

Android: passive terminal output with input disabled

Model: GPT-6. Harness: Codex.

Note

Add terminal:read scope for passive terminal observation

  • Adds a new terminal:read authorization scope alongside the existing terminal:operate scope, and a terminal.observe streaming RPC backed by TerminalManager.observeStream that delivers an existing terminal's snapshot and live events without starting or mutating the process
  • Updates RPC authorization so observation, event, and metadata subscriptions require terminal:read, while attach, write, resize, clear, restart, and close still require terminal:operate
  • Updates web and mobile terminal UIs to render read-only surfaces for users with only terminal:read: input, paste, resize, split, create, and close are disabled or guarded; project-script execution requires terminal:operate; existing terminal sessions remain selectable
  • Adds native readOnly view props to iOS and Android terminal modules, suppressing keyboard focus and input events when enabled
  • Adds "View terminals" as a delegatable pairing scope and includes it in the "Read only" pairing preset
  • Risk: RPC_REQUIRED_SCOPES in RpcAuthorization.ts moves terminal event and metadata subscriptions from terminal:operate to terminal:read; any existing client relying on terminal:read-only tokens for those streams will now succeed where they previously failed, and any client without terminal:read will lose access to event/metadata streams they had under terminal:operate alone

Macroscope summarized 33d5b21.


Note

High Risk
Splits terminal RPC scopes so clients with operate-only tokens lose event/metadata subscriptions unless re-paired with terminal:read; auth and PTY observation paths are security-sensitive.

Overview
Introduces terminal:read so clients can watch shells without terminal:operate. A new terminal.observe stream (and TerminalManager.observeStream) returns an existing session’s snapshot plus live output without spawning, restarting, resizing, or writing to the PTY; missing sessions still error.

Authorization now maps observe, subscribeTerminalEvents, and subscribeTerminalMetadata to read, while attach/open/write/resize/clear/restart/close stay on operate. Pairing adds a “View terminals” scope and includes it in the read-only preset; terminal:operate alone no longer covers event/metadata subscriptions.

Web and mobile attach with observe when the user only has read scope, gate writes/resizes/opens/splits/scripts, and drive readOnly on native surfaces (keyboard, paste, resize RPCs suppressed). Android/iOS expo modules honor readOnly on software/hardware input and terminal-generated replies. Web Ghostty sets input.readOnly and can resendSize once a writable session exists.

Reviewed by Cursor Bugbot for commit 33d5b21. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.5 KiB 15.1 KiB
Codex Thread snapshot wire 7.0 KiB 7.3 KiB
Codex Live turn WebSocket wire 6.5 KiB 7.8 KiB
Codex Live turn WebSocket decoded 57.0 KiB 66.4 KiB
Codex Live turn messages 8 21
Claude Total thread wire 13.6 KiB 15.1 KiB
Claude Thread snapshot wire 7.0 KiB 7.3 KiB
Claude Live turn WebSocket wire 6.5 KiB 7.8 KiB
Claude Live turn WebSocket decoded 57.8 KiB 66.4 KiB
Claude Live turn messages 9 21

Baseline: unavailable · PR result: 33d5b21 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@juliusmarminge juliusmarminge changed the title t3code/auth terminal read scope feat(auth): allow passive terminal observation Sep 4, 2026
Comment thread packages/contracts/src/auth.ts
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 1b83112 to 77bea7c Compare September 4, 2026 21:19
@juliusmarminge
juliusmarminge marked this pull request as ready for review September 4, 2026 21:25
@macroscopeapp

macroscopeapp Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds a new read-only terminal capability across server, shared RPC contracts, web, and mobile clients, while changing terminal authorization and default pairing/session scopes. Its broad runtime surface and authentication-sensitive permission changes require human review.

No code changes detected at 33d5b21. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 77bea7c to 755a0b0 Compare September 4, 2026 21:36
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 755a0b0 to a7693db Compare September 4, 2026 21:38
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from a7693db to a9d5aa3 Compare September 4, 2026 21:40
Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx
Comment thread apps/web/src/components/ProjectScriptsControl.tsx
Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx Outdated
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from a9d5aa3 to 12ffc87 Compare September 4, 2026 21:48
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Sep 4, 2026
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 12ffc87 to c391e46 Compare September 4, 2026 21:51
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from c391e46 to 8cf3080 Compare September 4, 2026 22:00

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit 8cf3080. Configure here.

Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 8cf3080 to 6deb383 Compare September 4, 2026 22:08
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 6deb383 to 43e5909 Compare September 4, 2026 22:15
Comment thread apps/web/src/components/ChatView.tsx Outdated
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 2644cae to b2be98d Compare September 4, 2026 22:59
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from b2be98d to 2de375b Compare September 4, 2026 23:52
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from 2de375b to 1e5fbea Compare September 5, 2026 00:08
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch 2 times, most recently from 2335ffb to d975869 Compare September 5, 2026 00:24
@juliusmarminge
juliusmarminge force-pushed the t3code/auth-terminal-read-scope branch from d975869 to 33d5b21 Compare September 5, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant