Skip to content

fix(ssh): relay-era terminals' orca orchestration commands work again, behind the per-host switch (STA-10765) - #29571

Merged
nwparker merged 8 commits into
mainfrom
nwparker/sta-10765-relay-cli
Oct 16, 2026
Merged

nwparker merged 8 commits into
mainfrom
nwparker/sta-10765-relay-cli

Conversation

@nwparker

@nwparker nwparker commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​837 $\color{#cf222e}{\Huge{\mathbf{−}}}$​4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​833
Prod 24 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1174 $\color{#cf222e}{\Huge{\mathbf{−}}}$​39 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1135

ELI5

Some terminals on SSH hosts were started by an older Orca, before hosts moved to Orca's managed server. Orca keeps those terminals running in their old tabs, but orca commands typed in them stopped working. That includes the commands a supervised agent uses to check its inbox and report back to its coordinator. The only fix was to open a new terminal and restart the agent. This PR brings the orchestration commands back in those retained terminals, but only on hosts where the user turned on "Allow this host's orca CLI to control Orca".

What Changed

Before: in a relay-era terminal, orca orchestration check, send (including worker_done), ask, reply and inbox all failed with "This terminal was started by an older version of Orca, so orca commands are unavailable in it." A supervised worker could not read its inbox or tell its coordinator it was finished.

After: with the per-host switch on, those five commands work again in the retained terminal, with no new terminal and no restart. With the switch off, or for any other orca command, the terminal gets a clear message saying why and what to do. The old relay is unchanged: it sends the same orca.cli and orca.cli.postOutput requests, and the reply has the same shape it always had.

Mechanism:

  • The legacy relay client used to refuse orca.cli and orca.cli.postOutput. It now hands them to a small server (legacy-relay-cli-server.ts) that runs them on the desktop runtime. That is where a retained worker's Dispatch and Run records live; the managed server never received them. If the relay connection closes, an abort signal stops the call, so a waiting check --wait stops too.
  • Calls go through the existing RPC dispatcher, set up the same way as the desktop browser bridge from fix(ssh): agents on a managed SSH host can drive the desktop Orca browser again, behind the per-host switch #29384. Its method registry holds only orchestration.check/inbox/send/ask/reply, and the caller is a paired scope with no grants. Each host terminal also gets its own mutation-receipt fingerprint, so a host can never replay another caller's result.
  • Identity (legacy-relay-cli-caller.ts): everything the host sends is treated as a claim. A caller is accepted only if all of these hold:
    • its ORCA_PANE_KEY leaf matches a pane this desktop leased on that host, and the host's relay still lists it as running;
    • the relay launched that pane with the same ORCA_TERMINAL_HANDLE;
    • the runtime does not host that handle in another pane;
    • this desktop's own orchestration records bind the handle to that pane, as a Dispatch assignee or as a Run coordinator. A Run migrated from older records counts its legacy coordinator only once a committed principal binds that coordinator to this exact pane. The unbound "first caller wins" adoption is never used for a host-attested caller, so another host pane cannot claim the coordinator's handle.
  • Scope: the caller can reach only its own mailboxes, its own Dispatch or Run, its coordinator, and same-host workers in a Run it coordinates. Other terminals, other Runs, group addresses, other Dispatches' payloads and other people's messages are refused before dispatch.
  • The runtime no longer hosts this terminal. So for the length of one call, the existing SSH compatibility attachment (registerOrchestrationCompatibilitySshAttachment) also carries the terminal's verified handle, pane and process incarnation. The pane-key and process-incarnation lookups, and compatibility-caller verification, fall back to it only for a handle the runtime does not otherwise know. Releasing the attachment removes it.

Limits (from the security review):

  • Turning the per-host switch off aborts calls already running. The setting is checked every second during a call, so every path that edits it is covered.
  • A host can hold at most 16 calls open on this desktop.
  • A wait's timeout is capped at the longest time an ask may wait (30 minutes).
  • A retry skips the sender, Run and recipient checks only when its method and payload hash exactly match a receipt this caller committed. A settled legacy worker is admitted only to replay an operation it committed under that same request id.
  • When two hosts claim the same handle, neither one's pane or process is used for it.

The help text for "Allow this host's orca CLI to control Orca" now also says the switch lets relay-era terminals run orchestration commands. This is updated in every locale and in the Korean overrides file.

Why

Linked Issue

Linear STA-10765 (no GitHub issue).

Visual Proof

N/A. There is no UI change; the change is the output of a CLI command in a retained terminal.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated

Not verified live: a real retained terminal needs a relay started by a pre-1.4.225 Orca desktop, and none was running on the test hosts. Automated coverage, using a real runtime and a real orchestration database:

  • A retained worker's check and worker_done succeed. This fails without the runtime fallback, because worker_done is rejected as "not the exact process".
  • On records migrated from schema 18: a worker check is not answered read-only, the retained coordinator is admitted and receives the worker's reports, and a legacy worker_done replays after it settles.
  • Retries: a completed worker_done replays after its Dispatch settles and after the terminal takes a new assignment; an emptied --retry-request is refused.
  • A check --wait stops when the connection closes.
  • Security review cases:
    • neither the unbound coordinator pane nor an unrelated leased pane can act as the migrated coordinator, by checking its mail or by answering a worker's question;
    • a pane-bound coordinator is admitted only from its own pane;
    • turning the switch off stops a running check --wait;
    • the per-host call cap holds;
    • wait timeouts are capped;
    • a reused retry id whose payload differs is still scoped;
    • two hosts claiming one handle match neither;
    • a settled legacy worker using a fresh retry id is refused;
    • an exact reply replay works after reassignment.
  • Denials:
    • the switch is off;
    • a command outside orchestration;
    • a pane the bridge doesn't run;
    • a mismatched relay handle;
    • no pane key;
    • a handle not bound to the pane;
    • a handle the runtime hosts elsewhere;
    • any of 10 attempts to reach outside the caller's own orchestration;
    • forged attachment evidence.
  • The relay client hands both requests to the server with an abort signal, and still refuses them when no server is wired.

Review

Codex (gpt-6.1-sol, high) was run for eight rounds, ending clean. Every finding was fixed, including all the security review findings:

  • legacy-record attestation;
  • receipt replay after settlement and after reassignment;
  • cancelling waits on disconnect;
  • retained legacy coordinators and their delivery targets;
  • emptied retry ids;
  • panes that moved between tabs.

Agent skill upstream boundary

  • Not applicable.

Notes

COMPAT(legacy-relay-bridge): this retires with the bridge on 2027-01-15. Wire compatibility: no new fields or opcodes; the old relay's existing request and reply shapes are served. The help text for the per-host switch still describes only the browser bridge (left unchanged so locales stay in sync); the error a retained terminal prints names the switch.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • N/A for screenshots (no UI change)
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote impact considered
  • CI covers lint/typecheck/test/build; changed tests, pnpm tc:node and check:code-quality:changed pass locally

@coderabbitai

coderabbitai Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

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 11 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 UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0c8d9732-8706-497f-94be-3ecdd298cbc8
📥 Commits

Reviewing files that changed from the base of the PR and between 2e97d9a and 9aa06f7.

📒 Files selected for processing: 19
  • src/main/ipc/ssh.ts
  • src/main/runtime/orchestration/terminal-authority/verify-orchestration-compatibility-caller.ts
  • src/main/runtime/runtime-terminal-contracts.ts
  • src/main/runtime/terminal/adoption/adopt-terminal-orphans-from-inventory.ts
  • src/main/runtime/terminal/output/headless-buffer-snapshot.ts
  • src/main/runtime/terminal/wait/terminal-interactive-wait.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-bridge-ipc.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-bridge-leases.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-bridge-registry.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-cli-args.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-cli-caller.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-cli-legacy-records.test.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-cli-output.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-cli-server.test.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-cli-server.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-client.test.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-client.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-target-bridge.test.ts
  • src/main/ssh/legacy-relay-bridge/legacy-relay-target-bridge.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@nwparker
nwparker force-pushed the nwparker/sta-10765-relay-cli branch from 9aa06f7 to 1c8f74f Compare October 16, 2026 09:46
@nwparker
nwparker merged commit f387c5d into main Oct 16, 2026
33 of 34 checks passed
nwparker added a commit that referenced this pull request Oct 16, 2026
…29835)

optionalAskTimeout computed exactly what cappedWait does (an undefined timeout stays undefined;
anything else clamps to 0..ORCHESTRATION_ASK_MAX_TIMEOUT_MS), so the ask call uses cappedWait.
Three functions used only inside their own file lose their export, and the per-host switch's
doc names the orchestration commands it has gated since #29571.

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant