Traversio

Public API

The public types, methods, and lifecycle rules for Traversio.

Entry Points

The public surface starts here:

@available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0, *)
public static func connect(
    configuration: SSHClientConfiguration
) async throws -> SSHConnection

@available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0, *)
public static func connect(
    configuration: SSHClientConfiguration,
    logHandler: SSHClientLogHandler
) async throws -> SSHConnection

@available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0, *)
public static func withConnection<Result>(
    configuration: SSHClientConfiguration,
    _ body: @escaping (SSHConnection) async throws -> Result
) async throws -> Result

@available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0, *)
public static func withConnection<Result>(
    configuration: SSHClientConfiguration,
    logHandler: SSHClientLogHandler,
    _ body: @escaping (SSHConnection) async throws -> Result
) async throws -> Result

connect(configuration:) is the core long-lived entry point.

withConnection(configuration:_:) is a convenience wrapper over that same connection setup path. It closes the returned SSHConnection automatically when the body returns or throws.

The logHandler: overloads receive structured connection lifecycle and wrapped operation-failure events. Logging stays disabled until you pass a handler.

Long-running connection, session, and SFTP operations observe task cancellation and typically surface CancellationError. Session transcript collectors and raw channel event iterators also attempt a best-effort channel-close on cancellation. Broader graceful-shutdown semantics remain under active definition.

SSHClient.measurePortLatency(...)

Traversio also exposes a small public latency utility for direct SSH-port timing:

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, visionOS 1.0, *)
public static func measurePortLatency(
    host: String,
    port: UInt16 = 22,
    options: SSHPortLatencyOptions = .init()
) async throws -> SSHPortLatencyReport

This utility opens a direct TCP socket to the target host and port, records TCP connect timing, then waits for the first server byte after connect.

Scope:

  • direct TCP socket to the target endpoint
  • independent from SSHClientConfiguration
  • independent from connectionProxy and proxyJumpHosts
  • no authentication or host-key verification
  • no guarantee about full SSH handshake success

The main public supporting types are:

  • SSHPortLatencyOptions
  • SSHPortLatencyReport
  • SSHPortLatencySample
  • SSHPortLatencyFailure
  • SSHPortLatencyStatistics

Practical example:

import Traversio

@available(macOS 10.15, iOS 13.0, *)
func inspectSSHPort() async throws {
    let report = try await SSHClient.measurePortLatency(
        host: "example.com",
        port: 22,
        options: SSHPortLatencyOptions(
            sampleCount: 5,
            connectTimeout: 2,
            firstServerByteTimeout: 2,
            delayBetweenSamples: 0.1
        )
    )

    print(report.connectRTTStatistics.averageMilliseconds)
    print(report.estimatedPathOneWayFromFirstServerByteStatistics.averageMilliseconds)
}

Keep both summary metrics when possible:

  • connectRTTStatistics.averageMilliseconds tells you how quickly the TCP connection became ready
  • estimatedPathOneWayFromFirstServerByteStatistics.averageMilliseconds is usually the better approximation of "how long until the server answered" on VPN or proxied paths

For a longer guide on when to choose one metric versus the other, see SSH Port Latency.

SSHClientConfiguration

Use SSHClientConfiguration to describe the remote endpoint and auth/trust policy.

FieldTypeMeaning
hostStringRemote host name or address
portUInt16Remote port, default 22
usernameStringSSH username
authenticationSSHAuthenticationMethodAuth method
hostKeyPolicySSHHostKeyPolicyRequired host trust policy
compressionPreferenceSSHCompressionPreferenceOptional transport compression preference: .disabled, RFC 4253 .zlib, or delayed OpenSSH .delayedZlib
legacyAlgorithmOptionsSSHLegacyAlgorithmOptionsOptional explicit legacy ssh-rsa host-key and RSA auth compatibility
automaticRekeyPolicySSHAutomaticRekeyPolicyOptional automatic client-initiated rekey thresholds and idle timer
keepalivePolicySSHKeepalivePolicyOptional post-auth idle keepalive policy, disabled by default
timeoutPolicySSHTimeoutPolicyOptional setup/reply timeout policy, disabled by default
connectionProxySSHConnectionProxy?Optional outermost-hop SOCKS5 or HTTP CONNECT proxy used before SSH starts
proxyJumpHosts[SSHProxyJumpHost]Optional explicit jump-host chain used before the final target

Example:

let configuration = SSHClientConfiguration(
    host: "example.com",
    port: 22,
    username: "deploy",
    authentication: .password("secret"),
    hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
    legacyAlgorithmOptions: .disabled,
    compressionPreference: .delayedZlib,
    automaticRekeyPolicy: .currentProfileDefault,
    keepalivePolicy: .init(interval: 30),
    timeoutPolicy: .init(
        connectionSetupTimeInterval: 15,
        responseTimeInterval: 5
    )
)

By default, compressionPreference is .disabled, legacyAlgorithmOptions is .disabled, automaticRekeyPolicy is .currentProfileDefault, keepalivePolicy is .disabled, and timeoutPolicy is .disabled.

SSHProxyJumpHost uses the same explicit per-hop inputs:

  • host
  • port
  • username
  • authentication
  • hostKeyPolicy
  • compressionPreference
  • legacyAlgorithmOptions
  • automaticRekeyPolicy
  • keepalivePolicy
  • timeoutPolicy

SSHCompressionPreference keeps a focused surface:

  • .disabled
  • .zlib
  • .delayedZlib

.zlib means RFC 4253 zlib: compression starts once encrypted transport is active after each key exchange and stays on for later protected packets until the next rekey resets the context.

.delayedZlib means OpenSSH [email protected]: compression stays off during key exchange and user authentication, then turns on for later protected packets after authentication succeeds.

proxyJumpHosts defines SSH hop chaining. connectionProxy defines the transport proxy used for the first TCP hop.

Current connectionProxy scope:

  • .socks5(SSHSOCKS5ConnectionProxy(...))
  • .httpConnect(SSHHTTPConnectConnectionProxy(...))

If you set both connectionProxy and proxyJumpHosts, the external proxy is used only for the first TCP connection. Later SSH hops still travel inside SSH direct-tcpip channels.

SSHLegacyAlgorithmOptions

Use SSHLegacyAlgorithmOptions when one connection or one jump-host hop must talk to an older SSH server.

Current members:

  • allowsSSHRSA
  • .disabled
  • .sshRSA

.sshRSA enables two behaviors for that configuration:

  • Traversio appends ssh-rsa to the preferred server-host-key list for key exchange
  • RSA public-key authentication can retry with ssh-rsa after the server declines the RSA-SHA2 attempt and still offers publickey

The RSA path still starts with rsa-sha2-512 and rsa-sha2-256. Use .sshRSA only for older endpoints which still require the SHA-1 RSA signature path.

SSHConnectionProxy

Use SSHConnectionProxy when Traversio itself must reach the SSH server through an external proxy before the SSH handshake begins.

Current cases:

  • .socks5(SSHSOCKS5ConnectionProxy)
  • .httpConnect(SSHHTTPConnectConnectionProxy)

SSHSOCKS5ConnectionProxy exposes:

  • host
  • port
  • authentication

SSHSOCKS5ProxyAuthentication exposes:

  • .none
  • .usernamePassword(username:password:)

SSHHTTPConnectConnectionProxy exposes:

  • host
  • port
  • authentication

SSHHTTPConnectProxyAuthentication exposes:

  • .none
  • .basic(username:password:)

These types describe the outermost TCP proxy hop. Later proxyJumpHosts continue through SSH direct-tcpip channels after the first SSH session is established.

SSHAutomaticRekeyPolicy

Use SSHAutomaticRekeyPolicy when you want to keep the default packet thresholds, disable automatic client-initiated rekey, or tighten those thresholds for testing.

Current members:

  • outboundPacketThreshold
  • inboundPacketThreshold
  • idleTimeInterval
  • .currentProfileDefault
  • .disabled

currentProfileDefault currently uses 1_048_576 encrypted packets in each direction.

The outbound threshold is checked before the next protected send. The inbound threshold is checked after authentication, before the next protected receive wait on the connection/session path. The optional idle timer is checked after authentication and can start a local rekey even when the connection is otherwise idle.

disabled turns off the automatic client-initiated rekey path entirely, including the idle timer.

Example:

let configuration = SSHClientConfiguration(
    host: "example.com",
    username: "deploy",
    authentication: .password("secret"),
    hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
    automaticRekeyPolicy: .init(
        outboundPacketThreshold: 50_000,
        inboundPacketThreshold: 50_000,
        idleTimeInterval: 600
    )
)

SSHKeepalivePolicy

Use SSHKeepalivePolicy when you want Traversio to send a keepalive on an otherwise idle authenticated connection.

Current members:

  • interval
  • .disabled

interval is a post-auth idle threshold in seconds.

Current behavior:

  • keepalive is checked only after authentication succeeds
  • it is meant for long-lived idle connections, shells, and forwarding sessions
  • it is not a blanket timeout for long-running output collection or already-open streams

If SSHTimeoutPolicy.responseTimeInterval is also set, keepalive reply waits use the tighter of that reply timeout and the keepalive interval itself.

Example:

let configuration = SSHClientConfiguration(
    host: "example.com",
    username: "deploy",
    authentication: .password("secret"),
    hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
    keepalivePolicy: .init(interval: 30)
)

SSHTimeoutPolicy

Use SSHTimeoutPolicy when the caller wants Traversio to stop waiting indefinitely on connection setup or reply-style protocol waits.

Current members:

  • connectionSetupTimeInterval
  • responseTimeInterval
  • .disabled

connectionSetupTimeInterval covers one connect attempt, including identification exchange, key exchange, host-key trust, and user authentication.

responseTimeInterval covers waits where the server is expected to answer promptly:

  • channel-open replies
  • channel-request replies such as exec, pty-req, shell, and subsystem
  • keepalive replies
  • global-request replies such as tcpip-forward
  • SFTP responses

It does not act as a blanket timeout for long-running shell/exec output collection, event streams, or remote-forward accept loops.

Example:

let configuration = SSHClientConfiguration(
    host: "example.com",
    username: "deploy",
    authentication: .password("secret"),
    hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
    timeoutPolicy: .init(
        connectionSetupTimeInterval: 15,
        responseTimeInterval: 5
    )
)

SSHClientLogHandler

Use SSHClientLogHandler with the logHandler: overloads on SSHClient.connect(...) and SSHClient.withConnection(...) when the application wants structured library log events.

Current members:

  • .disabled
  • init(minimumLevel:emitter:)
  • .sink(minimumLevel:_:)
  • .recorder(_:minimumLevel:)
  • .osLog(_ logger: Logger, minimumLevel:)
  • .osLog(subsystem:category:minimumLevel:)

SSHClientLogLevel exposes:

  • .debug
  • .info
  • .notice
  • .warning
  • .error

SSHClientLogCategory exposes:

  • .connection
  • .authentication
  • .session
  • .sftp
  • .forwarding
  • .transport

SSHClientLogEvent exposes:

  • timestamp
  • level
  • category
  • message
  • metadata

Traversio currently emits structured events for connection start/success/failure, authentication success/rejection, and wrapped operation failures on the stable public error paths.

SSHClientLogRecorder

Use SSHClientLogRecorder when the application wants a bounded in-memory buffer of recent SSHClientLogEvent values for support export or an in-app diagnostics screen.

Current members:

  • init(maximumEventCount:)
  • record(_:)
  • clear()
  • snapshot()
  • diagnosticReport(for:)
  • logHandler(minimumLevel:)

SSHClientLogRecorderSnapshot exposes:

  • events
  • maximumEventCount
  • droppedEventCount
  • formattedLines
  • formattedText
  • diagnosticReport(for:)

When the recorder reaches its capacity it keeps the newest events, drops older ones, and records how many were discarded in droppedEventCount.

SSHAuthenticationMethod

Current cases:

  • .password(String)
  • .keyboardInteractive(submethods:responseProvider:)
  • .ed25519PrivateKey(rawRepresentation: [UInt8])
  • .rsaPrivateKey(pkcs1DERRepresentation: [UInt8])
  • .ecdsaP256PrivateKey(rawRepresentation: [UInt8])
  • .ecdsaP384PrivateKey(rawRepresentation: [UInt8])
  • .ecdsaP521PrivateKey(rawRepresentation: [UInt8])

Current factory helpers:

  • try SSHAuthenticationMethod.ed25519PrivateKey(openSSHPrivateKey: String, passphrase: String? = nil)
  • try SSHAuthenticationMethod.ed25519PrivateKey(contentsOfOpenSSHPrivateKeyFile: String, passphrase: String? = nil)
  • try SSHAuthenticationMethod.rsaPrivateKey(openSSHPrivateKey: String, passphrase: String? = nil)
  • try SSHAuthenticationMethod.rsaPrivateKey(contentsOfOpenSSHPrivateKeyFile: String, passphrase: String? = nil)
  • try SSHAuthenticationMethod.ecdsaPrivateKey(openSSHPrivateKey: String, passphrase: String? = nil)
  • try SSHAuthenticationMethod.ecdsaPrivateKey(contentsOfOpenSSHPrivateKeyFile: String, passphrase: String? = nil)
  • try SSHOpenSSHKeyPair.generate(algorithm: SSHOpenSSHKeyPair.Algorithm, comment: String = "traversio", encryption: SSHOpenSSHPrivateKeyEncryption? = nil)

The Ed25519 raw case expects a 32-byte private-key seed. The RSA raw case expects PKCS#1 DER private-key bytes. The ECDSA raw cases expect the corresponding curve-specific raw private-key representation. The throwing helpers load OpenSSH openssh-key-v1 Ed25519, RSA, or ECDSA private keys and return the matching enum case. SSHOpenSSHKeyPair.generate(...) returns the generated authenticationMethod, the OpenSSH private-key PEM text, and the matching authorized-key line. Pass passphrase: or SSHOpenSSHPrivateKeyEncryption(...) when the private key should use the OpenSSH bcrypt KDF and a supported AES cipher such as aes256-ctr or aes256-cbc.

SSHOpenSSHKeyPair.Algorithm exposes:

  • .ed25519
  • .ecdsaP256
  • .ecdsaP384
  • .ecdsaP521
  • .rsa(bitCount: Int)

SSHOpenSSHPrivateKeyEncryption.Cipher exposes:

  • .aes128CTR
  • .aes192CTR
  • .aes256CTR
  • .aes128CBC
  • .aes192CBC
  • .aes256CBC

SSHOpenSSHKeyPair exposes:

  • algorithm
  • comment
  • authenticationMethod
  • privateKeyPEM
  • authorizedKeyLine

For the full generation workflow, including saving the generated PEM and installing the matching public line, see Key Generation.

keyboardInteractive gives you a generic async challenge-response hook. Traversio passes an SSHKeyboardInteractiveChallenge containing the server's name, instruction, languageTag, and ordered prompts. Your closure must return one UTF-8 response per prompt.

SSHKeyboardInteractiveChallenge exposes:

  • username
  • serviceName
  • name
  • instruction
  • languageTag
  • prompts

Each SSHKeyboardInteractivePrompt exposes:

  • prompt
  • shouldEcho

If your callback returns the wrong number of responses, Traversio throws SSHAuthenticationMethodError.invalidKeyboardInteractiveResponseCount(expected:received:).

SSHHostKeyPolicy

Current factory members:

  • .acceptAnyVerifiedHostKey
  • .requireMatch(SSHTrustedHostKey)
  • .requireMatchAny([SSHTrustedHostKey])
  • .trustOnFirstUse(lookup:store:)
  • .trustOnFirstUse(lookup:store:onStoredHostKeyMismatch:)
  • .trustOnFirstUse(using: SSHHostKeyTrustStore)
  • .knownHostsFile(String)
  • .knownHostsFile(String, additionalLookupNames: [String])
  • .callback((SSHHostKeyValidationRequest) async throws -> SSHHostKeyTrustMethod)

.trustOnFirstUse(lookup:store:) is the simplest first-seen trust path. Traversio gives your app a lookup closure plus an SSHHostKeyStoreRequest on writes. That store request includes expectedStoredHostKey, so a shared trust store can do compare-and-set instead of blindly overwriting concurrent updates.

The overload with onStoredHostKeyMismatch keeps the same store boundary but lets the app decide whether a changed stored key should be rejected or replaced. That callback receives an SSHHostKeyChangeRequest with both the stored and newly received host key, and it returns an SSHHostKeyChangeDecision.

.trustOnFirstUse(using:) wraps that same first-seen flow behind an app-owned SSHHostKeyTrustStore. The default protocol implementation rejects changed stored keys, and a store can opt into planned rotation by implementing decisionForChangedHostKey(_:). The write hook still receives the same compare-and-set SSHHostKeyStoreRequest.

.knownHostsFile(...) currently resolves exact, wildcard, negated, hashed, CIDR, @revoked, and @cert-authority OpenSSH known_hosts entries, including non-default-port forms like [host]:port. The overload with additionalLookupNames lets you include extra host or IP strings in the lookup, so a connection opened as example.com can still match entries stored under a resolved address like 192.0.2.10.

.callback(...) runs after the server proves possession of its host key and before the SSH session is activated. That gives the upper layer one explicit place to consult or update its own trust store without requiring Traversio to own persistence.

SSHTrustedHostKey exposes:

  • algorithmName
  • rawRepresentation
  • fingerprintSHA256

SSHHostKeyTrustMethod tells you which trust path was used:

  • acceptAnyVerifiedHostKey
  • exactMatch
  • trustedSetMatch
  • certificateAuthorityMatch
  • callback

SSHHostKeyValidationRequest exposes:

  • endpointHost
  • endpointPort
  • remoteIdentification
  • trustedHostKey
  • matches(_:)

SSHHostKeyChangeRequest exposes:

  • endpointHost
  • endpointPort
  • remoteIdentification
  • storedHostKey
  • receivedHostKey

SSHHostKeyStoreRequest exposes:

  • endpointHost
  • endpointPort
  • remoteIdentification
  • expectedStoredHostKey
  • trustedHostKey
  • matchesExpectedStoredHostKey(_:)

SSHHostKeyChangeDecision exposes:

  • reject
  • replaceStoredHostKey

SSHHostKeyTrustStore exposes:

  • lookupHostKey(endpointHost:endpointPort:)
  • storeHostKey(_:)
  • decisionForChangedHostKey(_:)

SSHConnection

SSHConnection is the public connection wrapper returned by connect(configuration:) and passed into withConnection(...).

Public members:

MemberTypeMeaning
metadataSSHConnectionMetadataHandshake and trust details for the active connection
close()asyncClose the SSH connection explicitly and invalidate every wrapper created from it
execute(_:environment:)async throws -> SSHExecResultExecute a one-shot remote command, optionally with SSH env requests sent before exec
openExec(_:environment:)async throws -> SSHSessionOpen a streamed exec session on a session channel, optionally with SSH env requests
openShell(pseudoTerminalRequest:environment:)async throws -> SSHSessionOpen a PTY-backed shell session, optionally with SSH env requests sent before shell startup
openSFTP(clientVersion:)async throws -> SFTPClientStart an SFTP client on a subsystem channel
openDirectTCPIPChannel(targetHost:targetPort:originatorAddress:originatorPort:)async throws -> SSHDirectTCPIPChannelOpen a raw direct-tcpip forwarding channel
withLocalPortForwarding(targetHost:targetPort:localHost:localPort:_:)async throws -> ResultStart a closure-scoped local listener on Apple 26+
withDynamicPortForwarding(localHost:localPort:socks5Authentication:_:)async throws -> ResultStart a closure-scoped local SOCKS proxy on Apple 26+
withRemotePortForwardListener(remoteHost:remotePort:_:)async throws -> ResultStart a closure-scoped remote listener and accept raw incoming forwarded-tcpip channels
withRemotePortForwarding(localPort:remoteHost:remotePort:localHost:_:)async throws -> ResultStart a closure-scoped remote listener on Apple 26+ and bridge it back to one local TCP endpoint

SSHConnectionMetadata

Metadata fields:

  • endpointHost
  • endpointPort
  • username
  • clientIdentification
  • remoteIdentification
  • preIdentificationLines
  • hostKeyAlgorithm
  • hostKeyFingerprintSHA256
  • hostKeyTrustMethod

This is useful when you want to log what server answered, which key was accepted, and which trust path allowed the handshake to continue.

SSHExecResult

SSHExecResult contains:

  • standardOutput
  • standardError
  • exitStatus
  • exitSignal
  • didReceiveEOF

It is a value type designed for one-shot command execution. execute(_:) remains the collected-result convenience API, while openExec(_:) exposes the same underlying session channel as a streamed SSHSession.

SSHSessionEnvironmentVariable

Use SSHSessionEnvironmentVariable when you want execute(...), openExec(...), or openShell(...) to send one or more RFC 4254 env requests before the remote process or shell starts.

Fields:

  • name
  • value

Each value becomes one SSH env request. Traversio currently waits for a reply to every environment request, so a server-side rejection fails the session startup instead of being ignored silently.

SSHSession

SSHSession is the public wrapper for an open session channel used by streamed exec and PTY-backed shell flows.

Public methods:

MethodMeaning
write(_ bytes: [UInt8])Send raw bytes to the session
write(_ string: String)Send UTF-8 text
sendEOF()Close the sending side
close()Send channel close explicitly
resizePseudoTerminal(characterWidth:characterHeight:pixelWidth:pixelHeight:)Send an SSH window-change request on the current session channel
sendSignal(_:)Send an SSH signal request on the current session channel without waiting for a reply
nextEvent()Read the next shell event, or nil after channel close
eventsConsume shell events as an AsyncSequence
readStandardOutputChunk()Read the next stdout chunk, or nil on close
collectOutputUntilClose()Gather stdout, stderr, exit status, exit signal, and EOF state until the session closes

Supporting value type:

  • SSHSessionOutput mirrors the transcript fields of SSHExecResult
  • SSHSessionEvent exposes .standardOutput, .standardError, .exitStatus, .exitSignal, and .endOfFile
  • resizePseudoTerminal(...) is mainly useful for PTY-backed shells opened through openShell(...); plain exec sessions currently do not allocate a PTY automatically
  • sendSignal(_:) sends RFC 4254 signal requests such as TERM or INT; the server may ignore them, and there is no reply message for this request
  • remote exit-signal requests now surface through SSHSessionEvent.exitSignal(...) and the exitSignal field on collected outputs
  • cancelling collectOutputUntilClose() now triggers a best-effort channel-close before the task surfaces CancellationError
  • cancelling for try await event in session.events now also triggers that same best-effort channel-close

SSHSessionSignal

Use SSHSessionSignal when you need to send a POSIX-style signal request on a shell or streamed exec session.

Common members include:

  • .interrupt
  • .terminate
  • .kill
  • .quit
  • .hangup
  • .abort
  • .alarm
  • .floatingPointException
  • .illegalInstruction
  • .brokenPipe
  • .segmentationViolation
  • .user1
  • .user2

For custom names, use SSHSessionSignal(rawValue: ...).

Traversio sends the raw value directly as the RFC 4254 signal name, so standard signals should use the protocol form without the SIG prefix.

SSHSessionExitSignal

SSHSessionExitSignal describes a remote exit-signal notification reported by the peer.

Fields:

  • signal
  • didCoreDump
  • errorMessage
  • languageTag

The errorMessage and languageTag fields are optional because many peers send them as empty strings.

SSHDirectTCPIPChannel

SSHDirectTCPIPChannel is the current expert forwarding wrapper for a raw direct-tcpip channel.

Public methods:

MethodMeaning
write(_ bytes: [UInt8])Send raw bytes to the forwarded target
write(_ string: String)Send UTF-8 text
sendEOF()Close the sending side
close()Send channel close explicitly
nextEvent()Read the next forwarding event, or nil after channel close
eventsConsume forwarding events as an AsyncSequence
readChunk()Read the next data chunk, or nil on close
collectDataUntilClose()Gather all bytes plus EOF state until the channel closes

Supporting value type:

  • SSHDirectTCPIPChannelOutput contains data and didReceiveEOF
  • SSHTCPIPChannelEvent exposes .data and .endOfFile
  • if you want one consistent incremental view, prefer nextEvent() / events instead of mixing them with readChunk() on the same channel
  • cancelling for try await event in channel.events now attempts a best-effort channel-close before surfacing CancellationError

SSHLocalPortForward

SSHLocalPortForward describes the currently bound local forwarding listener inside withLocalPortForwarding(...).

Fields:

  • localHost
  • localPort
  • targetHost
  • targetPort

Current usage note:

  • withLocalPortForwarding(...) is only available on Apple 26+ because it currently uses NetworkListener
  • the helper is intentionally closure-scoped, just like withConnection
  • the helper currently uses a best-effort shutdown contract: after scope exit Traversio stops bridging data and late accepted local connections are closed as soon as possible
  • Traversio does not currently promise that the local port becomes immediately unconnectable at the exact moment the scope returns

SSHDynamicPortForward

SSHDynamicPortForward describes the currently bound local SOCKS listener inside withDynamicPortForwarding(...).

Fields:

  • localHost
  • localPort

Supporting current auth type:

  • SSHSOCKS5ProxyAuthentication.none
  • SSHSOCKS5ProxyAuthentication.usernamePassword(username:password:)

Current usage note:

  • withDynamicPortForwarding(...) is only available on Apple 26+ because it currently uses NetworkListener
  • the helper is intentionally closure-scoped, just like withConnection
  • the local listener speaks SOCKS5 in no-auth or username/password mode, and it still accepts SOCKS4 / SOCKS4a only when SOCKS5 auth is not configured
  • shutdown is currently best-effort rather than an instant listener invalidation guarantee

SSHRemotePortForward

SSHRemotePortForward describes the currently active remote listener inside withRemotePortForwarding(...).

Fields:

  • localHost
  • localPort
  • remoteHost
  • remotePort

Current usage note:

  • withRemotePortForwarding(...) is only available on Apple 26+ because the current helper bridges back to a local TCP endpoint with the same Apple-first transport path
  • the helper is intentionally closure-scoped, just like withConnection and withLocalPortForwarding(...)
  • if you request remotePort: 0, Traversio reports the allocated remote port through SSHRemotePortForward.remotePort
  • the helper now builds on the lower-level remote listener API, but it still bridges to one fixed local TCP endpoint
  • accepted remote bridge failures stay scoped to the offending connection instead of poisoning later remote clients in the same forwarding scope
  • multiple accepted remote connections can stay bridged at the same time while the forwarding scope remains open

SSHRemotePortForwardListener

SSHRemotePortForwardListener describes the currently active remote listener inside withRemotePortForwardListener(...).

Fields:

  • remoteHost
  • remotePort

Public methods:

MethodMeaning
accept()Wait for the next incoming forwarded-tcpip channel

Current usage note:

  • withRemotePortForwardListener(...) is closure-scoped, just like the other public connection helpers
  • if you request remotePort: 0, Traversio reports the allocated remote port through SSHRemotePortForwardListener.remotePort
  • pending accepts are canceled before Traversio sends cancel-tcpip-forward on scope exit
  • the listener lifecycle and concurrency contract should still be treated as evolving even though the current OpenSSH data path is now live-validated

SSHForwardedTCPIPChannel

SSHForwardedTCPIPChannel is the current expert wrapper for one incoming remote-forward connection accepted through SSHRemotePortForwardListener.accept().

Fields:

  • listeningHost
  • listeningPort
  • originatorHost
  • originatorPort

Public methods:

MethodMeaning
write(_ bytes: [UInt8])Send raw bytes back through the accepted forwarded channel
write(_ string: String)Send UTF-8 text
sendEOF()Close the sending side
close()Send channel close explicitly
nextEvent()Read the next forwarding event, or nil after channel close
eventsConsume forwarding events as an AsyncSequence
readChunk()Read the next data chunk, or nil on close
collectDataUntilClose()Gather all bytes plus EOF state until the channel closes

Supporting value type:

  • SSHForwardedTCPIPChannelOutput contains data and didReceiveEOF
  • SSHTCPIPChannelEvent exposes .data and .endOfFile
  • if you want one consistent incremental view, prefer nextEvent() / events instead of mixing them with readChunk() on the same channel
  • cancelling for try await event in channel.events now attempts a best-effort channel-close before surfacing CancellationError

SSHPseudoTerminalRequest

Use this type when opening a shell with custom PTY settings.

Fields:

  • terminalType
  • characterWidth
  • characterHeight
  • pixelWidth
  • pixelHeight
  • encodedTerminalModes

Convenience:

  • SSHPseudoTerminalRequest.default

SFTPClient

SFTPClient is the public file-transfer wrapper.

Public methods:

MethodMeaning
close()Close the SFTP subsystem channel explicitly
currentVersionExchange()Return SSHSFTPVersionExchange
realPath(_:)Resolve a remote path
lstat(_:)Query symlink-aware metadata
stat(_:)Query metadata
setAttributes(_:,attributes:)Update path metadata with SSH_FXP_SETSTAT
fileSystemAttributes(_:)Query filesystem-level capacity and flags through OpenSSH [email protected]
openFile(_:,flags:,attributes:)Open a public SFTPFileHandle for handle-scoped reads, writes, metadata, and explicit close
listDirectory(_:)Read directory entries
readFile(_:,chunkSize:maxConcurrentReads:progress:)Read a whole file, optionally using bounded concurrent read requests on one handle and reporting cumulative progress
resumeDownloadFile(_:,existingData:,chunkSize:maxConcurrentReads:progress:)Resume a whole-file download from a caller-provided local prefix when the remote file is larger, then return SSHSFTPResumeDownloadResult
writeFile(_:,data:,chunkSize:maxConcurrentWrites:syncAfterWrite:progress:)Write a whole file, optionally keeping a bounded number of SSH_FXP_WRITE requests in flight before close and reporting cumulative progress
resumeUploadFile(_:,data:,chunkSize:maxConcurrentWrites:syncAfterWrite:progress:)Resume a whole-file upload from the current remote file size when the server reports one, then return SSHSFTPResumeUploadResult
makeDirectory(_:,attributes:)Create a directory
removeFile(_:)Remove a file
removeDirectory(_:)Remove a directory
rename(_:,to:)Rename a path
readLink(_:)Read a symlink target
createSymbolicLink(targetPath:linkPath:)Create a symlink

SFTPFileHandle

SFTPFileHandle is the public low-level file-handle wrapper returned by SFTPClient.openFile(...).

Public methods:

MethodMeaning
read(at:length:)Read up to length bytes from a specific offset, returning nil on EOF
readAll(chunkSize:maxConcurrentReads:progress:)Read a whole file from the current handle using bounded concurrent read requests when desired, with cumulative progress reporting
readChunks(startingAt:chunkSize:)Return an AsyncSequence of SSHSFTPFileChunk values for caller-controlled streamed reads
write(_:at:)Write bytes at a specific offset
write(contentsOf:startingAt:progress:)Consume an AsyncSequence<[UInt8]> and write the yielded chunks sequentially from one starting offset
stat()Query handle-scoped metadata through SSH_FXP_FSTAT
setAttributes(_:)Update handle-scoped metadata through SSH_FXP_FSETSTAT
fileSystemAttributes()Query handle-scoped filesystem capacity and flags through OpenSSH [email protected]
synchronize()Request OpenSSH [email protected] for the open handle
close()Close the remote file handle explicitly

Supporting public SFTP value types:

  • SFTPFileHandle
  • SSHSFTPFileChunk
  • SSHSFTPFileChunkSequence
  • SSHSFTPVersionExchange
  • SSHSFTPExtension
  • SSHSFTPNameEntry
  • SSHSFTPOpenFileFlags
  • SSHSFTPResumeDownloadResult
  • SSHSFTPResumeUploadResult
  • SSHSFTPResumeError
  • SSHSFTPTransferProgress
  • SSHSFTPFileAttributes
  • SSHSFTPFileSystemAttributes
  • SSHSFTPFileSystemFlags

SSHSFTPResumeDownloadResult, SSHSFTPResumeUploadResult, and SSHSFTPResumeError

resumeDownloadFile(...) returns SSHSFTPResumeDownloadResult.

Current members:

  • path
  • startingOffset
  • bytesDownloaded
  • totalBytes
  • data
  • didResume
  • finalOffset

resumeUploadFile(...) returns SSHSFTPResumeUploadResult.

Current members:

  • path
  • startingOffset
  • bytesUploaded
  • totalBytes
  • didResume
  • finalOffset

SSHSFTPResumeError currently exposes:

  • .remoteFileSizeUnavailable(path:)
  • .remoteFileIsSmallerThanLocalData(path:remoteSize:localSize:)
  • .remoteFileIsLargerThanLocalData(path:remoteSize:localSize:)

SSHSFTPTransferProgress

SSHSFTPTransferProgress is the public progress payload used by the whole-file SFTP convenience APIs.

Current members:

  • operation
  • bytesTransferred
  • totalBytes
  • fractionCompleted

Current behavior:

  • reads report cumulative bytesTransferred
  • resumeDownloadFile(...) reports cumulative read progress against the full remote length
  • writes report cumulative bytesTransferred plus totalBytes
  • fractionCompleted is available when totalBytes is known

Error Surface

The stable public error type today is SSHClientError.

Current cases:

  • authenticationRejected(methodName:availableMethods:partialSuccess:)
  • connectionFailed(SSHConnectionFailure)
  • operationFailed(SSHOperationFailure)
  • passwordChangeRequired(prompt:)
  • connectionScopeEnded

Supporting public connection-diagnostics types:

  • SSHConnectionFailure
  • SSHConnectionFailureStage
  • SSHConnectionFailureCode
  • SSHConnectionFailureCallbackSource
  • SSHConnectionFailureCallbackDetails
  • SSHConnectionFailureDiagnostics
  • SSHNegotiatedTransportAlgorithms
  • SSHRemoteDisconnect
  • SSHRemoteDebugMessage
  • SSHOperationFailure
  • SSHOperationFailureScope
  • SSHOperationFailureCode
  • SSHOperationFailureDiagnostics
  • SSHSFTPStatusDetails
  • SSHClientLogRecorder
  • SSHClientLogRecorderSnapshot

connectionFailed(...) is used for connection-setup failures and keeps stage plus diagnostic context instead of exposing lower-level error enums directly.

SSHConnectionFailure now also has diagnosticReport, a copy-ready multi-line summary for support/debug flows.

operationFailed(...) is the first stable wrapper for post-auth library operations such as session reads/writes, direct TCP/IP channel setup, forwarded channel I/O, remote-forward listener accept, and SFTP requests. It keeps:

  • the operation scope
  • a stable failure code
  • connection/channel diagnostics such as negotiated algorithms, whether the server sent SSH_MSG_EXT_INFO, the server extension names seen so far, remote disconnect/debug context, local/remote channel IDs when known, and SFTP status details when the server returned them

SSHOperationFailure now also has diagnosticReport for the same copy/paste workflow.

SSHNegotiatedTransportAlgorithms now also reports effective integrity per direction. For CTR + HMAC or UMAC transports that matches the negotiated MAC name; for OpenSSH AES-GCM and [email protected] it reports implicit while still keeping the raw negotiated MAC fields available.

SSHClientLogEvent also now has formattedLine. Applications can still build their own sink-based pipeline around that, and SSHClientLogRecorder now provides the built-in bounded recent-event buffer when they want a ready-to-use copy/export path.

For a concrete developer-facing integration pattern, including a recent-event recorder and copyable support payload export, see Diagnostics.

When the host-key policy callback or keyboard-interactive response callback throws, SSHConnectionFailureDiagnostics.callbackFailure describes the callback source and error type without exposing lower-level callback plumbing.

Some errors can still escape unchanged when they come from your own callbacks or trust-policy code. The library does not hide those behind SSHClientError.

Practical Rule

When you evaluate Traversio, think of this public API as:

  • real enough to build against
  • intentionally narrow
  • explicit about connection ownership and wrapper lifetime
  • still evolving in the areas of exec/cancellation semantics, host trust breadth, and forwarding lifecycle details

On this page