Sign in to Spelunk servers through Spelunk, and connect straight away from a Spelunk link

A Spelunk route can now say that Spelunk signs the user in. Each connection makes a throwaway Ed25519 key pair in memory, sends the public half with the ticket request ({"signIn":{"publicKey":...}}), and logs in as the account Spelunk names (root when it names none) with that key, over the agent's one-connection sshd. Such a host needs no username, key, credential or typed password: the editor greys the login out and saves none, the password box and sheet stay down, and shells, file transfer and port forwards all take the key and account from the route. The flag is nested in the spelunk object at host schema version 10; hosts without it keep their version-9 bytes.

A dodossh://spelunk/connect link now connects: to the personal vault's host tagged spelunk for that server, or, when a saved host already sends an API key to that Spelunk, to a new sign-in host saved in the personal vault with that key and the spelunk tag (made if missing). Only the first link from a Spelunk opens the editor, pre-filled with sign-in ticked and the tag on, so the key credential is chosen once and never sent to an address no saved host already sends it to.
This commit is contained in:
jaap-jan committed 2026-09-28 22:53:51 +02:00
1 parent 07bd5c9886
commit 38ab9b431b
18 files changed
+1239 -115

No files matched your search

+25
View File
@@ -59,6 +59,31 @@ public sealed record SpelunkRoute
/// </remarks>
public required Guid ApiKeyCredentialId { get; init; }
/// <summary>
/// Whether Spelunk signs the user in, so the host needs no SSH key, credential or password of its own.
/// </summary>
/// <remarks>
/// <para>
/// For each connection this client makes a throwaway key pair in memory and sends the public half with
/// the ticket request, which the API key authorises. The agent then runs a one-connection SSH server on
/// the machine whose only authorised key is that one, and says which account it logs in as. It is real
/// SSH to the server's own host key, so shells, file transfer and port forwards work as for any host,
/// and the key is gone when the connection is.
/// </para>
/// <para>
/// <b>What the host says about logging in is then not asked.</b> Its username, key, credential and
/// typed-password choice — its own or a group's — are left alone rather than refused, because they are
/// the host's answer for the day this is unticked, and refusing a host that carries them would make a
/// merge of two honest edits undecodable. The editor stores no binding beside this, and the connect path
/// never reads one for a host that has it. See <c>VaultViewModel.TryBuildAuthentication</c>.
/// </para>
/// <para>
/// Written from <see cref="HostSecretCodec.SpelunkSignInSchemaVersion"/>, and only when true, so a route
/// without it encodes exactly as it did before it existed.
/// </para>
/// </remarks>
public bool SignInThroughSpelunk { get; init; }
/// <summary>Whether this is the route to the given server on the given Spelunk.</summary>
/// <param name="apiBase">A normalised API base, as <see cref="TryNormalizeApiBase"/> writes it.</param>
/// <param name="serverId">The server's id in Spelunk.</param>