Skip to content

remote relay: permit scoped terminal paste - #14915

Merged
teamleaderleo merged 10 commits into
mainfrom
feat/remote-terminal-paste
Oct 1, 2026
Merged

teamleaderleo merged 10 commits into
mainfrom
feat/remote-terminal-paste

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Implements #14910.

cmux paste --submit already uses the right terminal path for multiline agent prompts, but terminal.paste was intentionally absent from the cmux ssh relay schema.

This adds it with a narrow remote contract:

workspace_id   exact authenticated relay owner
surface_id     exact live remote terminal owned by that workspace
text           String
submit_key     none | return

No window/focus fallback, arbitrary submit key, new shell command, or generic mobile RPC authority is added.

Authorization

The existing two relay gates remain authoritative:

  1. RemoteRelayCommandPolicy validates method/parameter shape before local-socket forwarding.
  2. RemoteRelayAuthorizationPolicy authenticates the relay generation and checks the exact live workspace/surface snapshot.

terminal.paste now participates in both.

The app-side dispatch gate re-captures the current remote topology before execution, so a moved/retired/localized surface loses paste authority just like surface.send_text.

Relay authorization analysis (GHSA-9vmv-3hjw-j28c)

Method added to the relay allowlist: terminal.paste, params workspace_id, surface_id, text, submit_key.

  • Can it execute commands or open content on local objects? No. It writes text into one terminal surface that the app-side gate has confirmed is live in the relay owner's remote topology, the same targets surface.send_text already reaches. It does not spawn or respawn terminals, open URLs, or run scripts. The text goes through Ghostty's paste encoder, which replaces unsafe control bytes (including ESC and DEL) with spaces and wraps the payload in bracketed-paste fences, so it can carry less than send_text, which writes raw bytes. submit_key is limited to none or return; return is the same Enter that send_text can already send as \r.
  • Can it mutate or destroy objects the remote session does not own? No. Both workspace_id and surface_id are required, both are existing scoped selector keys, and there is no window or focus fallback. RemoteRelayAuthorizationPolicy denies a surface outside the owner workspace, and the dispatch gate re-captures topology so a moved, retired, or localized surface loses authority. No new ID param names are introduced.
  • Does it read local state? No. The response reports only whether the paste and the optional submit were delivered.

Command-bearing params (command, initial_command, tmux_start_command, pane_start_command, ...) remain denied for this method, as do unknown keys such as window_id.

Policy tests:

  • RemoteCLIRelayPolicyTests: owned surface forwarded with none and return; denied with command, initial_command, window_id, submit_key: ctrl+enter, missing submit_key, non-string text, and a malformed surface_id, none of which reach the local socket.
  • RemoteRelayAuthorizationPolicyTests: owned surface allowed, foreign surface denied, arbitrary submit keys denied.
  • RemoteRelayCoreRPCPolicyTests and RemoteRelayTmuxCompatAuthorizationTests: syntax, capability discovery, and revocation after the surface leaves the relay topology.

Submit semantics

Remote relay callers can request only:

  • none — paste only
  • return — let the existing terminal handler select/send the agent-aware submit key

They cannot supply an arbitrary key chord through this method.

The existing paste handler still owns:

  • Ghostty bracketed-paste behavior;
  • unsafe control-byte sanitization;
  • queued terminal-start behavior;
  • text-first / submit-second result semantics.

Tests

Adds package-level coverage for:

  • exact owned surface admitted;
  • foreign surface denied;
  • malformed/missing paste fields denied;
  • arbitrary submit keys denied;
  • capability discovery includes the exact reviewed method.

Adds app-level relay coverage for:

  • admitted live terminal paste;
  • foreign terminal denial;
  • authority revoked when the terminal leaves the relay-owned remote topology.

Docs

Updates the CLI contract: cmux paste remains local by default and is available through an authenticated remote workspace only under the exact scope above.

Draft until package/app CI and an end-to-end git diff | cmux paste --submit check inside a cmux ssh workspace pass.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Permits terminal.paste through the cmux ssh relay for authenticated remote workspaces, scoped to an exact owned surface and submit_key of none or return. Previously paste was blocked on the relay; remote callers can now paste but cannot use fallback selectors or arbitrary submit keys.

  • Adds terminal.paste to both relay policies and the routing schema; the app-side dispatch gate re-captures remote topology so moved or retired surfaces lose paste authority.
  • Adds package-level, app-level, and policy-server tests covering owned/foreign surfaces, malformed fields, arbitrary submit keys, capability discovery, and authority revocation.
  • Updates the CLI contract to document the remote paste scope.

Written for commit 06b19e5. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 2 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: be0a6973-1cf9-461c-9dcb-071b64b58c2f

📥 Commits

Reviewing files that changed from the base of the PR and between aa4d529 and 06b19e5.

📒 Files selected for processing (8)
  • Packages/macOS/CmuxRemoteWorkspace/Sources/CmuxRemoteWorkspace/Relay/RemoteRelayAuthorizationPolicy.swift
  • Packages/macOS/CmuxRemoteWorkspace/Sources/CmuxRemoteWorkspace/Relay/RemoteRelayCommandPolicy.swift
  • Packages/macOS/CmuxRemoteWorkspace/Sources/CmuxRemoteWorkspace/Relay/RemoteRelayRoutingSchema.swift
  • Packages/macOS/CmuxRemoteWorkspace/Tests/CmuxRemoteWorkspaceTests/RemoteCLIRelayPolicyTests.swift
  • Packages/macOS/CmuxRemoteWorkspace/Tests/CmuxRemoteWorkspaceTests/RemoteRelayAuthorizationPolicyTests.swift
  • Packages/macOS/CmuxRemoteWorkspace/Tests/CmuxRemoteWorkspaceTests/RemoteRelayCoreRPCPolicyTests.swift
  • cmuxTests/RemoteRelayTmuxCompatAuthorizationTests.swift
  • docs/cli-contract.md
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Security note on relay equivalence with surface.send_text (added during the paste batch; this PR is item 5 of that batch, so I have not opened a duplicate):

  • Same targets as send_text, no wider. terminal.paste joins the same allowlist as surface.send_text in RemoteRelayCommandPolicy, RemoteRelayAuthorizationPolicy, and the routing schema. It requires the exact authenticated owner workspace_id and a live surface_id from that workspace's remote topology. It has no window or focus fallback, and it accepts no command-bearing params (initial_command, command, ...). The app-side gate re-captures topology, so a moved, retired, or localized surface loses paste authority the same way it loses send_text authority.
  • Content is less powerful than send_text. send_text writes raw bytes, including ESC, so it can already type anything a user can. terminal.paste goes through Ghostty's input/paste.zig encode, which replaces unsafe control bytes (including 0x1B ESC and 0x7F DEL) with spaces and then wraps the payload in bracketed-paste fences, or turns \n into \r when the program has not enabled bracketed paste. A relay caller cannot use it to forge the ESC[201~ fence terminator or inject escape sequences. It is therefore no riskier than send_text on the same surface.
  • Submit is bounded. submit_key accepts only none or return. return presses the same agent-aware Enter that send_text can already send as \r. Arbitrary chords are denied, and tests cover that.
  • It opens no new capability. It spawns no terminals, opens no URLs or content, reads no local state, and mutates no object the remote session does not own.

Tests in this PR cover the owned-surface allow, foreign-surface deny, missing/malformed field deny, arbitrary submit_key deny, capability listing, and revocation after the surface leaves the relay topology.

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits September 28, 2026 06:56
# Conflicts:
#	Packages/macOS/CmuxRemoteWorkspace/Sources/CmuxRemoteWorkspace/Relay/RemoteRelayAuthorizationPolicy.swift
Adds RemoteCLIRelayPolicyTests coverage for the reviewed terminal.paste
relay contract: an owned surface with submit_key none or return is
forwarded, and command-bearing params, window fallback selectors,
arbitrary submit keys, missing submit_key, non-string text, and
malformed surface IDs are denied before reaching the local socket.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Dogfood build of e6d813f636ba53be4f757dca0177f5f555912d1e

cmux DEV pr-14915-e6d813f6.app

The link opens this exact commit in the cmux dev menu bar app. The build starts on each push and the page waits until it is ready; a newer push replaces it. It signs in against production, so Cloud or backend changes still need a tagged build with a development backend.

Dogfood tours of e6d813f6

sidebar-and-chrome-tour at e6d813f6, on its merge a29064e3 that CI built: passed (run)

sidebar-and-chrome-tour at e6d813f6

Key frames of sidebar-and-chrome-tour at e6d813f 04-three-workspaces 10-split-right 15-command-palette 24-settings

Tours are picked by the paths globs in dogfood/scenarios/*.json; a Dogfood-tours: a, b line in the description picks them instead (none turns this off). Look at every frame before merging: a green tour only means no step failed.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

CI failure attribution

CI failed on e6d813f636 (run 36702404955 attempt 1): no failed job besides the gates.

Not re-run automatically: only gate jobs failed.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's.

@teamleaderleo teamleaderleo added the needs a call Finished and held for a team design or product decision (see #13742 and the gallery in #15427) label Sep 30, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Pending security and product review of scoped remote terminal paste, plus a manual rebase onto current main. — Oolong g1 🌾

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo marked this pull request as ready for review October 1, 2026 22:25
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

FYI: the scoped relay contract and the “Relay authorization analysis (GHSA-9vmv-3hjw-j28c)” section remain as written.

@teamleaderleo
teamleaderleo enabled auto-merge (squash) October 1, 2026 22:25
@teamleaderleo
teamleaderleo merged commit eba3c42 into main Oct 1, 2026
73 checks passed
@teamleaderleo
teamleaderleo deleted the feat/remote-terminal-paste branch October 1, 2026 22:25
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Merge receipt for 06b19e5fee, merged 2026-10-01 22:25:39 UTC

  • Not verified at merge: app-host unit tests (in progress), ci-status (not reported)
  • Verified: macOS compile admission, CI fast guards, detect-ios-changes, Fast static checks, GhosttyKit release check, guards (19), ios-tests, late-placement, linux-preflight, macOS admission gate, package-conventions-lint, runner, and 3 more
  • Skipped by policy: admission-placement, browser, Claude wrapper regressions, CLI product tests, Dogfood build #​${{ github.event.pull_request.number }}, full-suite-coverage, ios-simulator, ios-simulator-build, mobile-core-package, release-admission, release-build, remote-daemon, and 7 more
  • Full suite: runs on main after merge.

Labeled merged-unverified: if main breaks near this merge, look here first.

@github-actions github-actions Bot added the merged-unverified A judging check was not green at merge; see the merge receipt comment label Oct 1, 2026
rustybret pushed a commit to rustybret/bmux that referenced this pull request Oct 1, 2026
0906bcb fix: make main's full test suite pass again (manaflow-ai#16429)
11bfe00 Restore custom sidebar preview gallery (manaflow-ai#16535)
343dd1b web: sync all Hexclave webhooks into a validated, order-independent mirror (manaflow-ai#16339)
00547d5 ci: avoid blaming unrelated merges for compile failures (manaflow-ai#16533)
b782440 fix(ci): provision Go for every iOS Release archive (manaflow-ai#16534)
3555618 Add a Jump to Bottom button to terminal panes (manaflow-ai#15382)
79febcf fix: tolerate delayed App Store Connect processing (manaflow-ai#16527)
fcbf13c fix: export Foundation for remote paste policy (manaflow-ai#16525)
6d86537 Add What's New recap with an off / quiet / sheet setting (manaflow-ai#14876)
256d964 fix(xcstrings): keep conflict resolutions valid JSON (manaflow-ai#16071)
8473bdc fix: upload pasted images into private SSH directories (manaflow-ai#16523)
53c705c Show opt-in model, context %, and estimated cost next to agent status in the sidebar (manaflow-ai#14855)
eba3c42 remote relay: permit scoped terminal paste (manaflow-ai#14915)
e447665 fix: stop update relaunch prompts from looping (manaflow-ai#15702)
4a46320 Fix Cloud paid team limits for ID-only selected teams (manaflow-ai#16318)
c266af9 test(cloud): pin the CLI tree's link error message through the bundled CLI (manaflow-ai#16515)
0059066 Calmer focus feedback: one short pulse, no flash while typing (manaflow-ai#14894)
65930fc fix(remote): preserve tmux split metadata (manaflow-ai#16398)
512817d docs: fill missing unreleased user-facing changes (manaflow-ai#16519)
f204ade ci: nightly 120 Hz fling bench for the cmux-next agent pane (manaflow-ai#16511)
2be3b26 Remove generated custom sidebar preview art (manaflow-ai#16518)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merged-unverified A judging check was not green at merge; see the merge receipt comment needs a call Finished and held for a team design or product decision (see #13742 and the gallery in #15427)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant