Connection Configuration
How to choose endpoint, trust, transport, and routing settings for an SSH connection.
SSHClientConfiguration is the complete description of one SSH connection attempt. It answers four practical questions:
- where to connect
- how to authenticate
- how to trust the server
- how the transport should behave once the session is up
If you use jump hosts, SSHProxyJumpHost repeats the same per-hop transport knobs on each SSH hop. The root SSHClientConfiguration continues to describe the final target and any outer connectionProxy used before the first SSH handshake starts.
Defaults At A Glance
| Field | Default | What it controls |
|---|---|---|
host | required | Final target host name or address |
port | 22 | Final target port |
username | required | SSH username |
authentication | required | User authentication method |
hostKeyPolicy | required | Host trust policy |
compressionPreference | .disabled | Whether to advertise transport compression |
legacyAlgorithmOptions | .disabled | Optional legacy host-key and RSA auth compatibility controls |
automaticRekeyPolicy | .currentProfileDefault | Automatic client-initiated rekey thresholds |
keepalivePolicy | background keepalive, 15s interval | Post-auth background liveness keepalive; overridable or .disabled |
timeoutPolicy | .currentProfileDefault | Connection-setup, host-key trust, and prompt-reply timeout behavior |
connectionProxy | nil | Optional outer SOCKS5 or HTTP CONNECT hop before SSH starts |
proxyJumpHosts | [] | Optional SSH jump-host chain before the final target |
The matching SSHProxyJumpHost fields are:
hostportusernameauthenticationhostKeyPolicycompressionPreferencelegacyAlgorithmOptionsautomaticRekeyPolicykeepalivePolicytimeoutPolicy
Endpoint, Auth, And Trust
These are the required inputs:
hostusernameauthenticationhostKeyPolicy
Typical choices:
- use a stable DNS name when you rely on
known_hostshostnames or certificate principals - keep
hostKeyPolicystrict in production;.acceptAnyVerifiedHostKeyis only appropriate for disposable test targets - choose the simplest auth method your deployment already manages well: password for controlled lab/testing, public-key auth for normal production automation
Transport Settings
compressionPreference
Default: .disabled
Supported values:
.disabled.zlib.delayedZlib
.zlib means RFC 4253 zlib: compression starts as soon as the encrypted transport is active after each key exchange.
.delayedZlib means OpenSSH [email protected]: compression stays off during handshake and user authentication, then turns on only after authentication succeeds.
Use it when:
- bandwidth is scarce
- you expect larger shell, exec, or SFTP payloads
- the target server is already known to support either RFC 4253
zlibor the OpenSSH delayed-zlib path
Leave it off when:
- connections are short-lived
- payloads are small
- CPU cost matters more than wire size
automaticRekeyPolicy
Default: .currentProfileDefault
currentProfileDefault currently means:
outboundPacketThreshold: 1_048_576inboundPacketThreshold: 1_048_576idleTimeInterval: nil
Use a custom policy when:
- you want tighter packet budgets on long-lived connections
- you need an idle timer which triggers a client rekey even when the connection is quiet
- you are reproducing or validating server behavior under more aggressive rekey conditions
Use .disabled only when the app should turn off client-initiated automatic rekey.
legacyAlgorithmOptions
Default: .disabled
Supported values:
.disabled.sshRSA
SSHLegacyAlgorithmOptions.sshRSA enables Traversio's explicit legacy RSA compatibility path for one connection or one jump-host hop.
When it is enabled:
- the connection appends
ssh-rsato the advertised server-host-key preference list - built-in RSA private-key authentication still prefers
rsa-sha2-512andrsa-sha2-256 - if the server declines the built-in SHA-2 attempt and still offers
publickey, Traversio retries withssh-rsa - callback-backed and agent-backed public-key auth may select
ssh-rsa
When it is disabled, Traversio removes ssh-rsa from callback-backed and agent-backed candidate lists before selecting a public-key signature algorithm.
This option is about the SSH algorithm named ssh-rsa, not the private-key file format. To accept user or migrated key files from OpenSSH and OpenSSL-style PEM containers, build authentication with SSHAuthenticationMethod.privateKeyPEM(...).
Use it when:
- you target older SSH appliances, routers, NAS devices, or embedded systems
- the server only advertises
ssh-rsahost keys - the server accepts RSA keys only through the legacy SHA-1 signature path
Keep it disabled for current OpenSSH-family servers and other modern deployments.
keepalivePolicy
Default: a conservative background keepalive, enabled by default with a 15-second interval.
SSHKeepalivePolicy(interval: ...) runs a background keepalive after authentication succeeds. By default it is on, so an otherwise idle authenticated connection is probed for liveness even when your own code is only waiting on a long-running operation. The reply timeout is derived from the interval, and if the peer misses that reply the connection is torn down and all in-flight operations fail promptly instead of hanging against a silent or half-dead peer.
This makes the default keepalive an out-of-band liveness probe, not just a NAT keep-open. It is fully overridable: pass a different interval, or pass .disabled to turn it off entirely.
Override the default interval when:
- the connection stays open for a long time and you want a tighter or looser probe cadence
- shells or forwarding sessions may sit idle between bursts
- NAT or firewall devices may silently drop an otherwise idle TCP flow
Set .disabled when:
- the connection is short-lived
- you mostly run one command or one SFTP transfer and then disconnect
- extra background traffic is not desirable and you accept that a silent peer will not be detected out of band
Behavior:
- the background keepalive applies to authenticated connections and does not depend on you setting a response timeout
timeoutPolicyremains the dedicated timeout control for setup and reply paths- long-running shell and exec reads keep their own streaming behavior
If you also set timeoutPolicy.responseTimeInterval, keepalive reply waits use the tighter of that reply timeout and the keepalive interval itself.
timeoutPolicy
Default: .currentProfileDefault
The current default profile applies a 30-second connection setup timeout, allows 120 seconds for host-key trust confirmation, and leaves prompt reply waits unbounded.
Use .disabled to turn off all timeout controls.
SSHTimeoutPolicy has three independent knobs:
connectionSetupTimeIntervalhostKeyTrustTimeIntervalresponseTimeInterval
connectionSetupTimeInterval covers one connect attempt, including:
- transport connection setup
- identification exchange
- key exchange
- user authentication
The default SSHTimeoutPolicy() value uses SSHTimeoutPolicy.defaultConnectionSetupTimeInterval, currently 30.
hostKeyTrustTimeInterval covers the app-owned host-key trust decision. This is separate from connectionSetupTimeInterval, so a first-seen host-key prompt or changed-key review can have a longer allowance without consuming the setup timeout.
The default SSHTimeoutPolicy() value uses SSHTimeoutPolicy.defaultHostKeyTrustTimeInterval, currently 120.
responseTimeInterval covers waits where the peer should answer promptly, including:
- channel-open replies
- channel-request replies such as
exec,pty-req,shell, andsubsystem - global-request replies such as
tcpip-forward - SFTP responses
responseTimeInterval covers prompt request or reply paths. Long-running streams continue independently, including:
- long-running command output collection
- interactive shell streams
- remote-forward accept loops
responseTimeInterval stays nil by default on purpose. The shared encrypted-stream receive is deliberately left uncancellable so a timeout cannot interrupt it mid-record and corrupt the stream. Liveness against a silent or half-dead peer is instead provided out of band by the default background keepalive above, which tears the connection down and fails in-flight work when the peer stops answering. Set responseTimeInterval when you additionally want a bounded reply wait on prompt request/reply paths.
Routing: Proxy Versus Jump Host
connectionProxy and proxyJumpHosts solve different problems.
Use connectionProxy when Traversio itself must first reach the SSH server through:
- a SOCKS5 proxy
- an HTTP CONNECT proxy
Use proxyJumpHosts when you want SSH hop chaining through one or more bastion hosts.
Important boundary:
connectionProxyis only for the outermost TCP hop before the first SSH handshakeproxyJumpHostsis the SSH-native hop chain after that
If you set both, the external proxy is used only for the first TCP connection. Later hops travel inside SSH direct-tcpip channels.
Rules Of Thumb
- Start with the defaults and add only the knobs you actually need.
- Set
timeoutPolicy.responseTimeIntervalfirst for automation and background jobs that need bounded prompt replies. The connection setup path already has a 30-second default. - Leave the default background keepalive on for liveness; adjust
keepalivePolicyonly to tune the interval for long-lived shells, tunnels, or idle monitoring connections, or set.disabledto opt out. - Keep compression off unless you have a concrete bandwidth reason to turn it on.
- Treat
SSHProxyJumpHostas hop-specific transport policy. The final target still uses the rootSSHClientConfiguration.
Example
let configuration = SSHClientConfiguration(
host: "app.internal.example.com",
username: "deploy",
authentication: try .ed25519PrivateKey(
contentsOfOpenSSHPrivateKeyFile: "/Users/me/.ssh/id_ed25519"
),
hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
legacyAlgorithmOptions: .disabled,
automaticRekeyPolicy: .currentProfileDefault,
keepalivePolicy: .init(interval: 30),
timeoutPolicy: .init(
connectionSetupTimeInterval: 20,
responseTimeInterval: 5
),
proxyJumpHosts: [
SSHProxyJumpHost(
host: "bastion.example.com",
username: "deploy",
authentication: try .ed25519PrivateKey(
contentsOfOpenSSHPrivateKeyFile: "/Users/me/.ssh/id_ed25519"
),
hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
legacyAlgorithmOptions: .disabled,
keepalivePolicy: .init(interval: 30),
timeoutPolicy: .init(responseTimeInterval: 5)
)
]
)