Repository navigation
Description
From the Clanker:
Context: follow-up to #10179, which covered the systemd service. This report is the other Linux surface: the desktop app's SSH remote, which launches its own managed t3 serve over ssh and never touches systemd. It hits the same Permission denied (publickey) failure for a different reason: the server inherits the launching session's forwarded agent socket, and that socket is removed when the session ends. The reporter has a dotfiles workaround (below) but it only applies to hosts with a persistent agent socket.
Before submitting
- I searched existing issues and did not find an exact duplicate.
- I included enough detail to reproduce or investigate the problem.
Related: #10179 (systemd service has no SSH_AUTH_SOCK at all), #3067 (devcontainer server started before the forwarded socket exists), #5740 (WSL). This report is about the desktop SSH remote with remoteServerKind: managed, where the socket is present at launch but is a per-session forwarded path that stops existing shortly after.
Area
apps/desktop
Steps to reproduce
- On the local machine, set
ForwardAgent yesfor the remote host in~/.ssh/config(ssh_config defaults this tono, so it is an opt-in). Leave the remote sshd at its defaultAllowAgentForwarding yes. - On the remote (Linux), use a repository whose
originis a GitHub SSH URL. Do not exportSSH_AUTH_SOCKfrom any shell rc file, so the only agent socket a non-interactive ssh session sees is the forwarded per-session one sshd creates. On this host (OpenSSH 10.5p1) that is under~/.ssh/agent/. - Add the host as an SSH remote in the desktop app and connect. The desktop launches the managed server through
~/.t3/ssh-launch/<stateKey>/run-t3.shand detaches it (the process ends up reparented to PID 1). - Let the launching ssh session close.
- Open the repository in T3 Code and click pull, or wait for the background remote-status fetch.
Expected behavior
The managed server keeps working SSH agent access for as long as it runs, or the launch resolves a stable agent socket instead of the one tied to the launching session.
Actual behavior
Pull fails with:
Git command failed in GitVcsDriver.pullCurrentBranch.pull (/home/<user>/src/<repo>): git pull failed
The remote server.log also shows the background poller failing repeatedly (20 fetchRemoteForStatus entries):
GitCommandError: Git command failed in GitVcsDriver.fetchRemoteForStatus (/home/<user>/src/<repo>): Background Git fetch exited with a non-zero status.
at statusDetailsRemote (file:///home/<user>/.t3/runtime/versions/0.0.41-nightly.20260915.1752/t3:256742:34)
...
at VcsStatusBroadcaster.retainRemotePoller (file:///home/<user>/.t3/runtime/versions/0.0.41-nightly.20260915.1752/t3:258129:10)
/proc/<pid>/environ of the managed server:
SSH_AUTH_SOCK=/home/<user>/.ssh/agent/s.SUIjO4iobl.sshd.U1DmzQ0mYB
That path no longer exists. ls on it returns No such file or directory, and ssh-add -l against it returns Error connecting to agent: No such file or directory. The only sockets still present in ~/.ssh/agent/ belong to later ssh sessions.
Running git with the server's exact socket reproduces the failure; running it with any live agent socket succeeds:
SSH_AUTH_SOCK=/home/<user>/.ssh/agent/s.SUIjO4iobl.sshd.U1DmzQ0mYB git pull
# [email protected]: Permission denied (publickey).
# fatal: Could not read from remote repository.The desktop relaunches the managed server on reconnect, and each relaunch picks up a fresh per-session socket, so the problem comes back as soon as that session closes again. Two relaunches today (05:26 and 05:36) both ended up with dead socket paths.
Impact
Major degradation or frequent failure: pull, fetch, and the background remote status all fail for SSH remotes until the server is restarted, and a restart only helps until the launching session closes.
Version or commit
Desktop and managed archive: 0.0.41-nightly.20260915.1752
Environment
- Local: macOS desktop app,
ForwardAgent yesfor the host in~/.ssh/config - Remote: Arch Linux ARM in OrbStack, aarch64, sshd with
AllowAgentForwarding yes, login shell zsh - Remote server launched by the desktop SSH remote (
runner: archive,remoteServerKind: managed), tunneled to remote port 3774 - A separate
t3code.servicesystemd instance on port 3773 with the [Bug]: OrbStack systemd service misses SSH_AUTH_SOCK, causing worktree creation to fail at git fetch #10179 drop-in was running on the same host and was unaffected, because the desktop's SSH remote does not use it
Logs or stack traces
# desktop.trace.ndjson
ssh.remoteServer.launch.start {"alias":"mbp24dev","runner":"archive","archiveVersion":"0.0.41-nightly.20260915.1752","stateKey":"9c2f9984ac7eff72"}
ssh.tunnel.ready {"command":["ssh","-o","BatchMode=yes","-o","ConnectTimeout=10","-p","22","-o","ExitOnForwardFailure=yes","-o","ControlMaster=no","-o","ControlPath=none","-o","ControlPersist=no","-o","ServerAliveInterval=15","-o","ServerAliveCountMax=3","-n","-N","-L","49753:127.0.0.1:3774","jh@mbp24dev"]}
ssh.environment.ensure.succeeded {"localPort":49753,"remotePort":3774,"remoteServerKind":"managed","issuedPairingToken":true}
# remote: managed server process
$ ps -o pid,ppid,lstart,args -p 2684763
2684763 1 Tue Sep 15 05:26:29 2026 /home/jh/.t3/runtime/versions/0.0.41-nightly.20260915.1752/t3 serve --host 127.0.0.1 --port 3774 --base-dir /home/jh/.t3
$ tr '\0' '\n' < /proc/2684763/environ | grep ^SSH_AUTH_SOCK=
SSH_AUTH_SOCK=/home/jh/.ssh/agent/s.SUIjO4iobl.sshd.U1DmzQ0mYB
$ ls -la /home/jh/.ssh/agent/
srw------- 1 jh jh 0 Sep 14 19:52 s.SUIjO4iobl.sshd.PGcZvniObu
srw------- 1 jh jh 0 Sep 15 05:29 s.SUIjO4iobl.sshd.iEWCpn4Oc4Workaround
Export a stable agent socket from the remote's zsh startup so the launch shell overrides the forwarded one. On OrbStack:
# ~/.zshenv
if [[ -S "/opt/orbstack-guest/run/host-ssh-agent.sock" ]]; then
export SSH_AUTH_SOCK="/opt/orbstack-guest/run/host-ssh-agent.sock"
fiThen kill the managed t3 serve so the desktop relaunches it. After that the new process had the OrbStack socket in its environment and fetch/pull worked. This only helps on hosts that have a persistent agent socket to point at, which is not the general case for an SSH remote.
Suggested fix
The managed launch should not depend on the launching session's forwarded socket outliving the session. Options, in rough order of preference:
- Keep a long-lived ssh session (or the tunnel session itself, with
-A) as the owner of the forwarded agent, and point the managed server at that session's socket, re-resolving it on reconnect. - Or expose the socket path through a stable symlink the launch script maintains (for example
~/.t3/ssh-launch/<stateKey>/agent.sockre-linked on each connect) and setSSH_AUTH_SOCKto the symlink. - Or at minimum, detect a dead
SSH_AUTH_SOCKbefore running git and surface the underlyingPermission denied (publickey)stderr instead ofgit pull failed.
Activity
juliusmarminge commentedon Sep 15, 2026
Thanks for the careful repro and for separating this from #10179. This looks like a real bug on the desktop-managed SSH launch path, not OrbStack-specific and not a duplicate of the systemd hole.
What we think is happening
Desktop SSH remotes with
remoteServerKind: managednever toucht3code.service.launchOrReuseRemoteServerinpackages/ssh/src/tunnel.tsruns a one-shotsh -l -sthat writes~/.t3/ssh-launch/<stateKey>/run-t3.shand starts the server with:That process inherits the launch session’s
SSH_AUTH_SOCK. WithForwardAgent yes, sshd creates a per-session socket (~/.ssh/agent/s.*.sshd.*or/tmp/ssh-XXXX/agent.N) and deletes it when that session ends. The managed server is already detached (reparented to PID 1, after #9843’sexec), so/proc/<pid>/environkeeps the dead path. Reconnect reuses a live PID without refreshing the env; a full relaunch only picks the next short-lived socket.The long-lived
-N -Ltunnel is a different SSH session (ControlMaster=no, no-A). It does not own or republish an agent socket to the server.Your
/proc+ls+SSH_AUTH_SOCK=<dead> git pullvs a live socket pins this. It also matches why the systemd instance on 3773 with the #10179 drop-in stayed healthy: that process never inherited this launch-session socket.Same family as #10179 (systemd missing
SSH_AUTH_SOCK), #5740 (WSL), #3067 (devcontainer timing), and #971 (local macOS desktop). Not a duplicate — here the socket is present at exec and dies with the launcher. #10312’s login-shell backfill would not help: your probe on #10179 wasabsent, and this server already has a socket (the wrong one). Open #8190 already describes this lifetime bug and sketches a dedicatedssh -Akeepalive plus$STATE_DIR/agent-sock; it is unvouched and review-blocked, so treat it as a design reference, not a merge candidate.Workaround
The zshenv export of a durable socket (OrbStack
host-ssh-agent.sock) plus a managed-server relaunch is the right workaround on hosts that have one. It will not cover a remote whose only agent is a forwarded per-session socket.What we should change
Keep a long-lived owner of the forwarded agent for the life of the desktop connection (dedicated
ssh -A, or attach forwarding to the existing tunnel and share that session), pointt3 serveat a stable path under~/.t3/ssh-launch/<stateKey>/, and re-resolve that path on reconnect — including when the managed PID is reused. Do not inherit the one-shot launch session’sSSH_AUTH_SOCKwithout that owner. A symlink with no live owner still goes stale when the launch session exits.Secondary:
GitVcsDriver.pullCurrentBranch/fetchRemoteForStatusreplace git stderr withgit pull failed/Background Git fetch exited with a non-zero status, soPermission denied (publickey)never reaches the UI.#10951 (keep the managed server after desktop close) would make a stale socket last longer; that work should not land without an agent owner.
Keeping this open as an accepted bug on
packages/sshmanaged launch.GPT 6 via Cursor Cloud (triage only; no code change).
TinBane commentedon Oct 18, 2026
@juliusmarminge your triage above lists a secondary problem:
GitVcsDriver.pullCurrentBranchreplaces Git's output withgit pull failed, soPermission denied (publickey)never reaches the UI. #12485 fixed the fetch half of that. #18083 fixed the pull half the same way: the same helpers and fixed messages, one call site, and no raw Git output. It was closed as a diagnostics change rather than a bug fix. Would you take it as the pull counterpart to #12485? If so, I'll rebase it and ask for it to be reopened.juliusmarminge commentedon Oct 22, 2026
Triage update: searched all 945 open issues; no duplicate of this desktop-managed socket-lifetime report found. The earlier implementation #8190 is now closed and unmerged. No fresh desktop reproduction was run. Routing to Julius: the fix changes SSH credential forwarding/socket ownership, which is outside the automated triage flow’s allowed scope. Leaving this accepted bug open; no replacement PR created.
juliusmarminge commentedon Oct 22, 2026
Reproduced on main 3b95e7831b using the real managed launcher and server with an isolated Linux SSH host and
ForwardAgent yes. After the launching SSH session exited, the server retained a removed socket:ssh-add -lreturnedNo such file or directory, and fetch/pull returnedPermission denied (publickey). The same repository fetched and pulled successfully inside a live forwarded SSH session. Desktop UI on macOS was not exercised.