Repository navigation
Conversation
Each connection attempt gets 15 s, but the desktop's SSH setup cannot be cancelled once sent. On a slow host a timed-out attempt left its setup running under the per-target lock, every retry queued another setup behind it, and each one minted a pairing credential nobody used. The SSH gateway now hands a setup that a timed-out attempt left running to the next attempt, which spends its credential. Reconnects present the bearer from the last pairing when the environment that issued it is the one answering and still honors it, and pair only otherwise. Pairing checks the environment before spending the one-time credential, so a route that reaches a different machine never creates or keeps a session there. Refs pingdotgg#18229 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The launch script ran `t3 --version` only so the runner would install the release archive before the port and readiness helpers used it. Each t3 start costs seconds of CPU on a small host. The runner now has an install-only mode that installs when needed and exits without starting t3. Refs pingdotgg#18229 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This production SSH fix changes authentication and connection-session behavior by caching and reusing bearer tokens, coordinating abandoned setup calls, and altering remote launch sequencing. Although focused tests cover the new paths, the authentication-sensitive runtime changes warrant human review. You can add or adjust custom eligibility rules. Learn more. |
|
Note Written by Thanks for documenting the slow-host connection failure. Under Read this first, our review capacity is limited, and right now we are only taking focused bug fixes from outside contributors. This 562-line patch exceeds the size we can take over from an unvouched contributor. Please preserve the reproduction in issue #18229 and propose a smaller fix for reconsideration. |
Problem
Connecting the desktop app to an SSH environment on a slow host takes 25–60 s, even though SSH and the remote server itself respond quickly (#18229). The reporter's host spends about 5.5 s of CPU on every
t3start, and a connect startst3many times.The connection supervisor gives each attempt 15 s, then retries. The renderer asks the desktop to set up the environment with
ensureSshEnvironment(target, { issuePairingToken: true })on every attempt. The desktop runs that setup under a per-target lock, and IPC cannot cancel it. A timed-out attempt therefore leaves its setup running. The next attempt queues a new setup behind it, so every retry runst3 auth pairing create(about 7 s on that host) to mint a pairing credential that nobody uses. The reporter saw 9 credentials in one 64 s reconnect.Change
Retries take over the running setup (
apps/web/src/connection/platform.ts). The SSH gateway moves intomakeDesktopSshEnvironmentGateway. When an attempt is interrupted while its setup is still running, the gateway keeps that setup. The next attempt for the same target awaits it instead of queueing another, and spends the credential it mints. Once a setup finishes, a later attempt starts its own. The desktop keeps the tunnel, so the repeat is cheap, and a credential that may have passed its 5-minute lifetime is never handed out.Reconnects reuse the bearer. A bearer session lasts 30 days on the server. The gateway stores the bearer from the last pairing together with the environment that issued it. On reconnect it opens the tunnel without pairing and checks two things: the server answering is that environment, and
/api/auth/sessionstill accepts the bearer. If both hold, it presents the bearer; otherwise it pairs again. This also stops the remote from collecting a new session on every reconnect. Removing the environment forgets the bearer.Pairing checks the machine first.
preparenow reads the environment descriptor before spending the one-time credential, asprovisionalready did. A route that reaches a different machine fails with the sameenvironmentMismatchErrorthe resolver already returned. That machine never gets a session, and the gateway never stores one from it.One fewer
t3start per launch (packages/ssh/src/tunnel.ts). The launch script ran"$RUNNER_FILE" --versiononly so the runner would install the release archive before the port and readiness helpers used it. The runner now has an install-only mode (T3_RUNNER_INSTALL_ONLY=1) that installs when needed and exits without startingt3. On the reporter's host that saves about 5.5 s on every launch.No contract, server, desktop main-process, or mobile changes. SSH environments are desktop-only, and the desktop's
SshEnvironmentManageris unchanged.Scope and approval
Fixes #18229, a triaged bug. The maintainer triage confirms the mechanism above. It puts these in scope: coalescing or cancelling orphaned
ensureEnvironmentcalls, not minting a fresh pairing token on every retry when a prior grant is still usable, and trimming the launch path'st3starts. Each change here serves that one problem, the slow SSH connect.Lazy-loading the server bundle is out of scope, as the triage says. I left the launch script's server-reuse block alone because #17919 rewrites it, and its rewrite already removes the second readiness probe there (the issue's fourth
t3start). Neither this PR nor #17550 or #10588 touches the other's lines.Verification
Establishing the problem. I traced the path in the code:
supervisor.ts;Effect.tryPromisearound the IPC call, which nothing on the desktop side observes;KeyedLockand pairing call inSshEnvironmentManager.ensureEnvironment;--versionstart.That matches the reporter's measurements. I did not reproduce it on a slow host.
Focused tests.
platform.test.tsdrives the gateway against a fake desktop bridge whose host issues a different bearer per machine and accepts only its own:tunnel.test.tsruns the real runner script against a fake release archive. Install-only mode installs on a cold home, exits without output on a warm one, and a normal run still executest3.The web and
packages/sshtypechecks are clean, and lint and format pass on the changed files.Not checked.
Reviewed with Codex (GPT-6.1 Sol, high) over 2 rounds. Round 1 found that
preparecould pair with an unexpected machine and later present that machine's bearer to the expected one. That is fixed above: bearers carry their issuing environment, and pairing checks the machine before the exchange. Round 2 confirmed the fix and found no other blocking issues.Created with Claude Opus 5.5 in Claude Code.
🤖 Generated with Claude Code