Traversio

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 connection

Example: 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:

  1. Traversio opens an SSH connection to bastion.example.com
  2. Traversio authenticates and verifies that hop
  3. Traversio opens a direct-tcpip channel through that hop to db-admin.internal:22
  4. Traversio runs a second SSH handshake inside that channel
  5. the returned SSHConnection is 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:

  • connectionProxy controls how Traversio reaches the first hop
  • proxyJumpHosts controls 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 + proxyJumpHosts path

This validation covers the documented path and leaves broader interoperability as ongoing release work.

On this page