sshnet/SSH.NET
SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓16 active contributors
- ✓Distributed ownership (top contributor 44% of recent commits)
Show 3 more →Show less
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/sshnet/ssh.net)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/sshnet/ssh.net on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: sshnet/SSH.NET
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/sshnet/SSH.NET shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 16 active contributors
- Distributed ownership (top contributor 44% of recent commits)
- MIT licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live sshnet/SSH.NET
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/sshnet/SSH.NET.
What it runs against: a local clone of sshnet/SSH.NET — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in sshnet/SSH.NET | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch develop exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of sshnet/SSH.NET. If you don't
# have one yet, run these first:
#
# git clone https://github.com/sshnet/SSH.NET.git
# cd SSH.NET
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of sshnet/SSH.NET and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "sshnet/SSH.NET(\\.git)?\\b" \\
&& ok "origin remote is sshnet/SSH.NET" \\
|| miss "origin remote is not sshnet/SSH.NET (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify develop >/dev/null 2>&1 \\
&& ok "default branch develop exists" \\
|| miss "default branch develop no longer exists"
# 4. Critical files exist
test -f "src/Renci.SshNet/BaseClient.cs" \\
&& ok "src/Renci.SshNet/BaseClient.cs" \\
|| miss "missing critical file: src/Renci.SshNet/BaseClient.cs"
test -f "src/Renci.SshNet/Channels/Channel.cs" \\
&& ok "src/Renci.SshNet/Channels/Channel.cs" \\
|| miss "missing critical file: src/Renci.SshNet/Channels/Channel.cs"
test -f "src/Renci.SshNet/Abstractions/SocketAbstraction.cs" \\
&& ok "src/Renci.SshNet/Abstractions/SocketAbstraction.cs" \\
|| miss "missing critical file: src/Renci.SshNet/Abstractions/SocketAbstraction.cs"
test -f "src/Renci.SshNet/ClientAuthentication.cs" \\
&& ok "src/Renci.SshNet/ClientAuthentication.cs" \\
|| miss "missing critical file: src/Renci.SshNet/ClientAuthentication.cs"
test -f "src/Renci.SshNet/CipherInfo.cs" \\
&& ok "src/Renci.SshNet/CipherInfo.cs" \\
|| miss "missing critical file: src/Renci.SshNet/CipherInfo.cs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/sshnet/SSH.NET"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
SSH.NET is a managed .NET library implementing SSH-2 protocol with emphasis on parallel operations, enabling developers to execute remote commands, transfer files via SFTP/SCP, and manage port forwarding over encrypted SSH connections. It wraps the underlying SSH protocol into type-safe C# classes like SshClient, SftpClient, and ShellStream, supporting modern ciphers (AES-GCM, ChaCha20-Poly1305) and post-quantum key exchange (ML-KEM768x25519, Sntrup761x25519). Monolithic library structure: src/ contains the core SSH.NET implementation organized by protocol layer and feature (clients, authentication, key exchange, encryption). Documentation in docfx/ (markdown + DocFX metadata). Strong .NET tooling use: Renci.SshNet.slnx (Visual Studio solution), Directory.Build.props for centralized MSBuild configuration, strong naming via Renci.SshNet.snk (signing key).
👥Who it's for
.NET application developers (C# in enterprise, Azure, desktop apps) who need to automate remote server operations, manage SFTP uploads/downloads, or implement secure remote administration without shelling out to external SSH clients. DevOps tooling builders, system integrators, and cloud automation engineers shipping on .NET platforms.
🌱Maturity & risk
Production-ready and actively maintained. The repo has a documented release pipeline (NuGet publishing), GitHub Actions CI/CD (.github/workflows/build.yml), comprehensive encryption/auth support, and appears to receive regular updates. The codebase is substantial (4.7M lines of C#) with professional structure (Directory.Build.props, Directory.Packages.props, CODEOWNERS), indicating mature governance.
Low-to-moderate risk. Single primary maintainer evident (CODEOWNERS exists but appears focused), and cryptographic code requires careful review for security regressions. No visible dependency lock file risks noted in file list, though SSH protocol complexity means subtle implementation bugs are high-impact. Last build status badge shown green, suggesting active CI, but without commit history visible here, stability must be verified by checking recent releases.
Active areas of work
Based on the file structure showing GitHub Workflows and Copilot instructions, active development is occurring with CI/CD automation in place. The repo includes recent infrastructure updates (dependabot.yml for automated dependency updates, copilot-instructions.md suggesting AI-assisted development). Documentation is being actively maintained (docfx/index.md, examples.md).
🚀Get running
git clone https://github.com/sshnet/SSH.NET.git
cd SSH.NET
dotnet build Renci.SshNet.slnx
dotnet test
The repo uses .NET SDK tooling and centralized package management (Directory.Packages.props), so no npm/nuget.exe wrapping needed beyond standard dotnet CLI.
Daily commands:
For library development: dotnet build Renci.SshNet.slnx && dotnet test. For consuming: add NuGet package SSH.NET to your project; examples in docfx/examples.md show instantiating new SshClient(host, user, keyFile) and calling .Connect() then .RunCommand(). No server startup needed—it's a client library.
🗺️Map of the codebase
src/Renci.SshNet/BaseClient.cs— Core base class for SSH clients; all SSH operations derive from here, making it essential for understanding authentication, connection lifecycle, and channel management.src/Renci.SshNet/Channels/Channel.cs— Abstract channel implementation that handles SSH protocol message routing, threading, and data flow for all channel types (session, forwarding, direct TCP/IP).src/Renci.SshNet/Abstractions/SocketAbstraction.cs— Platform abstraction for socket I/O operations; critical for cross-platform SSH transport layer and async/sync socket handling.src/Renci.SshNet/ClientAuthentication.cs— Implements SSH authentication methods (password, public key, keyboard-interactive) and multi-factor auth orchestration; every client connection requires this.src/Renci.SshNet/CipherInfo.cs— Defines SSH cipher suite metadata and algorithm selection; underpins all cryptographic operations and key exchange.src/Renci.SshNet/Common/AsyncResult.cs— Generic async/await bridging pattern used throughout the codebase for synchronous method wrappers over async operations.src/Renci.SshNet/AuthenticationMethod.cs— Base abstraction for pluggable authentication strategies; defines the contract for all authentication implementations.
🛠️How to make changes
Add a Custom Authentication Method
- Create a new class inheriting from
AuthenticationMethodinsrc/Renci.SshNet/AuthenticationMethod.cs. (src/Renci.SshNet/AuthenticationMethod.cs) - Implement the
Authenticate(ISession session)method to send authentication packets and handle responses. (src/Renci.SshNet/AuthenticationMethod.cs) - Register the method in
ClientAuthentication.cswhereAuthenticationMethodscollection is processed during connection. (src/Renci.SshNet/ClientAuthentication.cs) - Add tests following the pattern in test projects to verify integration with
BaseClient.Connect()flow. (src/Renci.SshNet/BaseClient.cs)
Add a New SSH Channel Type
- Create a new channel class inheriting from
Channelinsrc/Renci.SshNet/Channels/Channel.cs. (src/Renci.SshNet/Channels/Channel.cs) - Define the channel type string in
src/Renci.SshNet/Channels/ChannelTypes.cs(e.g., "session", "direct-tcpip"). (src/Renci.SshNet/Channels/ChannelTypes.cs) - Implement message handlers in your channel class overriding
OnChannelOpen,OnChannelData, etc. fromChannel.cs. (src/Renci.SshNet/Channels/Channel.cs) - Add factory method in
BaseClient.csor derived client to create and manage your channel type. (src/Renci.SshNet/BaseClient.cs)
Add Cipher or Key Exchange Algorithm Support
- Define algorithm metadata in
src/Renci.SshNet/CipherInfo.cs(name, key size, IV size, block size). (src/Renci.SshNet/CipherInfo.cs) - Register the cipher in the static initialization of
CipherInfoclass with a unique identifier. (src/Renci.SshNet/CipherInfo.cs) - Implement crypto operations using platform abstraction in
src/Renci.SshNet/Abstractions/CryptoAbstraction.cs. (src/Renci.SshNet/Abstractions/CryptoAbstraction.cs) - Update key exchange negotiation logic to advertise and negotiate your new algorithm. (
src/Renci.SshNet/BaseClient.cs)
Add a Proxy Transport (SOCKS/HTTP)
- Extend
SocketAbstraction.csto add proxy connection methods (SOCKS4, SOCKS5, HTTP CONNECT). (src/Renci.SshNet/Abstractions/SocketAbstraction.cs) - Create proxy configuration classes (e.g., ProxyConfig with host, port, credentials) at package root. (
src/Renci.SshNet/BaseClient.cs) - Modify
BaseClient.csto accept and use proxy configuration duringConnect()before SSH negotiation. (src/Renci.SshNet/BaseClient.cs) - Update
SocketExtensions.csto handle proxy-aware timeouts and error reporting. (src/Renci.SshNet/Abstractions/SocketExtensions.cs)
🪤Traps & gotchas
Strong naming (Renci.SshNet.snk) means unsigned builds will fail; ensure signing key is available in build environment. SSH protocol negotiation is stateful—connection state matters for API calls; calling .RunCommand() before .Connect() will fail silently with cryptic errors. Async API may deadlock if not properly awaited in sync contexts. Key files must be in OpenSSH or PuTTY format; raw binary keys will throw ParsingException. Post-quantum key exchange (ML-KEM768x25519) requires OpenSSH 8.10+; older servers will fall back to classical algorithms.
🏗️Architecture
💡Concepts to learn
- SSH Transport Layer (RFC 4253) — Understanding packet framing, cipher negotiation, and MAC verification is essential for debugging protocol-level issues and adding new encryption algorithms
- Key Exchange (Diffie-Hellman variants, ECDH) — SSH.NET implements multiple KE algorithms (Curve25519, ML-KEM768x25519); understanding DH/ECDH forward secrecy is crucial for secure authentication and key derivation
- HMAC (Hash-based Message Authentication Code) — SSH.NET uses HMAC for authenticating encrypted packets; understanding HMAC-SHA1/SHA256 is necessary for implementing or extending MAC verification logic
- Post-Quantum Cryptography (ML-KEM, Sntrup761) — SSH.NET supports ML-KEM768x25519 and Sntrup761x25519 for harvest-now, decrypt-later resistance; understanding lattice-based cryptography helps future-proof implementations
- Async/Await Patterns in .NET — SSH.NET emphasizes 'parallelism'; misusing async APIs (blocking on .Result, not awaiting, mixing sync/async) causes deadlocks; mastery is non-negotiable
- SFTP Protocol (SSH File Transfer Protocol, RFC 3659) — SftpClient implements SFTP packet framing, handle management, and file operations; RFC 3659 is the authoritative spec for debugging file transfer issues
- SCP Protocol (Secure Copy) — ScpClient wraps SCP command invocation; understanding the SCP wire protocol (space-separated metadata, binary file chunks) is needed for debugging or extending copy behavior
🔗Related repos
paramiko/paramiko— Python SSH library with similar scope (SFTP, SCP, port forwarding); useful for cross-platform protocol reference and testing interoperabilityopenssh/openssh-portable— Official OpenSSH implementation; authoritative reference for SSH-2 RFC 4253, protocol negotiation, and cipher behaviorAzure/azure-sdk-for-net— Major .NET consumer of SSH.NET for VM remote management and automation; shows real production usage patterns in cloud scenariosdotnet/runtime— .NET runtime repository; relevant for understanding async/await semantics, cryptography APIs, and platform-specific networking behaviorIdentityModel/IdentityModel— Complementary .NET auth library; relevant for enterprise authentication scenarios combining SSH with OAuth/SAML flows
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add async/await unit tests for SocketAbstraction.Async.cs
The file src/Renci.SshNet/Abstractions/SocketAbstraction.Async.cs exists but there's no evidence of comprehensive async socket operation tests in the repo structure. Given SSH.NET's focus on 'optimized for parallelism', adding dedicated async tests would validate concurrent socket operations, cancellation token handling, and async method reliability across different scenarios.
- [ ] Create src/Renci.SshNet.Tests/Abstractions/SocketAbstractionAsyncTests.cs
- [ ] Add tests for async socket connect, read, write operations with CancellationToken
- [ ] Add tests for timeout and exception handling in async socket operations
- [ ] Integrate tests into .github/workflows/build.yml if not already running async tests
Add integration tests for SSH.NET's three proxy types (SOCKS4, SOCKS5, HTTP)
The README advertises 'Connection via SOCKS4, SOCKS5 or HTTP proxy' as a key feature, but there's no visible test coverage in the file structure. Add integration tests using containerized proxy servers (or test fixtures) to verify all three proxy types work correctly with real SSH connections.
- [ ] Create src/Renci.SshNet.Tests/Integration/ProxyConnectionTests.cs
- [ ] Add test cases for SOCKS4, SOCKS5, and HTTP proxy authentication and tunneling
- [ ] Create docker-compose or test fixture setup for lightweight proxy servers
- [ ] Document proxy test setup in CONTRIBUTING.md
Add missing code documentation for CryptoAbstraction.cs and hash extension classes
The docfx documentation structure exists (docfx/docfx.json, docfx/index.md) but cryptographic abstractions in src/Renci.SshNet/Abstractions/ (CryptoAbstraction.cs, MD5Extensions.cs, SHA*Extensions.cs, RandomNumberGeneratorExtensions.cs) appear to lack XML documentation comments. This is critical for a security-focused library where developers need to understand which algorithms are used and why.
- [ ] Add XML documentation comments to src/Renci.SshNet/Abstractions/CryptoAbstraction.cs with security notes
- [ ] Add XML documentation to src/Renci.SshNet/Abstractions/SHA256Extensions.cs, SHA512Extensions.cs, etc.
- [ ] Document deprecation warnings for weaker algorithms (MD5, SHA1) in their respective files
- [ ] Regenerate docfx docs via .github/workflows/docs.yml and verify output
🌿Good first issues
- Add unit tests for EdDSA key parsing in PrivateKeyFile; trace the existing RSA/DSA test structure in src/ and create parallel test cases covering edge cases (invalid key lengths, corrupted headers, empty passphrases)
- Document the async/await best practices for ShellStream in docfx/examples.md; create a runnable code sample showing proper awaiting of ExpectAsync() and WriteLineAsync() to prevent deadlocks
- Implement missing SFTP file attribute getter optimizations; profile SftpClient.ListDirectory() to identify N+1 stat calls and add batch stat support to reduce round-trips for large directories
⭐Top contributors
Click to expand
Top contributors
- @Rob-Hague — 44 commits
- @dependabot[bot] — 20 commits
- @mus65 — 18 commits
- @scott-xu — 4 commits
- @Copilot — 3 commits
📝Recent commits
Click to expand
Recent commits
5b8382d— Serialise packets into a buffer (#1792) (Rob-Hague)b6217cb— Remove plaintext receive buffer (#1788) (Rob-Hague)8ed8d38— Encrypt packets in-place (#1787) (Rob-Hague)45d8631— Add SECURITY.md (#1785) (Rob-Hague)04a6dc0— Harden actions (#1784) (Rob-Hague)25a931c— ScpClient: allow disabling the -d flag (#1751) (mus65)0ff2c50— SftpClient: add IProgress to DownloadFileAsync and UploadFileAsync (#1771) (mus65)8cd6ad6— fix Build with newer .NET 10 SDKs (#1772) (mus65)c3da85d— Add GitHub Copilot instructions for SSH.NET (#1767) (Copilot)4a6f7fd— Bump the dependencies group (#1761) (dependabot[bot])
🔒Security observations
SSH.NET demonstrates a reasonable security posture as a cryptographic library. The codebase appears well-structured with proper abstraction layers for cryptographic operations. However, there are concerns around complex async socket/network implementations, custom protocol handling, and feature complexity (port forwarding, keyboard-interactive auth). The project follows good security disclosure practices with a SECURITY.md file and GitHub private vulnerability reporting. No obvious hardcoded credentials or SQL injection risks were identified in the file structure. Recommended actions: implement comprehensive security testing, maintain regular dependency updates, conduct threat modeling for complex features, and perform regular security audits of cryptographic implementations.
- Medium · Cryptographic Library Dependency Risk —
src/Renci.SshNet/Abstractions/CryptoAbstraction.cs. SSH.NET relies on .NET's cryptographic abstractions (CryptoAbstraction.cs) for encryption operations. While this is generally secure, the actual implementation depends on underlying .NET runtime cryptographic providers. Any vulnerabilities in those providers could impact SSH.NET. Fix: Regularly update .NET runtime to latest stable versions. Monitor security advisories for .NET cryptographic implementations. Consider adding dependency scanning in CI/CD pipeline. - Medium · Socket and Network Stream Abstraction Complexity —
src/Renci.SshNet/Abstractions/SocketAbstraction.cs, SocketAbstraction.Async.cs, StreamExtensions.cs. Custom socket and stream abstractions (SocketAbstraction.cs, SocketAbstraction.Async.cs, StreamExtensions.cs) introduce potential for implementation errors in network communication handling. Complex async operations could lead to race conditions or improper connection state management. Fix: Conduct thorough security code review of async network operations. Add fuzzing tests for network communication. Implement timeout protections and connection state validation. - Low · Potential Information Disclosure via Error Messages —
src/Renci.SshNet/. SSH implementation details (such as protocol version, supported algorithms) may be exposed through error messages or debug information, potentially aiding attackers in reconnaissance. Fix: Review exception handling and error messages to ensure they don't leak sensitive SSH protocol information. Implement proper logging with security considerations. - Low · Keyboard-Interactive Authentication Implementation Risk —
src/Renci.SshNet/AuthenticationMethod.cs, src/Renci.SshNet/ClientAuthentication.cs. The keyboard-interactive authentication method (mentioned in README) requires careful implementation to prevent prompt injection or man-in-the-middle attacks during the interactive exchange. Fix: Verify keyboard-interactive authentication properly validates server responses. Ensure prompts are not executed as commands. Add security tests for authentication edge cases. - Low · Port Forwarding Implementation Complexity —
src/Renci.SshNet/Channels/ChannelDirectTcpip.cs, ChannelForwardedTcpip.cs. Remote, dynamic and local port forwarding features increase attack surface. Misconfigured forwarding could expose internal services or allow unauthorized network access. Fix: Implement strict validation of port forwarding requests. Add configuration options to restrict forwarding to specific ports/addresses. Document security implications of port forwarding.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.