Skip to content

Find the SSH agent and the default keys, the way ssh does - #13

Merged
ralyodio merged 1 commit into
mainfrom
fix/ssh-auth-discovery
Aug 30, 2026
Merged

Find the SSH agent and the default keys, the way ssh does#13
ralyodio merged 1 commit into
mainfrom
fix/ssh-auth-discovery

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

"I can't connect to any of my live servers"

Reproducible against a real ~/.ssh/config. Probing five hosts with the shipping code:

dev            key: /home/anthony/.ssh/id_rsa   -> OK (225 entries)
h4kr           key: (agent)                     -> [auth] No SSH agent is available (SSH_AUTH_SOCK is unset)
profullstack   key: (agent)                     -> [auth] No SSH agent is available (SSH_AUTH_SOCK is unset)
racknerd       key: (agent)                     -> [auth] No SSH agent is available (SSH_AUTH_SOCK is unset)
tilde          key: (agent)                     -> [auth] No SSH agent is available (SSH_AUTH_SOCK is unset)

Every host that declares no IdentityFile becomes authType: 'agent', and agent authentication demanded that SSH_AUTH_SOCK be exported. A desktop app is launched from a session that exports far less than a login shell — so those hosts failed in the window while all of them worked in a terminal on the same machine. The user had an agent running the whole time, at /run/user/1000/ssh-agent.socket; nothing ever looked there.

Wiring that socket in by hand removed the auth error entirely and left only ordinary network state, which is what confirmed the diagnosis rather than assumed it.

The fix

Agent authentication now does both halves of what ssh(1) does:

  • Finds a socketSSH_AUTH_SOCK first, because that is intent rather than a guess, then the sockets common agents leave in XDG_RUNTIME_DIR (systemd's ssh-agent.socket, gnome-keyring, gcr, openssh_agent).
  • Offers the default identitiesid_ed25519, id_ecdsa, id_rsa, id_dsa, in ssh's own order. A host with no configured key is not a host with no key.

Only when neither turns anything up is it an error, and the message now names what it looked for instead of naming an environment variable.

Two smaller things that break a path the same way

A tilde was expanded in exactly one place in the tree — the ssh_config reader. A key path typed into the New server dialog, or stored by either import command, kept its ~ and failed as ENOENT: no such file or directory, open '~/.ssh/id_ed25519' — an error naming a path that reads as plainly correct. It is now expanded where the key is read, so every source is covered, and both imports store it expanded.

IdentityFile "/path with spaces/key" is valid ssh_config, and the parser kept the quotes, making the path a file that cannot exist. (Present in the config that prompted this, on the thunder host.)

Verified

Against the real config with SSH_AUTH_SOCK unset — the exact condition that broke it.

  • Before: No SSH agent is available on every agent host.

  • After: the agent socket and ~/.ssh/id_ed25519 are both found; those hosts reach the network, where what remains is reachability from this box (ECONNREFUSED, handshake timeouts) and one unknown host key, which in the app is the Trust dialog.

  • dev still lists its home directory — 225 entries.

  • pnpm test — 314 passed, 28 files (18 new)

  • pnpm typecheck — clean

One test earned its place immediately: passing undefined for a uid silently triggered the parameter default, so "no uid" could not be expressed. The API now uses null for that.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GTQ3RzTAey9nT6r1kbGBCd

Reported as "I can't connect to any of my live servers", and reproducible
against the real ~/.ssh/config: every host that declares no IdentityFile
became `agent` authentication, and agent authentication demanded that
SSH_AUTH_SOCK be exported. A desktop app is launched from a session that
exports far less than a login shell, so those hosts failed with "No SSH
agent is available" — in a terminal, on the same machine, all of them
worked.

Agent authentication now does both halves of what ssh(1) does. It finds a
socket: SSH_AUTH_SOCK when set, because that is intent rather than a
guess, then the sockets the common agents leave in XDG_RUNTIME_DIR. And
it offers the default identities — id_ed25519, id_ecdsa, id_rsa, id_dsa,
in ssh's own order — so a host with no configured key is not a host with
no key. Only when neither turns anything up is it an error, and the
message now says what was looked for.

Two smaller things that make a path unusable in the same way:

`~` in a key path was expanded in exactly one place in the tree, the
ssh_config reader. A path typed into the New server dialog, or stored by
either import command, kept its tilde and failed as
`ENOENT ... open '~/.ssh/id_ed25519'` — a path that reads as plainly
correct. It is now expanded where the key is read, so every source is
covered, and both imports store it expanded too.

`IdentityFile "/path with spaces/key"` is valid ssh_config. The parser
kept the quotes, making the path a file that cannot exist.

Verified against the real config with SSH_AUTH_SOCK unset — the exact
condition that broke it. Before: "No SSH agent is available" on every
agent host. After: the agent socket and ~/.ssh/id_ed25519 are both found
and those hosts reach the network, where what is left is ordinary
reachability from this box. `dev` still lists its home directory, 225
entries. 18 new tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTQ3RzTAey9nT6r1kbGBCd
@ralyodio
ralyodio merged commit 6adc61a into main Aug 30, 2026
4 checks passed
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