ProxyJump
Reach a final SSH server through one or more SSH jump hosts before you open shell, exec, SFTP, or forwarding work on the final connection.
ProxyJump defines the SSH hop chain used to reach a final server through one or more bastion hosts.
+-----------+ +----------------------+ +---------------------+
| Local app | ---> | Jump host | ---> | Final SSH server |
+-----------+ +----------------------+ +---------------------+
| 1. Authenticate here |
| 2. Open direct-tcpip |
| to the next hop |
+----------------------+
What actually happens:
1. SSH connection #1 is established to the jump host
2. A direct-tcpip channel is opened through that jump host
3. SSH connection #2 is established inside that channel
4. The returned SSHConnection is the final target connectionExample: Reach An Internal Host Through A Bastion
import Traversio
let configuration = SSHClientConfiguration(
host: "db-admin.internal",
username: "deploy",
authentication: .password("final-target-password"),
hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
compressionPreference: .delayedZlib,
proxyJumpHosts: [
SSHProxyJumpHost(
host: "bastion.example.com",
username: "deploy",
authentication: .password("bastion-password"),
hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
compressionPreference: .delayedZlib
)
]
)
let result = try await SSHClient.withConnection(configuration: configuration) { connection in
try await connection.execute("hostname")
}Connection flow:
- Traversio opens an SSH connection to
bastion.example.com - Traversio authenticates and verifies that hop
- Traversio opens a
direct-tcpipchannel through that hop todb-admin.internal:22 - Traversio runs a second SSH handshake inside that channel
- the returned
SSHConnectionis the final target connection
Multi-Hop Example
proxyJumpHosts is an array because real networks often need more than one hop:
let configuration = SSHClientConfiguration(
host: "final.internal",
username: "deploy",
authentication: .password("final-password"),
hostKeyPolicy: .acceptAnyVerifiedHostKey,
proxyJumpHosts: [
SSHProxyJumpHost(
host: "bastion-a.example.com",
username: "jump-a",
authentication: .password("jump-a-password"),
hostKeyPolicy: .acceptAnyVerifiedHostKey
),
SSHProxyJumpHost(
host: "bastion-b.internal",
username: "jump-b",
authentication: .password("jump-b-password"),
hostKeyPolicy: .acceptAnyVerifiedHostKey
),
]
)Each hop has its own:
- host
- port
- username
- authentication
- host key policy
- compression preference
- automatic rekey policy
The final target keeps its transport settings on the root SSHClientConfiguration. Bastion hosts often use different credentials, trust rules, or transport policy than the final server.
Connection Proxies And ProxyJump
These features cover different layers of the route:
- ProxyJump: SSH to hop A first, then carry the next SSH hop inside A
- HTTP/SOCKS connection proxy: send the outer TCP connection through a non-SSH proxy before SSH starts
Traversio supports ProxyJump and user-specified HTTP CONNECT or SOCKS5 outer connection proxies through SSHClientConfiguration.connectionProxy.
If you set both:
connectionProxycontrols how Traversio reaches the first hopproxyJumpHostscontrols which SSH hop comes first after that tunnel exists
Validation
As of April 7, 2026:
- ProxyJump is live-validated against a real local OpenSSH 9.6 target
- the probe verifies a real first-hop SSH connection plus a nested second SSH handshake through that hop
- outer connection proxies are also live-validated through the local Dante/Tinyproxy Docker matrix for SOCKS5 no-auth, SOCKS5 username/password, HTTP CONNECT, HTTP CONNECT Basic auth, and one combined
connectionProxy + proxyJumpHostspath
This validation covers the documented path and leaves broader interoperability as ongoing release work.