Let a session's transport say what it negotiated

ISshConnection and ISftpSession both carry Cipher now — the server-to-client
algorithm off SSH.NET's own ConnectionInfo, captured once because a rekey is
not an event that library raises — and TerminalWorkspace.GetSessionFacts hands
that plus the host key's algorithm back per session, without ever handing over
the connection itself. Nothing reads either yet; the status bar that will is
the next commit.
This commit is contained in:
2026-08-08 20:54:14 +02:00
parent 8915650a0d
commit 8209f15741
10 changed files with 134 additions and 0 deletions
@@ -37,6 +37,18 @@ public sealed class TerminalSessionEndedEventArgs(uint sessionId) : EventArgs
public uint SessionId { get; } = sessionId;
}
/// <summary>
/// The negotiated cipher and host-key algorithm for one live session.
/// </summary>
/// <param name="Cipher">The server-to-client encryption algorithm; see <see cref="ISshConnection.Cipher"/>.</param>
/// <param name="HostKeyAlgorithm">The host key's algorithm, e.g. <c>ssh-ed25519</c>.</param>
/// <remarks>
/// Two facts rather than the whole <see cref="ISshConnection"/>, because that is all a caller outside this
/// assembly has any business reading off a session it does not own — everything else on the connection
/// (disposal, the shell) belongs to the workspace alone. See <see cref="TerminalWorkspace.GetSessionFacts"/>.
/// </remarks>
public sealed record SessionFacts(string Cipher, string HostKeyAlgorithm);
/// <summary>The renderer asking for a different font size.</summary>
/// <param name="step">
/// How far to move, in points of font size, or zero to go back to the default. It is a step rather than a
@@ -180,6 +192,37 @@ public sealed class TerminalWorkspace : IAsyncDisposable
}
}
/// <summary>
/// The negotiated cipher and host-key algorithm for one live session, or null when the id names no
/// session this workspace still has open.
/// </summary>
/// <remarks>
/// <para>
/// Additive, and deliberately narrow: the caller this exists for — the shell's own connect path, which
/// wants these two facts for its status bar — has no other business with a session it does not own, and a
/// method that handed back the <see cref="ISshConnection"/> itself would have handed over the shell,
/// disposal and all, to code that already goes through <see cref="CloseSessionAsync"/> for that.
/// </para>
/// <para>
/// "No session this workspace still has open" covers two different absences the same way
/// <see cref="IsSessionLive"/> already does: an id this workspace never issued, and one whose shell has
/// already ended but whose entry has not been removed yet. Both are "nothing to report" to a caller
/// asking what a session's transport looks like right now.
/// </para>
/// </remarks>
public SessionFacts? GetSessionFacts(uint sessionId)
{
lock (sessionGate)
{
if (!sessions.TryGetValue(sessionId, out var session) || session.Run.IsCompleted)
{
return null;
}
return new SessionFacts(session.Connection.Cipher, session.Connection.HostKey.Algorithm);
}
}
/// <summary>
/// Raised with the session id when a shell ends on its own.
/// </summary>