SSHKit
Swift package for libssh on Apple platforms. Async/await, callback, and Objective-C APIs over a single libssh-per-connection worker.
Capabilities
- Authentication: password, private-key file, keyboard-interactive, SSH agent, generated keys.
- Host trust: known-hosts files, pinned fingerprints, memory and Keychain trust stores, explicit insecure mode, and a transport-only host-key discovery entry point for first-time bootstrap.
- Commands: collected output with exit status, streamed channels with stdout/stderr/close events, PTY shells with resize.
- SFTP: directory listing, stat/lstat, attributes, mkdir/rmdir, rename, symlinks, chunked read/write, upload, download, resumable transfers with progress.
- Forwarding: direct TCP, local, remote, dynamic SOCKS, SOCKS5 routes, HTTP CONNECT routes, ProxyJump.
- SCP: single-file upload and download helpers built on the streamed channel.
- Diagnostics: structured log events, bounded log recorder, redacted diagnostic reports, algorithm inspection, latency probe.
Install
Add the package to Package.swift:
dependencies: [
.package(url: "https://github.com/Lakr233/SSHKit.git", from: "0.1.5"),
],
targets: [
.target(name: "App", dependencies: [
.product(name: "SSHKit", package: "SSHKit"),
]),
]
For Xcode projects, add the same URL through File → Add Package Dependencies… and link the SSHKit product. SSHKit, SSHKitObjC, and CLibSSH ship as dynamic libraries to keep the libssh LGPL boundary explicit.
Platforms
| Platform | Floor |
|---|---|
| iOS | 13.0 |
| macOS | 10.15 |
| Mac Catalyst | 13.0 |
| tvOS | 13.0 |
| visionOS | 1.0 |
watchOS is outside the supported set.
Quick start
import SSHKit
let configuration = SSHClient.Configuration(
host: "example.com",
username: "deploy",
authentication: .password("secret"),
hostKeyPolicy: .knownHostsFile(
("~/.ssh/known_hosts" as NSString).expandingTildeInPath
)
)
let output = try await SSHClient.withConnection(configuration) { connection in
try await connection.execute("uname -a")
}
print(String(data: output.standardOutput, encoding: .utf8) ?? "")
print("exit:", output.exitStatus)
withConnection opens a session, runs the body, and closes the connection on both success and failure. Non-zero exit status is part of SSHCommandResult, not a thrown error — inspect output.exitStatus after the call. For long-lived trust, use SSHClient.discoverHostKey to capture the server’s fingerprint, save it to a Keychain trust store, then switch hostKeyPolicy to .trustStore(...). The host-trust guide walks through the four steps.
Ownership rules at a glance
- One TCP socket owns one libssh
ssh_session. - One
SSHConnectionruns at most one active high-level job — collected command, streamed command, PTY shell, SFTP, or tunnel. - Concurrent work uses multiple
SSHConnectioninstances. - Task cancellation tears down the connection through
shutdown(fd, SHUT_RDWR).
The full model is in Architecture; the type catalog is in API.
License
SSHKit project code is MIT-licensed. The vendored libssh source under Vendor/libssh remains under its upstream GNU LGPL terms (see Vendor/libssh/COPYING in the repository). Linking SSHKit dynamically preserves the LGPL boundary.