AlfredBr.SshServer.Core
1.3.0
dotnet add package AlfredBr.SshServer.Core --version 1.3.0
NuGet\Install-Package AlfredBr.SshServer.Core -Version 1.3.0
<PackageReference Include="AlfredBr.SshServer.Core" Version="1.3.0" />
<PackageVersion Include="AlfredBr.SshServer.Core" Version="1.3.0" />
<PackageReference Include="AlfredBr.SshServer.Core" />
paket add AlfredBr.SshServer.Core --version 1.3.0
#r "nuget: AlfredBr.SshServer.Core, 1.3.0"
#:package AlfredBr.SshServer.Core@1.3.0
#addin nuget:?package=AlfredBr.SshServer.Core&version=1.3.0
#tool nuget:?package=AlfredBr.SshServer.Core&version=1.3.0
.NET SSH Server
A lightweight SSH server library in C# (.NET 10) that allows terminal clients to connect via SSH and interact with a TUI application. Inspired by charmbracelet's wish package in Go.
Quick Start
Install the Package
dotnet add package AlfredBr.SshServer.Core
View on NuGet: AlfredBr.SshServer.Core
Create Your Application
using AlfredBr.SshServer.Core;
public class MyApp : SshShellApplication
{
protected override string Prompt => "myapp> ";
protected override IEnumerable<string> Completions => ["help", "quit"];
protected override bool OnCommand(string command)
{
if (command == "quit") return false;
WriteLine($"You said: {Escape(command)}");
return true;
}
}
Run the Server
await SshServerHost.CreateBuilder()
.UsePort(2222)
.AllowAnonymous()
.UseMaxConnections(100)
.UseConnectionRateLimit(10, TimeSpan.FromSeconds(30))
.UseApplication<MyApp>()
.Build()
.RunAsync();
Connect
ssh -p 2222 localhost
See the API Manual for the complete API reference, or DEVELOPERS.md for the contributor guide.
Project Structure
| Project | Description |
|---|---|
| AlfredBr.SshServer.Core | The SSH server library — includes SSH protocol, TUI infrastructure, and builder API. |
| SshServer.Demo | Demo application showcasing Spectre.Console features. |
Key Classes
| Class | Description |
|---|---|
SshServerHost |
Main server host with fluent builder API. |
SshServerBuilder |
Fluent builder for configuring the server. |
SshShellApplication |
Abstract base class for SSH applications. |
Builder API
await SshServerHost.CreateBuilder()
.UsePort(2222) // TCP port (default: 2222)
.UseBanner("SSH-2.0-MyApp") // SSH protocol banner
.UseHostKeyPath("mykey.pem") // Host key file path
.AllowAnonymous() // Enable anonymous auth
.UseAuthorizedKeysFile("authorized_keys") // Public key auth file
.UseSessionTimeout(TimeSpan.FromMinutes(30)) // Idle timeout
.UseTransportOperationTimeout(TimeSpan.FromSeconds(30)) // Active SSH transport timeout
.UseMaxConnections(100) // Connection limit
.UseConnectionRateLimit(10, TimeSpan.FromSeconds(30)) // Per-IP connection-attempt limit
.UseLogLevel(LogLevel.Information) // Minimum log level
.UseApplication<MyApp>() // Your application class
.ConfigureLogging(builder => { ... }) // Custom logging config
.UseDefaultConfiguration(args) // Load from appsettings.json
.Build()
.RunAsync();
Multi-Application Support
Host multiple applications with username-based routing or an interactive menu:
await SshServerHost.CreateBuilder()
// Map usernames directly to apps
.MapUser<DemoApp>("demo") // ssh demo@host → DemoApp
.MapUser<AdminApp>("admin") // ssh admin@host → AdminApp
// Unmapped users see a selection menu
.UseApplicationMenu(menu => menu
.Add<DemoApp>("Demo", "Spectre.Console showcase")
.Add<AdminApp>("Admin", "Server administration")
.Add<MonitoringApp>("Monitor", "Live metrics dashboard")
.SetDefaultForExec("Demo")
.ReturnToMenuOnExit(true))
.Build()
.RunAsync();
Usage:
ssh demo@localhost -p 2222 # Direct to DemoApp
ssh admin@localhost -p 2222 # Direct to AdminApp
ssh localhost -p 2222 # Shows app selection menu
ssh guest@localhost "admin:logs" # Exec 'logs' in AdminApp
Features
Authentication
- Public key authentication — supports ssh-rsa, ssh-ed25519, ecdsa-sha2-nistp256/384/521
- Anonymous access — configurable via
AllowAnonymous()builder method - Authorized keys — standard OpenSSH
authorized_keysfile format
Host Keys
- ECDSA — auto-generated nistp256 key on first run
- Ed25519 — client key verification (via NSec.Cryptography)
- RSA — sha2-256/512 support
Terminal Emulation
- PTY support — full pseudo-terminal with resize handling
- Emacs-style line editing:
- Ctrl-A/E: beginning/end of line
- Ctrl-B/F: back/forward character
- Ctrl-P/N: previous/next history
- Ctrl-D: delete char or disconnect
- Ctrl-K/U: kill to end/beginning
- Ctrl-Y: yank (paste) from kill ring
- Ctrl-L: clear screen
- Alt-B/F: back/forward word
- Alt-D: delete word forward
- Home/End, Delete, Arrow keys
- Tab completion — command name auto-completion
- Command history — per-connection with navigation
TUI Integration (Spectre.Console)
- Rich output — tables, panels, rules, trees, bar charts
- Interactive prompts — selection, multi-select, confirmation, text input
- Live displays — progress bars, spinners, live-updating tables
SshShellApplication Helper Methods
| Method | Description |
|---|---|
WriteLine(text) |
Write a line of text (supports Spectre markup). |
Ask(prompt) |
Text input prompt (returns string). |
Ask<T>(prompt) |
Typed input prompt (e.g., int, double). |
AskBox(prompt, width) |
Show a bordered box input prompt (returns string). Width 0 = full console width. |
AskBox<T>(prompt, width) |
Show a bordered box prompt collecting a typed value. |
Confirm(prompt) |
Yes/No confirmation prompt. |
Select(prompt, items) |
Single-item selection list. |
MultiSelect(prompt, items) |
Multi-item selection list. |
Status(message, action) |
Status spinner while running an action. |
Progress(action) |
Progress bar for tracked tasks. |
Escape(text) |
Escape a string for safe use in Spectre markup. |
Run the Demo
dotnet run --project src/SshServer.Demo/SshServer.Demo.csproj
ssh -p 2222 localhost
Demo Commands
| Command | Description |
|---|---|
help |
Show available commands |
status |
Server status with process ID |
whoami |
Connection info, auth method, key fingerprint |
config |
Server configuration (hostname, IPs, settings) |
clear |
Clear the screen |
menu |
Interactive menu selection |
select |
Choose from a list |
multi |
Multi-select from a list |
confirm |
Yes/No confirmation |
ask |
Text input with validation |
askbox |
Bordered box input prompt demo |
demo |
Run all interactive demos |
progress |
Progress bar demo |
spinner |
Status spinner demo |
live |
Live-updating table demo |
tree |
Hierarchical tree display |
chart |
Bar chart visualization |
quit |
Disconnect |
With Public Key Authentication
- Add your public key to
authorized_keys:cat ~/.ssh/id_ed25519.pub >> src/SshServer.Demo/authorized_keys - Set
AllowAnonymoustofalseinappsettings.json - Connect:
ssh -p 2222 localhost
Scripted Commands (Exec Channel)
# Run a single command without entering interactive mode
ssh -p 2222 localhost status
ssh -p 2222 localhost whoami
ssh -p 2222 localhost config
# Use in scripts
result=$(ssh -p 2222 localhost status)
echo "Server says: $result"
Configuration
Settings can be configured via appsettings.json:
{
"SshServer": {
"Port": 2222,
"Banner": "SSH-2.0-SshServer",
"HostKeyPath": "hostkey_ecdsa_nistp256.pem",
"MaxConnections": 100,
"ConnectionRateLimitCount": 10,
"ConnectionRateLimitWindowSeconds": 30,
"LogLevel": "Debug",
"AllowAnonymous": true,
"AuthorizedKeysPath": "./authorized_keys",
"SessionTimeoutMinutes": 0,
"TransportOperationTimeoutSeconds": 30
}
}
Load configuration in your app:
await SshServerHost.CreateBuilder()
.UseDefaultConfiguration(args) // Loads appsettings.json + env vars + CLI args
.UseMaxConnections(100) // Optional explicit code default
.UseConnectionRateLimit(10, TimeSpan.FromSeconds(30))
.UseApplication<MyApp>()
.Build()
.RunAsync();
| Setting | Description |
|---|---|
Port |
TCP port to listen on (default: 2222) |
Banner |
SSH protocol banner |
HostKeyPath |
Path to host key PEM file |
MaxConnections |
Max concurrent connections (0 = unlimited) |
ConnectionRateLimitCount |
Max connection attempts per client IP in the active rate-limit window (0 = disabled) |
ConnectionRateLimitWindowSeconds |
Sliding window length for connection-attempt rate limiting in seconds (0 = disabled) |
LogLevel |
Minimum log level (Trace, Debug, Information, Warning, Error) |
AllowAnonymous |
Allow connections without authentication |
AuthorizedKeysPath |
Path to OpenSSH authorized_keys file |
SessionTimeoutMinutes |
Idle timeout in minutes (0 = disabled) |
TransportOperationTimeoutSeconds |
Timeout for active SSH transport operations; idle established sessions use SessionTimeoutMinutes |
Override via environment variables (SSHSERVER_ prefix) or command-line arguments.
When MaxConnections is reached, new SSH sessions are rejected until an existing session closes.
When ConnectionRateLimitCount and ConnectionRateLimitWindowSeconds are both positive, connection attempts are rate-limited per client IP using a sliding window.
Architecture
┌─────────────────────────────┐
│ Your Application │ extends SshShellApplication
├─────────────────────────────┤
│ SshServerHost + Builder │ Fluent API, lifecycle management
├─────────────────────────────┤
│ SshShellApplication │ Base class with helpers
├─────────────────────────────┤
│ TUI Infrastructure │ Spectre.Console, LineEditor
├─────────────────────────────┤
│ SSH Server Library │ FxSsh (modernised)
├─────────────────────────────┤
│ TCP Listener │ System.Net.Sockets
└─────────────────────────────┘
Dependencies
| Package | Purpose |
|---|---|
| FxSsh | Core SSH server (vendored source) |
| Microsoft.Extensions.Hosting | Configuration and DI |
| Microsoft.Extensions.Logging | Structured logging |
| Spectre.Console | TUI rendering and prompts |
| NSec.Cryptography | Ed25519 key support |
Project Status
Completed
- SSH transport (key exchange, encryption, MAC)
- Anonymous and public key authentication
- Connection limits
- Connection-attempt rate limiting
- PTY requests and window resize
- Emacs-style line editing with history
- Tab completion
- Spectre.Console integration (rendering + interactive prompts)
- Configuration via appsettings.json
- Graceful shutdown (Ctrl+C)
- Structured logging with connection IDs
- Ed25519 client key support
- Session timeout
- SshShellApplication base class for easy app development
- Fluent builder API
- NuGet package structure
Roadmap
- Password authentication
- Unit tests
Key RFCs
| RFC | Topic |
|---|---|
| 4251 | SSH Protocol Architecture |
| 4252 | SSH Authentication Protocol |
| 4253 | SSH Transport Layer Protocol |
| 4254 | SSH Connection Protocol (channels, PTY) |
| 5656 | ECDH key exchange |
| 8032 | Ed25519 signing |
Acknowledgements
This project builds upon the work of several excellent open source projects:
| Project | Author | Site | License | Role |
|---|---|---|---|---|
| FxSsh | Aimeast | MIT | Core SSH protocol implementation (vendored and extended) | |
| Spectre.Console | Patrik Svensson et al. | https://spectreconsole.net/ | MIT | TUI rendering, interactive prompts, and live displays |
| NSec.Cryptography | Nicholas Walther | nsec.rocks | MIT | Ed25519 client key verification |
| Microsoft.Extensions.* | Microsoft / .NET Foundation | MIT | Hosting, configuration, dependency injection, and logging | |
| charmbracelet/wish | Charmbracelet | https://charm.land/ | MIT | Original inspiration — SSH app framework for Go |
License
MIT — Copyright (c) 2026 Alfred Broderick
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Hosting (>= 10.0.3)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.3)
- Microsoft.Extensions.Logging.Console (>= 10.0.3)
- NSec.Cryptography (>= 24.4.0)
- Spectre.Console (>= 0.49.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.