fix(ssh): keep a paired server's SSH access link across its restarts, and let it relink

2 hours ago

When a server you paired with Orca restarts, its orcad process gets a new runtimeId. Orca treated that as a possibly different server, so a server with SSH access set up stopped working, and once you removed the access you couldn't add it back. This fix treats a restart as a restart. The SSH access link stays, and you can remove it and add it again freely.

Ask about this presentation

Chapters

  1. 0:00The SSH access link survives restarts
  2. 0:23Two errors after a restart
  3. 0:47A runtimeId names one orcad process
  4. 1:11markEnvironmentUsed records the new id
  5. 1:38The sidecar entry stays after a restart
  6. 2:03Two checks stop tracking a moving runtimeId
  7. 2:29Why the fix is client-only
  8. 2:53The tests
  9. 3:21What this does not cover
  10. 3:48A restart is a restart
Show transcript

The SSH access link survives restarts

Orca
fix(ssh) · src/shared

A paired server's SSH access link now survives its restarts, and you can relink it.

SSH accesslinked · restart · restart again

When a server you paired with Orca restarts, its orcad process gets a new runtimeId. Orca treated that as a possibly different server, so a server with SSH access set up stopped working, and once you removed the access you couldn't add it back. This fix treats a restart as a restart. The SSH access link stays, and you can remove it and add it again freely.

Two errors after a restart

Orca
before · issue #32957

After a restart, two errors locked SSH access

Opening a worktree after the server restarted
SSH access operation cannot change the paired runtime identity.
Setting SSH access again after removing it
The SSH access state is inconsistent with this paired server.

The only workaround was hand-editing orca-environment-sidecar.json

Issue 32957 reported two errors. After the server restarted, opening a worktree failed with "SSH access operation cannot change the paired runtime identity." After you removed SSH access, setting it again failed because the state was inconsistent with the paired server. The only way out was editing the sidecar file by hand.

A runtimeId names one orcad process

Orca
why

orcad mints a new runtimeId every time it starts

First start
host-runtime
runtimeId
Restart
restarted-runtime
runtimeId
Restart again
restarted-again
runtimeId
Same pinned host key·same device token·same pairing binding
So it is the same server, restartedSSH access stays linked

orcad mints a new runtimeId at every start, so the id names one process. In the new tests, one server goes from host runtime to restarted runtime to restarted again. What proves a reply came from the paired server is the pinned host key and the device token, checked in the encrypted handshake, plus the pairing binding. Those stay put, so a new id behind the same pinned pairing is a restart.

markEnvironmentUsed records the new id

Orca
src/shared/runtime-environment-store.ts

markEnvironmentUsed records the new id instead of throwing

markEnvironmentUsed()
if (runtimeIdChanged || pairedDeviceIdChanged) { if (current.sshAccess || current.pendingSshAccessOperation) { // orcad mints a runtimeId per start; only a pending link must keep the runtime it verifies. const intent = current.pendingSshAccessOperation if (intent?.operation === 'link' || (pairedDeviceIdChanged && (current.sshAccess || intent))) { throw new RuntimeEnvironmentStoreError(
Still refused: a new pairedDeviceId
while the server is linked or any SSH access operation is pending
Still refused: any new runtimeId
while a link is pending, so #27765 stays protected

The first change is in markEnvironmentUsed. It used to throw whenever a server with SSH access reported a new id, which is the first error from the issue. Now it records the new id. It still refuses a different paired device while the server is linked or has an operation pending, and it still refuses any runtimeId change while a link is pending, so the protection for 27765 stays in place.

The sidecar entry stays after a restart

Orca
src/shared/runtime-environment-sidecar.ts

The sidecar entry no longer vanishes after a restart

overlayRuntimeEnvironmentSidecar()
if ( entry.runtimeId !== undefined && environment.runtimeId !== null && environment.runtimeId !== entry.runtimeId) { return base}

That early return dropped the whole entry:

SSH access
Pending SSH access operation
Managed deployment link
Revision floor
The store's runtimeId winsThe sidecar's id fills in only while the store has none

The second change is in overlayRuntimeEnvironmentSidecar. This check returned early whenever the sidecar's runtimeId differed from the store's. That dropped the whole entry: the SSH access, the pending operation, the managed deployment link and the revision floor. After a restart the ids always differ. The check is gone. The store's id wins, and the sidecar's id only fills in while the store has none.

Two checks stop tracking a moving runtimeId

Orca
src/shared

Two checks stop comparing against a runtimeId that moves

runtimeEnvironmentSshAccessBinding
runtime-environment-authority-binding.ts
pairingRevision: …, runtimeId: environment.runtimeId, pairedDeviceId: …,

A reply that arrives after a restart, mid-unlink, no longer aborts the unlink.

requireMatchingCompletedLink
runtime-environment-ssh-access-store.ts
completed.runtimeId !== args.verifiedRuntimeId || linkedRuntime !== args.verifiedRuntimeId ||

A lost-completion retry checks the runtime the link verified, read by linkedRuntimeId(). An impostor is still refused.

Two smaller changes follow from the same idea. The runtimeId is out of the binding an SSH access operation is checked against, so a reply that arrives after a restart, in the middle of an unlink, no longer aborts it. And a lost-completion retry now compares against the runtime the link actually verified, which a new helper reads from the sidecar entry. An impostor runtime is still refused.

Why the fix is client-only

Orca
alternatives considered

The fix stays on the client and works with every host version

Rewrite the sidecar's id with the store's
Two files with a crash window between them. Older builds rewrite the store without touching the sidecar, so the link would still vanish.
Clear the sidecar's id on unlink
Fixes the relink half and leaves the restart half broken.
Client-only, in src/sharedno wire change

Two other fixes were considered. Rewriting the sidecar's id together with the store's means two files with a crash window between them, and older builds rewrite the store without touching the sidecar, so the link would still vanish. Clearing the sidecar's id on unlink fixes only the relink half. The chosen fix is client-only. It works with every host version, and nothing on the wire changes.

The tests

Orca
src/shared/runtime-environment-ssh-access-store.test.ts

Two new tests fail on main, and a third pins what is still refused

keeps SSH access across a server restart and relinks after an unlink (#32957)
fails on main
lets an unlink or a lost-completion retry read before a server restart still finish
fails on main
still refuses a new paired device on a linked server, and a new runtime during a link
guard
618
unit tests passing locally
61
test files that import a changed module
Full static analysis job from pr.yml
ratchets, localization extraction, orcad terminal smoke

The new tests sit beside the store. One restarts a linked server, unlinks, restarts again and relinks, and it fails on main with both reported errors. Another finishes an unlink and a lost-completion retry across a restart, which also fails on main. A third confirms that a new paired device, or a new runtime during a link, is still refused. Locally, about 620 unit tests in 61 files pass, along with the full static analysis job.

What this does not cover

Orca
risks and limits

The fix hasn't been run live against a paired server restarting

Untested end to end
Independent SSH access has zero e2e specs, and the Docker managed-orcad lane doesn't cover this client-side store.
A cloned server is accepted while linked
If it holds the same private key and device registry, the same as the managed path already does. Host authentication is unchanged.
A downgrade hides the link
A build with the old overlay check hides it until you upgrade again.

The tests exercise the store directly. A real paired server with an SSH access link hasn't been run through a restart, and independent SSH access has zero end-to-end specs. Host authentication is unchanged, but a cloned server holding the same private key and device registry is now accepted while linked, the same as the managed path. And a downgrade to a build with the old overlay check hides the link until you upgrade again.

A restart is a restart

Orca
fixes #32957

Longer term, a stable per-host identity would be a cleaner key for these checks. That needs a host change.

The pinned pairing decides which server it is, and the SSH access link stays through every restart.

SSH accesshost-runtime → restarted-runtime → restarted-again
github.com/stablyai/orca/pull/33001

A stable per-host identity that survives restarts would be a cleaner key for these checks, and for the registered-more-than-once check too, but that needs a host change and capability negotiation. This fix follows the issue reporter's root-cause analysis. The pinned pairing decides which server it is, and a paired server's SSH access link now survives its restarts.

What happened in AI today, in your inbox

One short email each evening. Read it and you’re caught up.

Built in New York with ❤️