Skip to content

Name the SSH key failure and add aether link --key - #59

Merged
ilovecrayons merged 3 commits into
mainfrom
varram/sup-64-aether-link-ssh-key-error-and-key-flag
Sep 4, 2026
Merged

Name the SSH key failure and add aether link --key#59
ilovecrayons merged 3 commits into
mainfrom
varram/sup-64-aether-link-ssh-key-error-and-key-flag

Conversation

@not-varram

Copy link
Copy Markdown
Contributor

Summary

Closes SUP-64. aether link discarded the error from loading the SSH key, so a passphrase-protected ~/.ssh/id_ed25519 surfaced only as ssh: unable to authenticate, attempted methods [none], and ssh-agent was the only workaround.

  • A failed handshake now names the cause: <path> is passphrase-protected; add it to ssh-agent (ssh-add <path>) or pass --key <unencrypted key>, with the underlying ssh error. A successful dial prints nothing, so healthy setups (an agent socket with no keys plus a key file) stay quiet.
  • aether link <addr> --key <path> saves the key in the link config (top level and in the --name profile), so every command that dials the control connection uses it. A chosen key that is missing or unreadable is a hard error before dialing; relinking without --key keeps the saved one.
  • The dashboard's daemon.install now passes the linked key into the daemon unit; the daemon CLI keeps its own --key, and git paths use the system ssh client, which the docs now say.

Validation

  • Tests: passphrase-protected key names the cause (real generated key, real rejecting SSH server); a keyless agent plus a good key is silent on stderr; a missing chosen key fails before dialing; the saved key carries forward across relinks; the rendered daemon unit carries the key.
  • make fmt-check vet lint test public-audit pass.
  • Two fresh-context adversarial reviews: the first found a noisy success warning, a silently ignored missing --key, and an overclaiming doc; all fixed. The second verified each fix and returned MERGE.

🤖 Generated with Claude Code

https://claude.ai/code/session_019irxg6aWWKmn2w3QsJR5CJ

A passphrase-protected ~/.ssh/id_ed25519 surfaced only as "attempted
methods [none], no supported methods remain": the key-file error was
printed as a bare warning before the handshake, or dropped entirely when
an ssh-agent supplied a method.

The dial now carries the reason a configured method was unusable. It is
appended to a failed handshake, so the rejection names the key, the
remedy, and the underlying ssh error, and it prints as a warning when the
handshake succeeds without it. A passphrase-protected key is detected
through ssh.PassphraseMissingError:

  cli: ssh handshake with host:2222: ssh: handshake failed: ssh: unable
  to authenticate, attempted methods [none], no supported methods
  remain; cli: /home/me/.ssh/id_ed25519 is passphrase-protected; add it
  to ssh-agent (ssh-add /home/me/.ssh/id_ed25519) or pass --key
  <unencrypted key>: ssh: this private key is passphrase protected

aether link --key <path> picks a key outside ~/.ssh/id_ed25519. The path
is resolved against the current directory and saved in
~/.config/aether/config.json - top level and, with --name, in the profile
- so every later command dials with the same key. The CLI still never
prompts for a passphrase; ssh-agent remains the way to use an encrypted
key.
A dial no longer says anything when it succeeds. loadAuth records a
problem for any unusable source, and "SSH agent has no signing keys" is
the normal state on a desktop that authenticates with the key file, so
the warning fired on every command and on every gateway redial. The
reason is now only appended to a dial that actually fails.

A key path the user chose is no longer skipped when the file is missing.
It was treated like an absent default key, so a typo fell back to the
agent, was saved to the config, and reproduced the original error. It is
now fatal - "cli: ssh key <path>: <os error>" - and `aether link --key`
stats the path before dialing, so it fails on the path the user typed.

The daemon the dashboard installs now dials with the linked key:
InstallDaemon takes it and renders --key into the unit. The docs no
longer claim every later command picks the key up; git and the
`aether daemon` CLI take their own configuration, and that is now named.

Re-linking without --key keeps the key the last link saved, for the
default link and for a named profile, instead of silently clearing it.

The invite path reports its causes as one line rather than the raw
errors.Join with embedded newlines, and appended causes no longer stack a
second "cli: " inside the line.

New tests: a successful dial with a running but keyless agent writes
nothing to stderr, a missing chosen key fails the dial while the agent
holds a usable key, `aether link --key <missing>` fails before dialing,
the saved key carries forward, and the installed daemon unit carries the
key path.
@ilovecrayons
ilovecrayons merged commit 48572d0 into main Sep 4, 2026
6 checks passed
@not-varram
not-varram deleted the varram/sup-64-aether-link-ssh-key-error-and-key-flag branch September 4, 2026 04:18
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.

2 participants