# How Tabby's SSH Jump Host Management Works: A Deep Dive into the Internals

> Explore Tabby's SSH jump host management internals. Learn how nested SSH sessions and channel forwarding create efficient, cached connection chains for seamless access.

- Repository: [Eugene/tabby](https://github.com/Eugeny/tabby)
- Tags: internals
- Published: 2026-03-03

---

**Tabby implements SSH jump host support by recursively creating nested SSH sessions that forward TCP channels through intermediate hosts, using a hierarchical multiplexer key system to cache and reuse connection chains.**

Tabby is an open-source terminal emulator built on Electron that provides advanced SSH connection capabilities. Understanding how Tabby SSH jump host management works internally reveals a layered architecture that enables multi-hop connections while optimizing resource usage through intelligent session reuse. The implementation spans across the TypeScript session management layer and leverages the underlying **russh** Rust library for transport abstraction.

## The Three-Component Architecture

Tabby orchestrates jump host connections through three tightly integrated components. The **SSHTabComponent** handles recursive session instantiation, the **SSHSession** class manages transport selection and authentication, and the **SSHMultiplexerService** generates deterministic keys for connection caching.

When configuring a jump host, users set the `jumpHost` property in an SSH profile to reference another profile's ID:

```json
{
  "type": "ssh",
  "options": {
    "host": "target.example.com",
    "port": 22,
    "user": "alice",
    "jumpHost": "my-gateway",
    "reuseSession": true
  }
}

```

This configuration triggers a recursive setup process rather than a direct TCP connection.

## Recursive Session Creation

The `setupOneSession()` method in [`tabby-ssh/src/components/sshTab.component.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/components/sshTab.component.ts) builds jump host chains through recursion. When opening a new tab, the component:

1. Queries `sshMultiplexer.getSession(profile)` to check for reusable sessions
2. Resolves the `jumpHost` profile ID to a full connection configuration
3. Recursively calls `setupOneSession()` to establish the jump session
4. Increments reference counters via `jumpSession.ref()` to prevent premature closure
5. Opens a TCP forward channel through the authenticated jump session

```ts
session.jumpChannel = await jumpSession.ssh.openTCPForwardChannel({
    addressToConnectTo: profile.options.host,
    portToConnectTo: profile.options.port ?? 22,
    originatorAddress: '127.0.0.1',
    originatorPort: 0,
})

```

This design supports arbitrary chain depth, allowing configurations like `gateway-a` → `gateway-b` → `final-target`.

## Transport Initialization in SSHSession

The `start()` method in [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts) implements a priority-based transport selection logic. When a jump channel exists from the previous step, Tabby initializes a nested SSH transport over that channel rather than a direct socket:

```ts
if (this.profile.options.proxyCommand) { … }
else if (this.jumpChannel) {
    transport = await russh.SshTransport.newSshChannel(this.jumpChannel.take())
    this.jumpChannel = null
}
else if (this.profile.options.socksProxyHost) { … }
else {
    transport = await russh.SshTransport.newSocket(`${host}:${port}`)
}

```

The `newSshChannel()` function creates a second-level SSH transport that tunnels all traffic through the already-authenticated jump session. This approach allows the **russh** library to handle encryption and authentication for the target host while using the intermediate host merely as a byte-forwarding layer.

## Hierarchical Multiplexer Keys

Connection reuse in jump host scenarios requires keys that uniquely identify entire connection chains. The `SSHMultiplexerService` in [`tabby-ssh/src/services/sshMultiplexer.service.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/services/sshMultiplexer.service.ts) generates these identifiers recursively:

```ts
let key = `${host}:${port}:${user}:${proxyCommand}:${socksProxyHost}:${socksProxyPort}:${httpProxyHost}:${httpProxyPort}`;
if (profile.options.jumpHost) {
    const jumpConnection = (await this.profilesService.getProfiles())
        .find(x => x.id === profile.options.jumpHost);
    const jumpProfile = this.profilesService.getConfigProxyForProfile<SSHProfile>(jumpConnection);
    key += '$' + await this.getMultiplexerKey(jumpProfile);
}

```

The `$` separator concatenates each jump host's key to create a hierarchical identifier. When `addSession()` stores a connection under this composite key, subsequent calls to `getSession()` with identical chain configurations retrieve the same underlying `SSHSession`. This mechanism enables multiple terminal tabs to share authenticated jump host connections without creating redundant network sockets.

## Resource Cleanup and Reference Counting

Tabby prevents resource leaks through a reference counting system integrated with RxJS observables. Each `SSHSession` exposes a `willDestroy$` observable that child sessions subscribe to during the setup phase.

When a child session closes, it calls `jumpSession.unref()` to decrement the parent session's reference count. The jump session automatically destroys itself—closing the TCP forward channel and underlying socket—only when this count reaches zero. This ensures intermediate hosts remain available for the duration of all dependent child sessions while releasing resources promptly when no longer needed.

## Summary

- **Tabby SSH jump host management** uses recursive construction in `SSHTabComponent.setupOneSession()` to build multi-hop connection chains.
- TCP forward channels are established through `openTCPForwardChannel()` and stored in the child session's `jumpChannel` property.
- Transport initialization in `SSHSession.start()` leverages `russh.SshTransport.newSshChannel()` to tunnel through existing authenticated sessions.
- **Hierarchical multiplex keys** using the `$` separator enable the `SSHMultiplexerService` to track entire connection paths and enable session reuse across tabs.
- **Reference counting** via `ref()` and `unref()` methods ensures jump sessions persist only while actively referenced by child sessions.

## Frequently Asked Questions

### How does Tabby handle authentication for multiple jump hosts?

Each hop operates as an independent `SSHSession` instance resolved from its own profile configuration. Because `setupOneSession()` treats each jump host as a distinct connection, you can use different SSH keys, passwords, or authentication agents for the gateway and final target hosts. The multiplexer system caches each authenticated session separately while maintaining the parent-child relationship through the composite key structure.

### Can different terminal tabs share the same jump host connection?

Yes. The `SSHMultiplexerService` generates deterministic keys that incorporate the entire jump host chain. When opening multiple tabs to the same target through identical intermediate hosts, Tabby retrieves existing authenticated sessions from the cache rather than establishing new TCP connections, provided the profile has `reuseSession` enabled.

### What happens when a jump host connection drops unexpectedly?

When a jump session emits a destruction event through `willDestroy$`, all child sessions detect the closure via their subscriptions and trigger immediate cleanup. The reference counting mechanism ensures dependent sessions close gracefully, and Tabby's reconnection logic can attempt to rebuild the entire chain if automatic reconnection is configured in the profile settings.

### Does Tabby support ProxyJump equivalents with multiple hops?

Yes. The recursive implementation in `SSHTabComponent` supports arbitrary chain depth through repeated application of the setup logic. You can chain multiple `jumpHost` references—where profile A jumps through profile B, which jumps through profile C—and the system will construct the appropriate nested transport layers and generate the correct hierarchical multiplex keys for caching.