Mental Model
Understand Traversio's public types and how the main SSH concepts map onto the API.
Start Here
Traversio is an SSH client library for these main jobs:
- run remote commands
- stream a long-running command
- hold an interactive PTY-backed shell
- transfer files with SFTP or single-file SCP helpers
- tunnel TCP or Unix-socket traffic through SSH
- route SSH setup through SOCKS5, HTTP CONNECT, or ProxyJump
The core ownership rule is simple:
SSHConnectionowns one authenticated SSH connection- that connection opens channels
- public wrapper types sit on top of those channel families
- when the connection closes, its child wrappers stop being usable
The 30-Second Object Graph
SSHClientConfiguration
|
v
SSHClient.connect(...) / SSHClient.withConnection(...)
|
v
SSHConnection ------------------------------------------------ owns the lifetime
| | | |
| | | +--> forwarding and routing
| | | - openDirectTCPIPChannel(...)
| | | - openDirectStreamLocalChannel(...)
| | | - withLocalPortForwarding(...)
| | | - withDynamicPortForwarding(...)
| | | - withRemotePortForwardListener(...)
| | | - withRemoteStreamLocalForwardListener(...)
| | | - withRemotePortForwarding(...)
| | |
| | +--> openSFTP(...) --> SFTPClient
| |
| +--> openExec(...) / openShell(...) / openSubsystem(...) --> SSHSession
|
+--> execute(...) --> SSHExecResult
|
+--> receiveSCPFile(...) / sendSCPFile(...) and local URL SCP helpersSSHClientConfiguration also describes outer route setup:
connectionProxyfor SOCKS5 or HTTP CONNECT before SSH startsproxyJumpHostsfor one or more SSH jump hosts before the final target- compression, rekey, keepalive, timeout, and legacy-RSA policy
What These Types Mean
| Public type | What it means | Typical use |
|---|---|---|
SSHClientConfiguration | Target endpoint plus auth, host trust, transport, proxy, and timeout policy | Describe how to connect |
SSHConnection | One authenticated SSH connection | Own the lifetime and open work on it |
SSHExecResult | A collected one-shot command result | Run one command and get full output |
SSHSession | A wrapper around one SSH session channel | Stream exec, drive a shell, or use a named subsystem |
SFTPClient | An SFTP subsystem running on one SSH session channel | File operations |
SFTPFileHandle | A public handle for handle-scoped SFTP reads, writes, stats, seek/tell, and streamed chunks | Work with one remote file |
SSHDirectTCPIPChannel | A raw direct-tcpip channel opened by your app | Speak one TCP stream through SSH |
SSHDirectStreamLocalChannel | A raw OpenSSH streamlocal channel opened by your app | Speak to one remote Unix socket path |
SSHForwardedTCPIPChannel | One accepted remote TCP forwarding channel | Handle incoming remote-forward traffic |
SSHForwardedStreamLocalChannel | One accepted remote streamlocal forwarding channel | Handle incoming remote Unix-socket traffic |
SSHRemotePortForwardListener | A closure-scoped remote TCP listener | Accept remote-forwarded TCP channels yourself |
SSHRemoteStreamLocalForwardListener | A closure-scoped remote Unix-socket listener | Accept remote streamlocal channels yourself |
Connection, Session, And Channel
If you know OpenSSH terms, Traversio maps onto them like this:
one TCP or proxied byte-stream route
-> one SSH connection
-> many SSH channels over time
-> "session" channels for shell / exec / subsystem / SFTP
-> "direct-tcpip" channels for client-initiated TCP forwarding
-> "forwarded-tcpip" channels for accepted remote-forward traffic
-> OpenSSH streamlocal channels for Unix socket pathsThese terms describe different layers:
SSHConnectionis the whole authenticated SSH connection.- A channel is one logical conversation running inside that connection.
SSHSessionis the public wrapper for one SSHsessionchannel.SFTPClientis one SFTP subsystem running on onesessionchannel.- Forwarding wrappers expose raw byte streams over SSH channel types.
This rule matters when you combine features:
- one
SSHConnectioncan carry many channels at once - one
sessionchannel can only become one of: shell, exec, subsystem, or SFTP - for many exec commands plus a shell plus SFTP, keep one connection and open separate channels for each job
For a concrete monitoring-oriented example, see Sharing One Connection.
Which API Should You Reach For?
| If you need... | Use this | Why |
|---|---|---|
| one command and one collected result | SSHConnection.execute(_:) | Smallest command path |
| one command with streaming stdout/stderr or stdin writes | SSHConnection.openExec(_:) | Gives you SSHSession.events, write, and sendEOF |
| an interactive terminal | SSHConnection.openShell(...) | Opens a PTY-backed shell as SSHSession |
| to resize that PTY later | SSHSession.resizePseudoTerminal(...) | Sends SSH window-change |
| a named subsystem other than SFTP | SSHConnection.openSubsystem(_:) | Starts one session channel with a subsystem request |
| file reads, writes, metadata, recursive helpers, and resumable helpers | SSHConnection.openSFTP(...) | Starts an SFTP client |
| single-file legacy SCP compatibility | receiveSCPFile(...), sendSCPFile(...), or URL SCP helpers | Runs remote scp through exec |
| one raw TCP stream through SSH | SSHConnection.openDirectTCPIPChannel(...) | Expert forwarding API |
| one raw remote Unix-socket stream through SSH | SSHConnection.openDirectStreamLocalChannel(...) | Expert OpenSSH streamlocal API |
| a temporary local listener that forwards through SSH | SSHConnection.withLocalPortForwarding(...) | Closure-scoped local tunnel |
| a temporary local SOCKS proxy that chooses the target per connection | SSHConnection.withDynamicPortForwarding(...) | Closure-scoped dynamic tunnel |
| a temporary remote TCP listener that you accept from manually | SSHConnection.withRemotePortForwardListener(...) | Yields raw incoming TCP channels |
| a temporary remote Unix-socket listener that you accept from manually | SSHConnection.withRemoteStreamLocalForwardListener(...) | Yields raw incoming streamlocal channels |
| a temporary remote listener bridged back to one local TCP service | SSHConnection.withRemotePortForwarding(...) | Closure-scoped bridge helper |
| measure SSH route latency without logging in to the final target | SSHClient.measurePortLatency(...) | Diagnostic-only timing helper |
Supported Areas
The short version of the current feature set:
- password auth with password-change callbacks
- keyboard-interactive auth
- Ed25519, RSA, and ECDSA public-key auth
- callback-backed public-key signing and SSH agent-backed signing
- OpenSSH private-key loading, OpenSSL-style PEM loading, and key generation
- host-key trust through pinning, app-owned first-seen trust, async callbacks, or OpenSSH
known_hosts - explicit opt-in legacy
ssh-rsa - optional RFC 4253
zlibor delayed OpenSSH[email protected]compression - automatic local rekey policy and post-auth keepalive
- one-shot exec, streamed exec, named subsystems, and PTY shells
- SFTP metadata, listing, file handles, reads, writes, local-file helpers, recursive helpers, resumable helpers, rename, remove, mkdir/rmdir, symlink, and readlink
- single-file SCP receive/send helpers
- raw TCP/IP and streamlocal channels
- local, remote, and dynamic forwarding helpers
- outer connection proxies and explicit ProxyJump hop chains
- structured log handlers, support-report helpers, and SSH-port latency measurement
Known limits:
- application code owns reconnect, session restoration, credential storage, trust persistence, and rollout policy
- hostbased auth, security-key auth, X11 forwarding, and auth-agent forwarding are outside the current release line
- streamlocal forwarding depends on OpenSSH extension support
- broader enterprise proxy auth, broader legacy algorithms, and additional server-family compatibility are not currently covered
For the fuller matrix, read Implemented Features.
Lifetime Rule
Traversio's API is connection-owned.
SSHConnection closes
-> SSHSession becomes invalid
-> SFTPClient becomes invalid
-> raw forwarding channels become invalid
-> forwarding listeners and scopes become invalidThis applies in both ownership styles:
- you call
await connection.close()yourself, or - you used
SSHClient.withConnection(...)and the closure has ended
The usual pattern is:
- open one
SSHConnection - open the child wrapper you need
- do the work
- close the connection, or let
withConnection(...)close it
Long-running operations observe Swift task cancellation. When cancellation wins, callers should expect CancellationError. When transport loss, remote disconnect, host-trust failure, auth rejection, timeout, or protocol failure arrives first, callers should handle the corresponding Traversio error.
Suggested Reading Order
If you are new to Traversio, this order works well:
- this page for the object model
- Quickstart for the first connection
- Diagnostics for logs and support reports
- Running Commands or Interactive Shell for session usage
- SFTP or Forwarding for the bigger feature areas
- Public API for type-by-type reference
- Architecture for implementation layers and package boundaries