namespace DodoSSH.Client.Ssh;
/// How to authenticate to a host.
///
/// Always decrypted from the vault immediately before use and never persisted outside it. Because
/// SSH terminates on the client, using a credential requires its plaintext here — which is exactly
/// why the Connect permission is a UI hint and not an enforceable boundary. See ADR 0001.
///
public abstract record SshCredential;
/// Password authentication, and keyboard-interactive where the server prefers it.
public sealed record SshPasswordCredential(string Password) : SshCredential;
/// Public-key authentication.
/// The private key in PEM form, as stored in the vault.
/// Passphrase protecting the key, when it has one.
public sealed record SshPrivateKeyCredential(byte[] PrivateKeyPem, string? Passphrase) : SshCredential;
/// Everything needed to reach one host.
/// Hostname or address.
/// Port.
/// Remote account.
/// How to authenticate.
/// How long to wait for the transport and handshake.
public sealed record SshConnectionRequest(
string Host,
int Port,
string Username,
SshCredential Credential,
TimeSpan? ConnectTimeout = null);
/// An interactive shell over a pseudo-terminal.
public interface ISshShellSession : IAsyncDisposable
{
/// Whether the channel is still usable.
bool IsOpen { get; }
/// Reads whatever output is available, blocking until at least one byte arrives.
/// Bytes read, or 0 once the remote closes the channel.
ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken);
/// Sends keystrokes to the remote.
ValueTask WriteAsync(ReadOnlyMemory data, CancellationToken cancellationToken);
///
/// Tells the remote the terminal has been resized.
///
///
/// Verified to reach the remote against a real sshd; see PtyAndResizeSpikeTests. Sizes
/// that are not are dropped rather than forwarded.
///
void Resize(TerminalSize size);
}
/// An authenticated connection to one host.
public interface ISshConnection : IAsyncDisposable
{
/// Whether the transport is still up.
bool IsConnected { get; }
/// The host key that was accepted for this connection.
HostKeyPresentation HostKey { get; }
/// Opens an interactive shell with a pseudo-terminal.
Task OpenShellAsync(TerminalSize size, CancellationToken cancellationToken);
}
/// Opens connections, enforcing host key trust before authenticating.
public interface ISshConnectionFactory
{
///
/// Connects and authenticates.
///
///
/// The host has no pinned key. The caller must show the fingerprint, and only on explicit
/// confirmation record it via and retry.
///
///
/// The presented key differs from the pin. There is no retry path: this is a hard block.
///
Task ConnectAsync(SshConnectionRequest request, CancellationToken cancellationToken);
}