Host trust
SSHHostKeyPolicy
public enum SSHHostKeyPolicy: Sendable {
case insecureAcceptAnyHostKey
case knownHostsFile(String)
case pinnedFingerprint(SSHHostKeyFingerprint)
case trustStore(any SSHHostTrustStore)
}
SSHHostKeyFingerprint
public struct SSHHostKeyFingerprint: Equatable, Codable, Sendable {
public var rawValue: String
public init(_ rawValue: String) // prepends "SHA256:" if missing
}
SSHHostTrustStore
public protocol SSHHostTrustStore: Sendable {
func fingerprint(host: String, port: UInt16) throws -> SSHHostKeyFingerprint?
func saveFingerprint(_ fingerprint: SSHHostKeyFingerprint, host: String, port: UInt16) throws
func removeFingerprint(host: String, port: UInt16) throws
}
Synchronous — no async or callback variants. Trust store calls happen on the worker queue during host-key verification.
SSHMemoryHostTrustStore
public final class SSHMemoryHostTrustStore: SSHHostTrustStore, @unchecked Sendable {
public init(fingerprints: [String: SSHHostKeyFingerprint] = [:])
}
Keys are "\(host.lowercased()):\(port)" if you want to seed the dictionary directly.
SSHKeychainHostTrustStore
public struct SSHKeychainHostTrustStore: SSHHostTrustStore, @unchecked Sendable {
public static let defaultService = "wiki.qaq.sshkit"
public init(
service: String = SSHKeychainHostTrustStore.defaultService,
accessGroup: String? = nil
)
}
Discovery
Transport-only API for collecting a server’s fingerprint before any authentication. See the Keychain bootstrap flow in the guide for usage.
SSHHostKeyDiscoveryConfiguration
public struct SSHHostKeyDiscoveryConfiguration: Sendable {
public var host: String
public var port: UInt16
public var timeout: TimeInterval
public var logHandler: SSHLogHandler?
public var proxyRoute: SSHProxyRoute?
public var algorithmProfile: SSHAlgorithmProfile
public init(
host: String,
port: UInt16 = 22,
timeout: TimeInterval = 30,
logHandler: SSHLogHandler? = nil,
proxyRoute: SSHProxyRoute? = nil,
algorithmProfile: SSHAlgorithmProfile = .modern
)
}
Carries the transport-level fields needed to reach the server. Notably absent: username and authentication — discovery never authenticates. The init preconditions reject an empty host, a zero port, and a non-positive timeout.
SSHDiscoveredHostKey
public struct SSHDiscoveredHostKey: Equatable, Sendable {
public let host: String
public let port: UInt16
public let fingerprint: SSHHostKeyFingerprint
public init(host: String, port: UInt16, fingerprint: SSHHostKeyFingerprint)
}
SSHClient.discoverHostKey
extension SSHClient {
public static func discoverHostKey(
configuration: SSHHostKeyDiscoveryConfiguration
) async throws -> SSHDiscoveredHostKey
public static func discoverHostKey(
configuration: SSHHostKeyDiscoveryConfiguration,
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<SSHDiscoveredHostKey, SSHKitError>) -> Void
)
}
Connects, reads the SHA-256 host-key fingerprint, and tears the session down. The returned fingerprint is in the canonical SHA256:… form. Task cancellation maps to SSHKitError(.cancelled) through the same socket-shutdown path described in Architecture.