# How Tabby Integrates with the Windows OpenSSH Agent: A Deep Dive into Named Pipe Authentication

> Discover how Tabby integrates with the Windows OpenSSH Agent using named pipe authentication. Learn about the russh library and automatic pipe detection for seamless SSH connectivity.

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

---

**Tabby connects to the Windows OpenSSH ssh-agent through the named pipe `\\.\pipe\openssh-ssh-agent` using the russh library, automatically detecting the pipe in [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts) and forwarding authentication challenges via `SSHAgentStream`.**

Tabby, the open-source terminal emulator maintained by Eugeny, implements Windows OpenSSH Agent integration through its `tabby-ssh` module. This integration allows Windows users to leverage existing ssh-agent key material stored in the Windows OpenSSH service without manual password entry. The implementation relies on the `russh` Rust library and involves specific detection logic for the OpenSSH named pipe alongside fallback support for Pageant.

## Agent Discovery via Named Pipe Detection

The core of Tabby's Windows OpenSSH integration resides in `SSHSession#getAgentConnectionSpec()` within [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts) (lines 296-330). This method implements the automatic discovery logic that determines which local agent to use for SSH authentication.

When `agentType` is set to `"auto"` (the default defined in [`tabby-ssh/src/config.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/config.ts)), Tabby first checks for the existence of the Windows OpenSSH named pipe at `\\.\pipe\openssh-ssh-agent` using `fs.stat`. If the pipe exists, the method returns a `russh.AgentConnectionSpec` with `kind: 'named-pipe'` pointing to this path. If the pipe is absent, Tabby queries `russh.isPageantRunning()` to detect Pageant as a fallback mechanism.

### Configuration Options in SSHConfigProvider

The default behavior is controlled by `SSHConfigProvider` in [`tabby-ssh/src/config.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/config.ts) (lines 5-11), which exposes the following settings:

- **`ssh.agentType`**: Accepts `"auto"`, `"pageant"`, or `"custom"` to control agent selection
- **`ssh.agentPath`**: Optional string specifying a custom named pipe path when `agentType` is set to `"custom"`

When `agentType` is `"custom"`, Tabby bypasses automatic detection and connects directly to the pipe specified in `agentPath`.

## The Agent Authentication Workflow

Once the agent specification is resolved, Tabby prepares authentication methods during session initialization. In [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts) (lines 298-328), the `init()` method constructs an array of `AuthMethod` objects. If the profile uses `auth: "agent"` or has private keys configured, Tabby loads corresponding `.pub` files to create specific agent-identity methods, followed by a general agent fallback that allows the agent to try all loaded keys.

When the remote server requests agent forwarding, the subscription at lines 35-52 in the same file handles the `agentChannelOpen$` event. This creates a bidirectional data pipe between the SSH channel and the local agent:

```ts
this.ssh.agentChannelOpen$.subscribe(async newChannel => {
  if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
    throw new Error('Cannot open agent channel before auth')
  }
  const channel = await this.ssh.activateChannel(newChannel)
  const spec = await this.getAgentConnectionSpec()
  if (!spec) { await channel.close(); return }

  const agent = await russh.SSHAgentStream.connect(spec)
  channel.data$.subscribe(data => agent.write(data))
  agent.data$.subscribe(data => channel.write(data), undefined, () => channel.close())
  channel.closed$.subscribe(() => agent.close())
})

```

This real-time piping allows the remote server to request signatures from your local Windows OpenSSH agent while maintaining session security.

## Practical Configuration Examples

### Enabling Windows OpenSSH Agent in a Profile

To use the Windows OpenSSH agent with automatic detection, configure your Tabby profile with the following JSON structure:

```json
{
  "name": "My Windows Host",
  "type": "ssh",
  "options": {
    "host": "my.host.com",
    "port": 22,
    "user": "myuser",
    "auth": "agent",
    "privateKeys": [
      "%h/.ssh/id_rsa"
    ]
  },
  "store": {
    "ssh": {
      "agentType": "auto",
      "agentPath": null
    }
  }
}

```

With this configuration, Tabby calls `getAgentConnectionSpec()`, discovers the `\\.\pipe\openssh-ssh-agent` pipe, and authenticates using keys loaded in the agent. The `privateKeys` array is optional but recommended when you want Tabby to pre-load specific public keys for agent-identity authentication.

### Specifying a Custom Named Pipe

For scenarios requiring a non-standard agent location, bypass automatic detection:

```json
{
  "store": {
    "ssh": {
      "agentType": "custom",
      "agentPath": "\\\\.\\pipe\\my-custom-agent"
    }
  }
}

```

Setting `"agentType": "custom"` forces Tabby to connect directly to the supplied pipe path without checking for the standard OpenSSH pipe or Pageant.

## Summary

- **Tabby integrates with the Windows OpenSSH Agent** through the named pipe `\\.\pipe\openssh-ssh-agent` using the `russh` library's `SSHAgentStream` interface.
- **Automatic detection occurs** in `SSHSession#getAgentConnectionSpec()` at [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts) (lines 296-330), which checks for the pipe's existence via `fs.stat` before falling back to Pageant.
- **Configuration is controlled** by `SSHConfigProvider` in [`tabby-ssh/src/config.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/config.ts), offering `agentType` options of `"auto"`, `"pageant"`, or `"custom"`.
- **Agent forwarding** is handled through bidirectional piping between the SSH channel and local agent in the `agentChannelOpen$` subscription at lines 35-52 of the session file.
- **Public key pre-loading** occurs when private key paths are configured, allowing Tabby to specify exact agent identities before falling back to general agent authentication.

## Frequently Asked Questions

### Does Tabby require manual configuration to use the Windows OpenSSH agent?

No, Tabby automatically detects the standard Windows OpenSSH agent pipe at `\\.\pipe\openssh-ssh-agent` when `agentType` is set to `"auto"` (the default). The detection logic in [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts) checks for pipe existence during session initialization, requiring no manual path configuration unless you are using a custom agent location.

### Can Tabby fall back to Pageant if the OpenSSH agent is not running?

Yes, when `agentType` is `"auto"`, Tabby implements a two-step discovery process. If `fs.stat` fails to locate the OpenSSH named pipe, the code calls `russh.isPageantRunning()` to check for a running Pageant instance. If Pageant is detected, Tabby returns a connection spec with `kind: 'pageant'` instead of the named-pipe configuration, allowing seamless fallback without user intervention.

### How does Tabby handle agent forwarding requests from remote servers?

When a remote server opens an agent channel, Tabby's `agentChannelOpen$` subscription in [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts) creates an `russh.SSHAgentStream` connected to the spec returned by `getAgentConnectionSpec()`. The implementation uses RxJS observables to pipe data bidirectionally: data from the SSH channel is written to the local agent via `agent.write()`, and responses from the agent are written back to the channel via `channel.write()`, enabling secure agent forwarding to remote hosts.

### What is the difference between `agentType: "auto"` and `"pageant"`?

Setting `agentType` to `"auto"` instructs Tabby to prefer the Windows OpenSSH named pipe and automatically fall back to Pageant only if the pipe is unavailable. Conversely, setting it to `"pageant"` forces Tabby to skip the named pipe detection entirely and connect directly to Pageant, which is useful when both agents are running but you specifically want to use Pageant's key store.