# TWSSH API Reference

Complete API documentation for the TWSSH SSH library.

## Table of Contents

- [Namespaces](#namespaces)
- [Core Classes](#core-classes)
  - [SshClient](#sshclient)
  - [SshClientSettings](#sshclientsettings)
  - [SshConnectionInfo](#sshconnectioninfo)
  - [SshAlgorithmSet](#sshalgorithmset)
- [Authentication](#authentication)
  - [SshCredential](#sshcredential)
  - [PasswordCredential](#passwordcredential)
  - [PrivateKeyCredential](#privatekeycredential)
  - [KeyboardInteractiveCredential](#keyboardinteractivecredential)
- [Command Execution](#command-execution)
  - [SshCommand](#sshcommand)
  - [SshCommandResult](#sshcommandresult)
- [Interactive Shell](#interactive-shell)
  - [ShellStream](#shellstream)
- [Port Forwarding](#port-forwarding)
  - [ForwardedPort](#forwardedport)
  - [LocalPortForwarder](#localportforwarder)
  - [RemotePortForwarder](#remoteportforwarder)
- [SFTP](#sftp)
  - [SftpClient](#sftpclient)
  - [SftpFileStream](#sftpfilestream)
  - [SftpFileInfo](#sftpfileinfo)
  - [SftpDirectoryEntry](#sftpdirectoryentry)
  - [SftpTransferOptions](#sftptransferoptions)
  - [SftpTransferProgress](#sftptransferprogress)
  - [SftpCapabilities](#sftpcapabilities)
- [Private Key Parsing](#private-key-parsing)
  - [PrivateKeyParser](#privatekeyparser)
  - [SshPrivateKey](#sshprivatekey)
- [PTY Settings](#pty-settings)
  - [PtyRequest](#ptyrequest)
- [Licensing](#licensing)
  - [LicenseManager](#licensemanager)
- [Enumerations](#enumerations)
- [Exceptions](#exceptions)
- [Logging](#logging)
  - [ISshLogger](#isshlogger)
  - [NullSshLogger](#nullsshlogger)

---

## Namespaces

| Namespace | Description |
|-----------|-------------|
| `TwSsh.Client` | High-level SSH client classes |
| `TwSsh.Authentication` | Credential and authentication types |
| `TwSsh.Core.Authentication` | Private key parsing utilities |
| `TwSsh.Connection` | Channel types and PTY settings |
| `TwSsh.Sftp` | SFTP client and file transfer operations |
| `TwSsh.Scp` | SCP file transfer operations |
| `TwSsh.Licensing` | License management |
| `TwSsh.Common` | Shared types and exceptions |

---

## Core Classes

### SshClient

**Namespace:** `TwSsh.Client`

The main class for establishing SSH connections and performing operations.

```csharp
public sealed class SshClient : IAsyncDisposable
```

#### Constructors

```csharp
// Create client with full settings
public SshClient(SshClientSettings settings);

// Create client with simplified settings
public SshClient(string host, int port, SshCredential credential);
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `State` | `SshClientState` | Current connection state |
| `IsConnected` | `bool` | Whether connected to server |
| `IsAuthenticated` | `bool` | Whether authenticated |
| `ConnectionInfo` | `SshConnectionInfo?` | Connection details (null if not connected) |

#### Events

| Event | Type | Description |
|-------|------|-------------|
| `Disconnected` | `EventHandler<SshDisconnectEventArgs>` | Raised when disconnected |
| `HostKeyReceived` | `EventHandler<HostKeyEventArgs>` | Raised for host key verification |

#### Methods

##### Connection Methods

```csharp
// Connect to SSH server
public ValueTask ConnectAsync(CancellationToken ct = default);

// Connect and authenticate in one call
public ValueTask ConnectAndAuthenticateAsync(CancellationToken ct = default);

// Authenticate with credential
public ValueTask<bool> AuthenticateAsync(SshCredential credential, CancellationToken ct = default);

// Disconnect from server
public ValueTask DisconnectAsync(CancellationToken ct = default);
```

##### Command Execution Methods

```csharp
// Create a command object
public ValueTask<SshCommand> CreateCommandAsync(string commandText, CancellationToken ct = default);

// Run command and get result
public ValueTask<SshCommandResult> RunCommandAsync(string commandText, CancellationToken ct = default);
```

##### Shell Methods

```csharp
// Create interactive shell stream
public ValueTask<ShellStream> CreateShellAsync(PtyRequest? ptyRequest = null, CancellationToken ct = default);

// Open subsystem channel (e.g., SFTP)
public ValueTask<ISessionChannel> OpenSubsystemChannelAsync(string subsystemName, CancellationToken ct = default);
```

##### Port Forwarding Methods

```csharp
// Forward local port to remote (SSH -L) - returns LocalPortForwarder
public ValueTask<ForwardedPort> ForwardLocalPortAsync(
    string localHost, int localPort,
    string remoteHost, int remotePort,
    CancellationToken ct = default);

// Forward remote port to local (SSH -R) - returns RemotePortForwarder
public ValueTask<ForwardedPort> ForwardRemotePortAsync(
    string remoteHost, int remotePort,
    string localHost, int localPort,
    CancellationToken ct = default);
```

**Note:** These methods return the base `ForwardedPort` type. To access specific properties like `LocalPort` or `RemotePort`, cast to `LocalPortForwarder` or `RemotePortForwarder`:

```csharp
await using var tunnel = await client.ForwardLocalPortAsync("127.0.0.1", 0, "localhost", 3306);
if (tunnel is LocalPortForwarder lpf)
{
    Console.WriteLine($"Listening on port {lpf.LocalPort}");
}
```

#### Example

```csharp
using TwSsh.Client;
using TwSsh.Authentication;
using TwSsh.Licensing;

// Initialize license
LicenseManager.Instance.Initialize("YOUR-LICENSE-KEY");

// Create and use client
var settings = new SshClientSettings
{
    Host = "example.com",
    Port = 22,
    Credential = new PasswordCredential("user", "password")
};

await using var client = new SshClient(settings);
await client.ConnectAndAuthenticateAsync();

var result = await client.RunCommandAsync("uname -a");
Console.WriteLine(result.Output);
```

---

### SshClientSettings

**Namespace:** `TwSsh.Client`

Configuration settings for SSH client connections.

```csharp
public sealed class SshClientSettings
```

#### Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `Host` | `string` | `""` | Remote host to connect to |
| `Port` | `int` | `22` | Remote port |
| `Credential` | `SshCredential?` | `null` | Authentication credential |
| `ConnectionTimeout` | `TimeSpan` | `30 seconds` | Connection timeout |
| `OperationTimeout` | `TimeSpan` | `1 minute` | Operation timeout |
| `KeepAliveInterval` | `TimeSpan` | `Zero` | Keep-alive interval (Zero = disabled) |
| `AutoReconnect` | `bool` | `false` | Auto-reconnect on disconnect |
| `ClientVersion` | `string?` | `null` | Custom client version string |
| `HostKeyVerification` | `HostKeyVerificationMode` | `AutoAdd` | Host key verification mode |
| `HostKeyCallback` | `Func<HostKeyEventArgs, bool>?` | `null` | Custom verification callback |
| `Logger` | `ISshLogger?` | `null` | Custom logger for SSH operations |

##### Algorithm Preferences

| Property | Type | Description |
|----------|------|-------------|
| `KeyExchangeAlgorithms` | `IList<string>?` | Preferred key exchange algorithms |
| `HostKeyAlgorithms` | `IList<string>?` | Preferred host key algorithms |
| `EncryptionAlgorithms` | `IList<string>?` | Preferred encryption algorithms |
| `MacAlgorithms` | `IList<string>?` | Preferred MAC algorithms |
| `CompressionAlgorithms` | `IList<string>?` | Preferred compression algorithms |

#### Methods

```csharp
// Validate settings
public void Validate();
```

#### Example

```csharp
var settings = new SshClientSettings
{
    Host = "192.168.1.100",
    Port = 22,
    Credential = new PasswordCredential("admin", "secret"),
    ConnectionTimeout = TimeSpan.FromSeconds(10),
    OperationTimeout = TimeSpan.FromMinutes(5),
    KeepAliveInterval = TimeSpan.FromSeconds(30),
    HostKeyVerification = HostKeyVerificationMode.Strict
};
```

#### Algorithm Configuration Examples

You can specify preferred algorithms (in order of preference) to restrict which algorithms are negotiated with the server. This is useful for:
- Compliance requirements (e.g., FIPS, PCI-DSS)
- Connecting to legacy servers that only support older algorithms
- Enforcing stronger cryptography

```csharp
// Example: FIPS-compliant configuration (no ChaCha20, RSA with SHA-2)
var fipsSettings = new SshClientSettings
{
    Host = "secure-server.example.com",
    Credential = credential,
    KeyExchangeAlgorithms = new List<string>
    {
        "ecdh-sha2-nistp256",
        "ecdh-sha2-nistp384",
        "diffie-hellman-group16-sha512",
        "diffie-hellman-group14-sha256"
    },
    HostKeyAlgorithms = new List<string>
    {
        "ecdsa-sha2-nistp256",
        "ecdsa-sha2-nistp384",
        "rsa-sha2-512",
        "rsa-sha2-256"
    },
    EncryptionAlgorithms = new List<string>
    {
        "aes256-gcm@openssh.com",
        "aes128-gcm@openssh.com",
        "aes256-ctr",
        "aes128-ctr"
    },
    MacAlgorithms = new List<string>
    {
        "hmac-sha2-512-etm@openssh.com",
        "hmac-sha2-256-etm@openssh.com",
        "hmac-sha2-512",
        "hmac-sha2-256"
    }
};

// Example: Legacy server support (include older algorithms)
var legacySettings = new SshClientSettings
{
    Host = "old-server.example.com",
    Credential = credential,
    HostKeyAlgorithms = new List<string>
    {
        "rsa-sha2-512",
        "rsa-sha2-256",
        "ssh-rsa"  // Legacy RSA with SHA-1, use only if required
    }
};

// Example: Maximum security (modern algorithms only)
var secureSettings = new SshClientSettings
{
    Host = "modern-server.example.com",
    Credential = credential,
    KeyExchangeAlgorithms = new List<string>
    {
        "curve25519-sha256",
        "curve25519-sha256@libssh.org"
    },
    EncryptionAlgorithms = new List<string>
    {
        "chacha20-poly1305@openssh.com",
        "aes256-gcm@openssh.com"
    }
};
```

**Note:** If you don't specify algorithm preferences, the library uses secure defaults that prefer modern algorithms while maintaining compatibility with most servers.

---

### SshConnectionInfo

**Namespace:** `TwSsh.Client`

Information about an established SSH connection.

```csharp
public sealed class SshConnectionInfo
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `Host` | `string` | Remote host |
| `Port` | `int` | Remote port |
| `ServerVersion` | `string` | Server's SSH version string |
| `ClientVersion` | `string` | Client's SSH version string |
| `SessionId` | `byte[]` | Session identifier |
| `Algorithms` | `SshAlgorithmSet` | Negotiated algorithms |
| `Username` | `string` | Authenticated username |
| `IsAuthenticated` | `bool` | Whether authenticated |

---

### SshAlgorithmSet

**Namespace:** `TwSsh.Client`

Contains the negotiated algorithms for an SSH connection.

```csharp
public sealed class SshAlgorithmSet
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `KeyExchange` | `string` | Negotiated key exchange algorithm |
| `HostKey` | `string` | Negotiated host key algorithm |
| `EncryptionClientToServer` | `string` | Client-to-server encryption algorithm |
| `EncryptionServerToClient` | `string` | Server-to-client encryption algorithm |
| `MacClientToServer` | `string` | Client-to-server MAC algorithm |
| `MacServerToClient` | `string` | Server-to-client MAC algorithm |
| `CompressionClientToServer` | `string` | Client-to-server compression |
| `CompressionServerToClient` | `string` | Server-to-client compression |

#### Example

```csharp
await client.ConnectAsync();

var algorithms = client.ConnectionInfo.Algorithms;
Console.WriteLine($"Key Exchange: {algorithms.KeyExchange}");
Console.WriteLine($"Encryption: {algorithms.EncryptionClientToServer}");
Console.WriteLine($"MAC: {algorithms.MacClientToServer}");
Console.WriteLine($"Host Key: {algorithms.HostKey}");
```

---

## Authentication

### SshCredential

**Namespace:** `TwSsh.Authentication`

Abstract base class for SSH credentials.

```csharp
public abstract class SshCredential
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `Username` | `string` | Username for authentication |

---

### PasswordCredential

**Namespace:** `TwSsh.Authentication`

Password-based authentication credential.

```csharp
public sealed class PasswordCredential : SshCredential
```

#### Constructor

```csharp
public PasswordCredential(string username, string password);
```

#### Methods

```csharp
// Get the password
public string GetPassword();
```

#### Example

```csharp
var credential = new PasswordCredential("admin", "MySecurePassword123!");
```

---

### PrivateKeyCredential

**Namespace:** `TwSsh.Authentication`

Private key-based authentication credential.

```csharp
public sealed class PrivateKeyCredential : SshCredential, IDisposable
```

#### Constructor

```csharp
public PrivateKeyCredential(string username, params SshPrivateKey[] privateKeys);
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `PrivateKeys` | `IReadOnlyList<SshPrivateKey>` | Private keys for authentication |

#### Example

```csharp
using TwSsh.Core.Authentication;
using TwSsh.Authentication;

// RSA key authentication (pass null for unencrypted keys)
var rsaKey = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_rsa", null);
using var rsaCredential = new PrivateKeyCredential("username", rsaKey);

// Ed25519 key authentication (.NET 6.0+ only)
var ed25519Key = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_ed25519", null);
using var ed25519Credential = new PrivateKeyCredential("username", ed25519Key);

// ECDSA key authentication
var ecdsaKey = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_ecdsa", null);
using var ecdsaCredential = new PrivateKeyCredential("username", ecdsaKey);

// Encrypted key with passphrase
var encryptedKey = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_rsa_encrypted", "mypassphrase");
using var encryptedCredential = new PrivateKeyCredential("username", encryptedKey);

// Multiple keys for fallback (tries each until one succeeds)
var key1 = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_ed25519", null);
var key2 = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_rsa", null);
using var multiKeyCredential = new PrivateKeyCredential("username", key1, key2);
```

#### Disposal Best Practices

`PrivateKeyCredential` implements `IDisposable` to securely clear private key material from memory. Always use `using` statements or explicit disposal:

```csharp
// Recommended: using statement ensures disposal
using var credential = new PrivateKeyCredential("user", key);
await using var client = new SshClient(new SshClientSettings
{
    Host = "server.example.com",
    Credential = credential
});
await client.ConnectAndAuthenticateAsync();
// credential is disposed when it goes out of scope

// Alternative: explicit disposal in try-finally
var credential2 = new PrivateKeyCredential("user", key);
try
{
    // Use credential...
}
finally
{
    credential2.Dispose();  // Clears key material from memory
}
```

---

### KeyboardInteractiveCredential

**Namespace:** `TwSsh.Authentication`

Keyboard-interactive authentication credential with custom prompt handling.

```csharp
public sealed class KeyboardInteractiveCredential : SshCredential
```

#### Constructor

```csharp
public KeyboardInteractiveCredential(
    string username,
    Func<string, string[], bool[], string[]> promptHandler);
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `PromptHandler` | `Func<string, string[], bool[], string[]>` | Handler for server prompts |

#### Static Methods

```csharp
// Create with simple password handler
public static KeyboardInteractiveCredential FromPassword(string username, string password);
```

#### Example

```csharp
// Simple password-based keyboard-interactive
var credential = KeyboardInteractiveCredential.FromPassword("user", "password");

// Custom handler for multi-factor auth
var mfaCredential = new KeyboardInteractiveCredential("user", (instruction, prompts, echo) =>
{
    var responses = new string[prompts.Length];
    for (int i = 0; i < prompts.Length; i++)
    {
        if (prompts[i].Contains("Password"))
            responses[i] = "mypassword";
        else if (prompts[i].Contains("code") || prompts[i].Contains("OTP"))
            responses[i] = GetOtpCode(); // Your OTP implementation
        else
            responses[i] = Console.ReadLine();
    }
    return responses;
});
```

---

## Private Key Parsing

### PrivateKeyParser

**Namespace:** `TwSsh.Core.Authentication`

Static utility class for parsing SSH private keys. Automatically detects key format (OpenSSH, PEM, PKCS#8).

```csharp
public static class PrivateKeyParser
```

#### Static Methods

```csharp
// Load private key from file
public static SshPrivateKey LoadFromFile(string filePath, string? passphrase = null);

// Parse private key from string data
public static SshPrivateKey Parse(string keyData, string? passphrase = null);

// Parse private key from binary data
public static SshPrivateKey ParseBinary(byte[] keyData, string? passphrase = null);
```

#### Supported Key Formats

| Format | Header |
|--------|--------|
| OpenSSH | `BEGIN OPENSSH PRIVATE KEY` |
| PEM RSA | `BEGIN RSA PRIVATE KEY` |
| PEM EC | `BEGIN EC PRIVATE KEY` |
| PKCS#8 | `BEGIN PRIVATE KEY` |
| PKCS#8 Encrypted | `BEGIN ENCRYPTED PRIVATE KEY` |

#### Example

```csharp
using TwSsh.Core.Authentication;
using TwSsh.Authentication;

// Load from file (pass null for unencrypted keys)
var key = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_rsa", null);

// Load encrypted key with passphrase
var encryptedKey = PrivateKeyParser.LoadFromFile("/home/user/.ssh/id_ed25519", "passphrase");

// Parse from string (pass null for unencrypted keys)
var keyContent = File.ReadAllText("~/.ssh/id_rsa");
var key2 = PrivateKeyParser.Parse(keyContent, null);

// Use with credential
var credential = new PrivateKeyCredential("username", key);
```

---

### SshPrivateKey

**Namespace:** `TwSsh.Authentication`

Abstract base class representing an SSH private key.

```csharp
public abstract class SshPrivateKey : IDisposable
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `Algorithm` | `string` | Key algorithm name (e.g., "ssh-rsa", "ssh-ed25519") |

#### Methods

```csharp
// Get public key blob in SSH wire format
public abstract byte[] GetPublicKeyBlob();

// Sign data using the private key
public abstract byte[] Sign(ReadOnlySpan<byte> data, string? signatureAlgorithm = null);

// Clear sensitive data from memory
public abstract void Dispose();
```

#### Supported Key Types

- RSA (ssh-rsa, rsa-sha2-256, rsa-sha2-512)
- Ed25519 (ssh-ed25519) - **requires .NET 6.0+**
- ECDSA (ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521)

**Note:** Ed25519 keys require .NET 6.0 or later. On .NET Standard 2.1, use RSA or ECDSA keys.

---

## PTY Settings

### PtyRequest

**Namespace:** `TwSsh.Connection`

Configuration for pseudo-terminal (PTY) requests when creating interactive shells.

```csharp
public sealed class PtyRequest
```

#### Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `TerminalType` | `string` | `"xterm"` | Terminal type (e.g., "xterm", "vt100", "xterm-256color") |
| `WidthChars` | `uint` | `80` | Terminal width in characters |
| `Columns` | `uint` | `80` | Alias for WidthChars |
| `HeightRows` | `uint` | `24` | Terminal height in rows |
| `Rows` | `uint` | `24` | Alias for HeightRows |
| `WidthPixels` | `uint` | `0` | Terminal width in pixels (0 = ignore) |
| `HeightPixels` | `uint` | `0` | Terminal height in pixels (0 = ignore) |
| `Modes` | `Dictionary<TerminalMode, uint>` | `new()` | Terminal modes |

**Note:** `Columns`/`Rows` and `WidthChars`/`HeightRows` are aliases - either naming convention works.

#### Example

```csharp
using TwSsh.Connection;

var ptyRequest = new PtyRequest
{
    TerminalType = "xterm-256color",
    Columns = 120,      // or WidthChars = 120
    Rows = 40,          // or HeightRows = 40
    WidthPixels = 0,
    HeightPixels = 0
};

await using var shell = await client.CreateShellAsync(ptyRequest);
```

---

## Command Execution

### SshCommand

**Namespace:** `TwSsh.Client`

Represents an SSH command execution.

```csharp
public sealed class SshCommand : IAsyncDisposable
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `CommandText` | `string` | Command text to execute |
| `OutputStream` | `Stream` | Standard output stream |
| `ErrorStream` | `Stream` | Standard error stream |
| `ExitStatus` | `int?` | Exit status (null if not exited) |
| `HasExecuted` | `bool` | Whether command has executed |

#### Methods

```csharp
// Execute command and get result
public ValueTask<SshCommandResult> ExecuteAsync(CancellationToken ct = default);

// Execute and return only stdout as string
public ValueTask<string> ExecuteAndGetOutputAsync(CancellationToken ct = default);

// Get current output as string
public string GetOutputString(Encoding? encoding = null);

// Get current error output as string
public string GetErrorString(Encoding? encoding = null);
```

#### Example

```csharp
// Simple execution
var result = await client.RunCommandAsync("ls -la");
Console.WriteLine(result.Output);

// Using command object for streaming output
await using var command = await client.CreateCommandAsync("tail -f /var/log/syslog");
var result = await command.ExecuteAsync();
```

---

### SshCommandResult

**Namespace:** `TwSsh.Client`

Result from an SSH command execution.

```csharp
public sealed class SshCommandResult
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `Output` | `string` | Standard output |
| `Error` | `string` | Standard error |
| `ErrorOutput` | `string` | Alias for Error |
| `ExitStatus` | `int` | Exit status code |
| `ExitCode` | `int` | Alias for ExitStatus |
| `IsSuccess` | `bool` | True if ExitStatus is 0 |

#### Example

```csharp
var result = await client.RunCommandAsync("grep 'error' /var/log/app.log");

if (result.IsSuccess)
{
    Console.WriteLine($"Found errors:\n{result.Output}");
}
else
{
    Console.WriteLine($"Command failed with code {result.ExitCode}");
    Console.WriteLine($"Error: {result.ErrorOutput}");
}
```

---

## Interactive Shell

### ShellStream

**Namespace:** `TwSsh.Client`

An interactive shell stream extending `System.IO.Stream`.

```csharp
public class ShellStream : Stream
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `Available` | `int` | Bytes available to read |
| `DataAvailable` | `bool` | Whether data is available |

#### Methods

##### Read/Write Methods (inherited from Stream)

```csharp
public override int Read(byte[] buffer, int offset, int count);
public override void Write(byte[] buffer, int offset, int count);
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken ct);
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken ct);
```

##### Text Methods

```csharp
// Read a line
public string? ReadLine();
public ValueTask<string?> ReadLineAsync(CancellationToken ct = default);

// Write text
public void Write(string text);
public ValueTask WriteAsync(string text, CancellationToken ct = default);

// Write line (with newline)
public void WriteLine(string text);
public ValueTask WriteLineAsync(string text, CancellationToken ct = default);
```

##### Expect Methods

```csharp
// Wait for specific text - returns all text received up to and including match
public string? Expect(string text, TimeSpan? timeout = null);
public Task<string> ExpectAsync(string text, TimeSpan? timeout = null, CancellationToken ct = default);

// Wait for regex match - returns all text received up to and including match
public string? Expect(Regex regex, TimeSpan? timeout = null);
public Task<string> ExpectAsync(Regex regex, TimeSpan? timeout = null, CancellationToken ct = default);
```

**Important Notes:**

1. **Return Type**: Both overloads return the accumulated text as a `string`, not a `Match` object. To extract regex groups, apply the regex to the returned string.

2. **Default Timeout**: If no timeout is specified, ExpectAsync uses the `OperationTimeout` from `SshClientSettings` (default: 1 minute). Always specify a timeout for shell prompts.

3. **ANSI Escape Codes**: Shell prompts often include ANSI escape sequences for colors and formatting. Use regex patterns that account for this:

```csharp
// Simple pattern may not match colored prompts
await shell.ExpectAsync("$");  // May timeout if prompt has colors

// Better: Use regex that handles escape sequences
var promptPattern = new Regex(@"[\$#]\s*$");
await shell.ExpectAsync(promptPattern, TimeSpan.FromSeconds(5));
```

4. **TimeoutException**: If the pattern is not found within the timeout, the method throws `TimeoutException` (sync) or the Task is cancelled (async).

**Example with Robust Prompt Handling:**

```csharp
// Wait for common shell prompts (handles ANSI codes and different prompt styles)
var shellPrompt = new Regex(@"[$#>]\s*$");
await shell.ExpectAsync(shellPrompt, TimeSpan.FromSeconds(10));
```

##### PTY Control

```csharp
// Send window size change (ShellStream simplified method)
public Task SendWindowChangeAsync(uint columns, uint rows);
```

**Note:** ShellStream provides a simplified 2-parameter method. For full control including pixel dimensions, use `ISessionChannel.SendWindowChangeAsync()` directly.

#### Example

```csharp
await using var shell = await client.CreateShellAsync();

// Wait for prompt
await shell.ExpectAsync("$", TimeSpan.FromSeconds(5));

// Execute command
shell.WriteLine("ls -la");
var output = await shell.ExpectAsync("$", TimeSpan.FromSeconds(5));
Console.WriteLine(output);

// Interactive session
shell.WriteLine("cd /var/log");
await shell.ExpectAsync("$");
shell.WriteLine("cat syslog | head -20");
var logs = await shell.ExpectAsync("$");
Console.WriteLine(logs);
```

---

## Port Forwarding

### ForwardedPort

**Namespace:** `TwSsh.Client`

Base class for port forwarding.

```csharp
public abstract class ForwardedPort : IAsyncDisposable
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `IsStarted` | `bool` | Whether forwarding is active |

#### Events

| Event | Type | Description |
|-------|------|-------------|
| `ErrorOccurred` | `EventHandler<Exception>` | Raised on forwarding error |

#### Methods

```csharp
// Stop forwarding
public abstract ValueTask StopAsync();
```

---

### LocalPortForwarder

**Namespace:** `TwSsh.Client`

Local port forwarding (SSH -L option). Listens on a local port and forwards to a remote destination through SSH.

```csharp
public sealed class LocalPortForwarder : ForwardedPort
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `LocalHost` | `string` | Local host being listened on |
| `LocalPort` | `int` | Local port being listened on |
| `RemoteHost` | `string` | Remote destination host |
| `RemotePort` | `int` | Remote destination port |

#### Example

```csharp
// Forward local port 8080 to remote MySQL port 3306
await using var tunnel = await client.ForwardLocalPortAsync(
    "127.0.0.1", 8080,
    "127.0.0.1", 3306);

// Now connect to MySQL at localhost:8080
var connectionString = "Server=127.0.0.1;Port=8080;Database=mydb;User=root;Password=secret;";
```

---

### RemotePortForwarder

**Namespace:** `TwSsh.Client`

Remote port forwarding (SSH -R option). Server listens on a remote port and forwards to a local destination through SSH.

```csharp
public sealed class RemotePortForwarder : ForwardedPort
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `RemoteHost` | `string` | Remote host being listened on |
| `RemotePort` | `int` | Remote port being listened on |
| `LocalHost` | `string` | Local destination host |
| `LocalPort` | `int` | Local destination port |

#### Example

```csharp
// Allow remote server to access local web server
await using var tunnel = await client.ForwardRemotePortAsync(
    "0.0.0.0", 8080,    // Remote: listen on all interfaces, port 8080
    "127.0.0.1", 3000); // Local: forward to localhost:3000

if (tunnel is RemotePortForwarder rpf)
{
    Console.WriteLine($"Remote port {rpf.RemotePort} forwarded to local port {rpf.LocalPort}");
}
```

---

## SFTP

### SftpClient

**Namespace:** `TwSsh.Sftp`

High-level SFTP client for file transfer operations.

```csharp
public sealed class SftpClient : IAsyncDisposable
```

#### Static Methods

```csharp
// Create SFTP client from authenticated SSH connection
public static ValueTask<SftpClient> ConnectAsync(SshClient client, CancellationToken ct = default);
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `IsConnected` | `bool` | Whether SFTP session is active |
| `ProtocolVersion` | `int` | Negotiated SFTP protocol version (3-6) |
| `Capabilities` | `SftpCapabilities` | Server capability detection |

#### File Transfer Methods

```csharp
// Upload file
public ValueTask UploadFileAsync(string localPath, string remotePath, CancellationToken ct = default);
public ValueTask UploadFileAsync(string localPath, string remotePath, SftpTransferOptions options, IProgress<SftpTransferProgress>? progress = null, CancellationToken ct = default);

// Download file
public ValueTask DownloadFileAsync(string remotePath, string localPath, CancellationToken ct = default);
public ValueTask DownloadFileAsync(string remotePath, string localPath, SftpTransferOptions options, IProgress<SftpTransferProgress>? progress = null, CancellationToken ct = default);

// Batch transfers - by file list
public ValueTask DownloadFilesAsync(IEnumerable<string> remotePaths, string localDirectory, IProgress<SftpTransferProgress>? progress = null, CancellationToken ct = default);
public ValueTask UploadFilesAsync(IEnumerable<string> localPaths, string remoteDirectory, IProgress<SftpTransferProgress>? progress = null, CancellationToken ct = default);

// Batch transfers - by directory and pattern
public ValueTask DownloadFilesAsync(string remoteDirectory, string localDirectory, string pattern = "*", bool recursive = false, IProgress<SftpTransferProgress>? progress = null, CancellationToken ct = default);
public ValueTask UploadFilesAsync(string localDirectory, string remoteDirectory, string pattern = "*", bool recursive = false, IProgress<SftpTransferProgress>? progress = null, CancellationToken ct = default);

// Directory sync
public ValueTask SyncToRemoteAsync(string localDirectory, string remoteDirectory, bool deleteExtra = false, CancellationToken ct = default);
```

#### Stream Operations

```csharp
// Open file as stream
public ValueTask<SftpFileStream> OpenFileAsync(string path, FileMode mode, FileAccess access, CancellationToken ct = default);
public ValueTask<SftpFileStream> OpenReadAsync(string path, CancellationToken ct = default);
public ValueTask<SftpFileStream> OpenWriteAsync(string path, CancellationToken ct = default);

// Stream transfers
public ValueTask DownloadToStreamAsync(string remotePath, Stream destination, CancellationToken ct = default);
public ValueTask UploadFromStreamAsync(Stream source, string remotePath, CancellationToken ct = default);

// Read/write all bytes
public ValueTask<byte[]> ReadAllBytesAsync(string path, CancellationToken ct = default);
public ValueTask WriteAllBytesAsync(string path, byte[] data, CancellationToken ct = default);
```

#### File Operations

```csharp
// Delete file
public ValueTask DeleteFileAsync(string path, CancellationToken ct = default);

// Rename/move file
public ValueTask RenameAsync(string oldPath, string newPath, CancellationToken ct = default);
public ValueTask RenameAsync(string oldPath, string newPath, bool overwrite, CancellationToken ct = default);

// POSIX rename (atomic overwrite, requires server extension)
public ValueTask PosixRenameAsync(string oldPath, string newPath, CancellationToken ct = default);

// File info and attributes
public ValueTask<SftpFileInfo> GetFileInfoAsync(string path, CancellationToken ct = default);
public ValueTask<bool> ExistsAsync(string path, CancellationToken ct = default);
public ValueTask<SftpFileAttributes> GetAttributesAsync(string path, CancellationToken ct = default);
public ValueTask<SftpFileAttributes> GetLinkAttributesAsync(string path, CancellationToken ct = default);
public ValueTask SetAttributesAsync(string path, SftpFileAttributes attrs, CancellationToken ct = default);
public ValueTask<string> GetRealPathAsync(string path, CancellationToken ct = default);
public ValueTask<string> GetWorkingDirectoryAsync(CancellationToken ct = default);
```

#### Directory Operations

```csharp
// Create directory
public ValueTask CreateDirectoryAsync(string path, CancellationToken ct = default);
public ValueTask CreateDirectoryAsync(string path, uint permissions, CancellationToken ct = default);

// Delete directory
public ValueTask DeleteDirectoryAsync(string path, CancellationToken ct = default);

// List directory contents (returns entries)
public ValueTask<IReadOnlyList<SftpDirectoryEntry>> ListDirectoryAsync(string path, CancellationToken ct = default);
public IAsyncEnumerable<SftpDirectoryEntry> EnumerateDirectoryAsync(string path, CancellationToken ct = default);

// List directory contents (returns file info)
public ValueTask<IReadOnlyList<SftpFileInfo>> ListDirectoryInfoAsync(string path, CancellationToken ct = default);
public IAsyncEnumerable<SftpFileInfo> EnumerateDirectoryInfoAsync(string path, CancellationToken ct = default);
```

#### Link Operations

```csharp
// Create symbolic link
public ValueTask CreateSymbolicLinkAsync(string linkPath, string targetPath, CancellationToken ct = default);

// Read symbolic link target
public ValueTask<string> ReadSymbolicLinkAsync(string path, CancellationToken ct = default);

// Create hard link (requires server extension)
public ValueTask CreateHardLinkAsync(string existingPath, string newPath, CancellationToken ct = default);
```

#### Filesystem Info

```csharp
// Get filesystem statistics (requires server extension)
public ValueTask<SftpStatVfs?> GetFilesystemInfoAsync(string path, CancellationToken ct = default);
```

#### Example

```csharp
using TwSsh.Client;
using TwSsh.Sftp;

await using var client = new SshClient(settings);
await client.ConnectAndAuthenticateAsync();

await using var sftp = await SftpClient.ConnectAsync(client);

// Upload with progress
var progress = new Progress<SftpTransferProgress>(p =>
    Console.WriteLine($"{p.FileName}: {p.BytesTransferred}/{p.TotalBytes} ({p.PercentComplete:F1}%)"));

await sftp.UploadFileAsync("local.zip", "/remote/file.zip", new SftpTransferOptions(), progress);

// List directory
var files = await sftp.ListDirectoryAsync("/home/user");
foreach (var file in files)
{
    Console.WriteLine($"{file.FileName} - {file.Attributes.Size} bytes");
}
```

---

### SftpFileStream

**Namespace:** `TwSsh.Sftp`

A seekable stream for reading and writing remote files over SFTP.

```csharp
public sealed class SftpFileStream : Stream
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `CanRead` | `bool` | Whether stream can read |
| `CanWrite` | `bool` | Whether stream can write |
| `CanSeek` | `bool` | Always `true` |
| `Length` | `long` | File length |
| `Position` | `long` | Current position |

#### Methods

Standard `Stream` methods are supported:

```csharp
public override int Read(byte[] buffer, int offset, int count);
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken ct);
public override void Write(byte[] buffer, int offset, int count);
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken ct);
public override long Seek(long offset, SeekOrigin origin);
public override void SetLength(long value);
public override void Flush();
```

#### Example

```csharp
// Read from remote file
await using var stream = await sftp.OpenFileAsync("/remote/file.txt", FileMode.Open, FileAccess.Read);
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();

// Write to remote file
await using var writeStream = await sftp.OpenFileAsync("/remote/output.txt", FileMode.Create, FileAccess.Write);
await using var writer = new StreamWriter(writeStream);
await writer.WriteLineAsync("Hello, World!");
```

---

### SftpFileInfo

**Namespace:** `TwSsh.Sftp`

Information about a remote file or directory.

```csharp
public sealed class SftpFileInfo
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `FullName` | `string` | Full path to file |
| `Name` | `string` | File name only |
| `Attributes` | `SftpFileAttributes` | File attributes |
| `IsDirectory` | `bool` | Whether this is a directory |
| `IsFile` | `bool` | Whether this is a regular file |
| `IsRegularFile` | `bool` | Alias for IsFile |
| `IsSymbolicLink` | `bool` | Whether this is a symbolic link |
| `Length` | `long` | File size in bytes |
| `Size` | `long` | Alias for Length (both are equivalent) |
| `LastAccessTime` | `DateTime?` | Last access time |
| `LastWriteTime` | `DateTime?` | Last modification time |
| `Permissions` | `uint?` | Unix permissions |

#### Example

```csharp
var info = await sftp.GetFileInfoAsync("/home/user/data.txt");

Console.WriteLine($"Name: {info.Name}");
Console.WriteLine($"Size: {info.Size} bytes");
Console.WriteLine($"Modified: {info.LastWriteTime}");
Console.WriteLine($"Is Directory: {info.IsDirectory}");
```

---

### SftpDirectoryEntry

**Namespace:** `TwSsh.Sftp`

A single entry from a directory listing.

```csharp
public sealed class SftpDirectoryEntry
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `FileName` | `string` | File or directory name |
| `LongName` | `string` | Long format listing (like `ls -l`) |
| `Attributes` | `SftpFileAttributes` | File attributes |
| `IsDirectory` | `bool` | Whether this is a directory |
| `IsRegularFile` | `bool` | Whether this is a regular file |
| `IsSymbolicLink` | `bool` | Whether this is a symbolic link |
| `Size` | `long` | File size in bytes |
| `ModifyTime` | `DateTimeOffset?` | Last modification time |

#### Example

```csharp
var entries = await sftp.ListDirectoryAsync("/var/log");

foreach (var entry in entries.Where(e => !e.FileName.StartsWith(".")))
{
    var type = entry.IsDirectory ? "[DIR]" : "     ";
    Console.WriteLine($"{type} {entry.FileName,-30} {entry.Size,12} bytes");
```

---

### SftpTransferOptions

**Namespace:** `TwSsh.Sftp`

Options for file transfer operations.

```csharp
public sealed class SftpTransferOptions
```

#### Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `BufferSize` | `int` | `32768` | Transfer buffer size |
| `Resume` | `bool` | `false` | Resume interrupted transfers |
| `StartOffset` | `long` | `0` | Offset to start transfer from |
| `MaxBytes` | `long?` | `null` | Maximum bytes to transfer (null = all) |
| `VerifySize` | `bool` | `true` | Verify file sizes after transfer |
| `Overwrite` | `bool` | `true` | Overwrite existing files |
| `PreserveTimestamps` | `bool` | `false` | Preserve file timestamps |
| `PreservePermissions` | `bool` | `false` | Preserve file permissions |
| `ParallelRequests` | `int` | `1` | Number of parallel requests |
| `ProgressInterval` | `long` | `0` | Progress reporting interval (bytes) |

#### Static Factory Methods

```csharp
// Default options
public static SftpTransferOptions Default { get; }

// Resume a download from existing file size
public static SftpTransferOptions ResumeDownload(long startOffset);

// Resume an upload from existing remote file size
public static SftpTransferOptions ResumeUpload(long startOffset);

// Create options with parallel requests
public static SftpTransferOptions Parallel(int requests = 4);
```

#### Example

```csharp
// Standard transfer
var options = new SftpTransferOptions
{
    BufferSize = 65536,
    PreserveTimestamps = true
};

// Resume interrupted download
var localSize = new FileInfo("partial.zip").Length;
var resumeOptions = SftpTransferOptions.ResumeDownload(localSize);
await sftp.DownloadFileAsync("/remote/file.zip", "partial.zip", resumeOptions, progress);

// High-speed parallel transfer
var parallelOptions = SftpTransferOptions.Parallel(4);
await sftp.UploadFileAsync("large-file.zip", "/remote/large-file.zip", parallelOptions, progress);
```

---

### SftpTransferProgress

**Namespace:** `TwSsh.Sftp`

Progress information for file transfers.

```csharp
public sealed class SftpTransferProgress
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `FileName` | `string` | Name of file being transferred |
| `BytesTransferred` | `long` | Bytes transferred so far |
| `TotalBytes` | `long` | Total bytes to transfer |
| `Elapsed` | `TimeSpan` | Time elapsed since transfer started |
| `PercentComplete` | `double` | Completion percentage (0-100) |
| `BytesPerSecond` | `double` | Transfer speed in bytes per second |
| `EstimatedRemaining` | `TimeSpan?` | Estimated time remaining (null if unknown) |
| `IsComplete` | `bool` | Whether transfer is complete |

#### Example

```csharp
var progress = new Progress<SftpTransferProgress>(p =>
{
    var speedKB = p.BytesPerSecond / 1024;
    var eta = p.EstimatedRemaining?.TotalSeconds ?? 0;
    Console.Write($"\r{p.PercentComplete:F1}% - {speedKB:F1} KB/s - ETA: {eta:F0}s");
});

await sftp.DownloadFileAsync("/remote/file.zip", "local.zip", new SftpTransferOptions(), progress);
Console.WriteLine("\nComplete!");
```

---

### SftpCapabilities

**Namespace:** `TwSsh.Sftp`

Detects server capabilities and supported features.

```csharp
public sealed class SftpCapabilities
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `ProtocolVersion` | `int` | Negotiated protocol version |
| `SupportsV3` | `bool` | SFTP version 3 support |
| `SupportsV4` | `bool` | SFTP version 4 support |
| `SupportsV5` | `bool` | SFTP version 5 support |
| `SupportsV6` | `bool` | SFTP version 6 support |
| `SupportsPosixRename` | `bool` | posix-rename@openssh.com extension |
| `SupportsStatVfs` | `bool` | statvfs@openssh.com extension |
| `SupportsFStatVfs` | `bool` | fstatvfs@openssh.com extension |
| `SupportsHardLink` | `bool` | hardlink@openssh.com extension |
| `SupportsFsync` | `bool` | fsync@openssh.com extension |
| `SupportsSymlinks` | `bool` | Symlink support (v3+) |
| `SupportsFileType` | `bool` | File type in attributes (v4+) |
| `SupportsOwnerGroupNames` | `bool` | Owner/group names in attributes (v4+) |
| `SupportsCreationTime` | `bool` | Creation time in attributes (v4+) |
| `SupportsRenameFlags` | `bool` | Atomic rename with overwrite (v5+) |
| `SupportsTextMode` | `bool` | Text mode transfers (v5+) |
| `SupportsFileLocking` | `bool` | File locking operations (v6+) |
| `SupportsLinkOperation` | `bool` | Hard links in protocol (v6+) |

#### Methods

```csharp
// Check for any extension by name
public bool SupportsExtension(string extensionName);

// Get extension version string
public string? GetExtensionVersion(string extensionName);

// Get all supported extensions as dictionary
public IReadOnlyDictionary<string, string> GetSupportedExtensions();
```

#### Example

```csharp
var caps = sftp.Capabilities;

Console.WriteLine($"Protocol Version: {caps.ProtocolVersion}");
Console.WriteLine($"Supports StatVfs: {caps.SupportsStatVfs}");
Console.WriteLine($"Supports POSIX Rename: {caps.SupportsPosixRename}");

// Use capability detection for feature availability
if (caps.SupportsStatVfs)
{
    var fsInfo = await sftp.GetFilesystemInfoAsync("/");
    Console.WriteLine($"Free space: {fsInfo?.FreeSize} bytes");
}
```

---

### SftpStatVfs

**Namespace:** `TwSsh.Sftp`

Filesystem statistics from the statvfs@openssh.com extension.

```csharp
public sealed class SftpStatVfs
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `BlockSize` | `ulong` | Filesystem block size |
| `FragmentSize` | `ulong` | Fragment size |
| `TotalBlocks` | `ulong` | Total blocks in filesystem |
| `FreeBlocks` | `ulong` | Free blocks |
| `AvailableBlocks` | `ulong` | Available blocks (non-root) |
| `TotalSize` | `ulong` | Total size in bytes |
| `FreeSize` | `ulong` | Free space in bytes |
| `AvailableSize` | `ulong` | Available space in bytes |
| `UsedSize` | `ulong` | Used space in bytes |
| `UsagePercent` | `double` | Usage percentage (0-100) |
| `TotalInodes` | `ulong` | Total inodes |
| `FreeInodes` | `ulong` | Free inodes |
| `AvailableInodes` | `ulong` | Available inodes (non-root) |
| `FileSystemId` | `ulong` | Filesystem ID |
| `MountFlags` | `ulong` | Mount flags |
| `MaxNameLength` | `ulong` | Maximum filename length |

#### Example

```csharp
if (sftp.Capabilities.SupportsStatVfs)
{
    var stats = await sftp.GetFilesystemInfoAsync("/home");
    if (stats != null)
    {
        Console.WriteLine($"Total: {stats.TotalSize / 1024 / 1024 / 1024} GB");
        Console.WriteLine($"Free: {stats.FreeSize / 1024 / 1024 / 1024} GB");
        Console.WriteLine($"Used: {stats.UsedSize / 1024 / 1024 / 1024} GB");
        Console.WriteLine($"Usage: {stats.UsagePercent:F1}%");
    }
}
```

---

## SCP

### ScpClient

**Namespace:** `TwSsh.Scp`

SCP (Secure Copy Protocol) client for simple file copy operations over SSH.

```csharp
public sealed class ScpClient : IAsyncDisposable
```

#### Static Methods

```csharp
// Create SCP client from authenticated SSH connection
public static ValueTask<ScpClient> ConnectAsync(SshClient client, CancellationToken ct = default);
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `IsConnected` | `bool` | Whether SCP session is active |

#### Methods

```csharp
// Upload file to remote
public ValueTask UploadAsync(string localPath, string remotePath, CancellationToken ct = default);
public ValueTask UploadAsync(string localPath, string remotePath, IProgress<ScpTransferProgress>? progress, CancellationToken ct = default);

// Download file from remote
public ValueTask DownloadAsync(string remotePath, string localPath, CancellationToken ct = default);
public ValueTask DownloadAsync(string remotePath, string localPath, IProgress<ScpTransferProgress>? progress, CancellationToken ct = default);
```

#### Example

```csharp
using TwSsh.Client;
using TwSsh.Scp;

await using var client = new SshClient(settings);
await client.ConnectAndAuthenticateAsync();

await using var scp = await ScpClient.ConnectAsync(client);

// Upload with progress
var progress = new Progress<ScpTransferProgress>(p =>
    Console.WriteLine($"{p.FileName}: {p.PercentComplete:F1}%"));

await scp.UploadAsync("local.txt", "/remote/file.txt", progress);
await scp.DownloadAsync("/remote/data.bin", "local-data.bin", progress);
```

---

### ScpTransferProgress

**Namespace:** `TwSsh.Scp`

Progress information for SCP file transfers.

```csharp
public sealed class ScpTransferProgress
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `FileName` | `string` | Name of file being transferred |
| `BytesTransferred` | `long` | Bytes transferred so far |
| `TotalBytes` | `long` | Total bytes to transfer |
| `PercentComplete` | `double` | Completion percentage (0-100) |

---

## Licensing

### LicenseManager

**Namespace:** `TwSsh.Licensing`

Singleton manager for TWSSH licensing.

```csharp
public sealed class LicenseManager
```

#### Static Properties

| Property | Type | Description |
|----------|------|-------------|
| `Instance` | `LicenseManager` | Singleton instance |

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `IsInitialized` | `bool` | Whether license is initialized |
| `LicenseInfo` | `LicenseInfo?` | Current license information |

#### Methods

```csharp
// Initialize with license key string
public void Initialize(string licenseKey);

// Initialize from license file
public void InitializeFromFile(string filePath);

// Auto-discover and load license from common locations
public bool TryAutoInitialize();

// Check if feature is available
public bool HasFeature(LicenseFeatures feature);

// Require feature (throws if not available)
public void RequireFeature(LicenseFeatures feature);
```

##### TryAutoInitialize

Automatically searches for and loads a license file from common locations:
- `twssh.lic` (current directory)
- `license.lic` (current directory)
- Application directory
- `%APPDATA%\TwSsh\license.lic` (Windows)
- `%PROGRAMDATA%\TwSsh\license.lic` (Windows)
- `~/.config/twssh/license.lic` (Linux/macOS)

Returns `true` if a license was found and loaded, `false` otherwise.

#### Example

```csharp
// Option 1: License key
LicenseManager.Instance.Initialize("eyJJZCI6Ij...");

// Option 2: License file
LicenseManager.Instance.InitializeFromFile("C:/licenses/twssh.lic");

// Option 3: Auto-discovery
LicenseManager.Instance.TryAutoInitialize();

// Check features
if (LicenseManager.Instance.HasFeature(LicenseFeatures.PortForwarding))
{
    // Use port forwarding
}

// Get license info
var info = LicenseManager.Instance.LicenseInfo;
Console.WriteLine($"License: {info.Type}, Expires: {info.Expires}");
```

Get a free 30-day trial license at [https://twssh.io](https://twssh.io).

---

## Enumerations

### SshClientState

```csharp
public enum SshClientState
{
    Disconnected,    // Not connected
    Connecting,      // Connecting to server
    Connected,       // Connected but not authenticated
    Authenticated,   // Authenticated and ready
    Disconnecting    // Disconnecting
}
```

### HostKeyVerificationMode

```csharp
public enum HostKeyVerificationMode
{
    None,      // Accept all keys (insecure!)
    AutoAdd,   // Auto-add unknown keys
    Strict,    // Reject unknown keys
    Callback   // Use custom callback
}
```

### LicenseFeatures

```csharp
[Flags]
public enum LicenseFeatures
{
    None = 0,
    BasicSsh = 1,
    Shell = 2,
    Sftp = 4,              // Enables SFTP subsystem channel access
    Scp = 8,               // Enables SCP operations
    PublicKeyAuth = 16,
    PortForwarding = 32,
    KeyboardInteractive = 64,
    AdvancedCrypto = 128,
    ConnectionPooling = 256,
    PrioritySupport = 512
}
```

**Note:** The SFTP feature enables full SFTP client functionality via `SftpClient`. See [SFTP](#sftp) for the complete API and [SFTP Tutorial](TUTORIALS.md#sftp-file-transfer) for examples.

---

## Exceptions

### SshException

**Namespace:** `TwSsh.Common`

Base exception for SSH errors.

```csharp
public class SshException : Exception
```

### SshConnectionException

Connection-related errors.

```csharp
public class SshConnectionException : SshException
```

### SshAuthenticationException

Authentication failures.

```csharp
public class SshAuthenticationException : SshException
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `AllowedMethods` | `IReadOnlyList<string>?` | Authentication methods allowed by server |

The `AllowedMethods` property indicates what authentication methods the server accepts, useful for implementing fallback logic:

```csharp
try
{
    await client.ConnectAndAuthenticateAsync();
}
catch (SshAuthenticationException ex)
{
    Console.WriteLine($"Authentication failed: {ex.Message}");

    if (ex.AllowedMethods?.Contains("keyboard-interactive") == true)
    {
        // Try keyboard-interactive as fallback
    }
}
```

### SshChannelException

Channel operation errors (e.g., channel open failed, channel closed unexpectedly).

```csharp
public class SshChannelException : SshException
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `ChannelId` | `uint?` | The channel ID that failed (if applicable) |
| `ReasonCode` | `SshChannelOpenFailureReason?` | The failure reason code |

### SftpException

**Namespace:** `TwSsh.Sftp`

SFTP-specific errors with status codes.

```csharp
public class SftpException : SshException
```

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `StatusCode` | `SftpStatusCode` | The SFTP status code from the server |

**Note:** The error message typically contains path information when relevant. Use `ex.Message` for detailed error context.

#### Example

```csharp
try
{
    await sftp.DownloadFileAsync("/nonexistent/file.txt", "local.txt");
}
catch (SftpException ex) when (ex.StatusCode == SftpStatusCode.NoSuchFile)
{
    Console.WriteLine($"File not found: {ex.Message}");
}
catch (SftpException ex) when (ex.StatusCode == SftpStatusCode.PermissionDenied)
{
    Console.WriteLine($"Permission denied: {ex.Message}");
}
catch (SftpException ex)
{
    Console.WriteLine($"SFTP error ({ex.StatusCode}): {ex.Message}");
}
```

### SftpStatusCode

**Namespace:** `TwSsh.Sftp`

SFTP protocol status codes.

```csharp
public enum SftpStatusCode
{
    Ok = 0,                    // Success
    Eof = 1,                   // End of file
    NoSuchFile = 2,            // File or directory not found
    PermissionDenied = 3,      // Permission denied
    Failure = 4,               // General failure
    BadMessage = 5,            // Bad message format
    NoConnection = 6,          // No connection
    ConnectionLost = 7,        // Connection lost
    OpUnsupported = 8,         // Operation unsupported
    InvalidHandle = 9,         // Invalid file handle
    NoSuchPath = 10,           // Path not found
    FileAlreadyExists = 11,    // File already exists
    WriteProtect = 12,         // Write protected
    NoMedia = 13,              // No media
    NoSpaceOnFilesystem = 14,  // No space left
    QuotaExceeded = 15,        // Quota exceeded
    UnknownPrincipal = 16,     // Unknown user/group
    LockConflict = 17,         // File lock conflict
    DirNotEmpty = 18,          // Directory not empty
    NotADirectory = 19,        // Not a directory
    InvalidFilename = 20,      // Invalid filename
    LinkLoop = 21              // Symbolic link loop
}
```

### SshChannelOpenFailureReason

**Namespace:** `TwSsh.Connection`

Reason codes for channel open failures.

```csharp
public enum SshChannelOpenFailureReason : uint
{
    AdministrativelyProhibited = 1,  // Server policy forbids the channel
    ConnectFailed = 2,                // Connection to target failed
    UnknownChannelType = 3,           // Unknown channel type requested
    ResourceShortage = 4              // Server resource shortage
}
```

### LicenseException

License-related errors.

```csharp
public class LicenseException : Exception
```

---

## Logging

### ISshLogger

**Namespace:** `TwSsh.Common`

Interface for custom logging implementations.

```csharp
public interface ISshLogger
{
    void Log(SshLogLevel level, string message, Exception? exception = null);
    bool IsEnabled(SshLogLevel level);
}
```

#### Log Levels

```csharp
public enum SshLogLevel
{
    Trace,     // Detailed protocol messages
    Debug,     // Debugging information
    Info,      // General information
    Warning,   // Warning messages
    Error,     // Error messages
    Fatal      // Fatal errors
}
```

### NullSshLogger

**Namespace:** `TwSsh.Common`

Default logger that discards all log messages.

```csharp
public sealed class NullSshLogger : ISshLogger
```

#### Usage

To enable logging, implement `ISshLogger` and pass to `SshClientSettings`:

```csharp
public class ConsoleLogger : ISshLogger
{
    public void Log(SshLogLevel level, string message, Exception? ex = null)
    {
        Console.WriteLine($"[{level}] {message}");
        if (ex != null) Console.WriteLine($"  Exception: {ex.Message}");
    }

    public bool IsEnabled(SshLogLevel level) => level >= SshLogLevel.Info;
}

var settings = new SshClientSettings
{
    Host = "example.com",
    Credential = credential,
    Logger = new ConsoleLogger()
};
```

---

## Supported Algorithms

### Key Exchange

- `curve25519-sha256`
- `curve25519-sha256@libssh.org`
- `ecdh-sha2-nistp256`
- `ecdh-sha2-nistp384`
- `ecdh-sha2-nistp521`
- `diffie-hellman-group-exchange-sha256`
- `diffie-hellman-group16-sha512`
- `diffie-hellman-group14-sha256`

### Host Key

- `ssh-ed25519` (**requires .NET 6.0+**)
- `ecdsa-sha2-nistp256`
- `ecdsa-sha2-nistp384`
- `ecdsa-sha2-nistp521`
- `rsa-sha2-512`
- `rsa-sha2-256`
- `ssh-rsa`

**Note:** Ed25519 support requires .NET 6.0 or later. On .NET Standard 2.1, use RSA or ECDSA keys instead.

### Encryption

- `chacha20-poly1305@openssh.com`
- `aes256-gcm@openssh.com`
- `aes128-gcm@openssh.com`
- `aes256-ctr`
- `aes192-ctr`
- `aes128-ctr`

### MAC

- `hmac-sha2-512-etm@openssh.com`
- `hmac-sha2-256-etm@openssh.com`
- `hmac-sha2-512`
- `hmac-sha2-256`

### Compression

- `none`
- `zlib@openssh.com`
- `zlib`

---

Copyright 2025 Terminalworks Ltd. All rights reserved.
