# How the Remote-SSH Module Works in Reasonix for Remote Development

> Discover how Reasonix Remote-SSH module enables seamless remote development by mirroring VS Code's functionality with a callback-driven API for host resolution authentication keep-alive and port forwarding.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: how-to-guide
- Published: 2026-08-08

---

**Reasonix's Remote-SSH module provides a fully-supervised SSH transport that mirrors VS Code's Remote-SSH extension, handling host resolution, authentication, keep-alive, reconnection, and port forwarding through a callback-driven API consumed by CLI, TUI, and desktop interfaces.**

The DeepSeek-Reasonix repository includes a production-grade Remote-SSH module that enables remote development by managing SSH connections through a unified Go backend. Located in `internal/remote/`, this module supports host alias resolution from `~/.ssh/config`, automatic reconnection with exponential backoff, and interactive authentication flows that surface to any frontend via callbacks.

## Core Architecture Components

The Remote-SSH module separates concerns across distinct packages to handle transport logic, configuration parsing, and UI state synchronization.

### The Remote Package ([`internal/remote/remote.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/remote/remote.go))

The `remote` package serves as the top-level supervisor for SSH transport. It defines the **Client** struct, status machine, and public API that manages the entire connection lifecycle. This file also houses the **BackoffPolicy** and **KeepalivePolicy** configurations that govern reconnection pacing and liveness probing.

### Client Struct and Public API ([`internal/remote/client.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/remote/client.go))

The **Client** struct in [`client.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/client.go) acts as the central object for remote interactions. It holds dial options, the active `ssh.Client` connection, an SFTP handle, a forward registry, and a status hub. Key methods include `New()` for construction, `Start()` for non-blocking dial and supervision, `Subscribe()` for status events, `SFTP()` for filesystem access, and `Forwards()` for port tunneling management.

### Frontend State Management ([`desktop/frontend/src/store/remote.ts`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/frontend/src/store/remote.ts))

The desktop UI consumes the Remote-SSH module through a Zustand store defined in [`remote.ts`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/remote.ts). This store mirrors the kernel's Remote-SSH surface, tracking hosts, connection statuses, port forwards, and pending fingerprints. The `waitForRemoteConnection()` helper allows React components to block until specific hosts reach a connected state.

## Host Resolution and Configuration

Reasonix implements VS Code-compatible SSH configuration resolution, supporting both manual TOML entries and existing `~/.ssh/config` files.

### SSH Config Parsing ([`internal/remote/sshconfig.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/remote/sshconfig.go))

The **SSHConfigSource** type parses `~/.ssh/config` using an embedded fallback parser or delegates to the system's `ssh -G` command for accurate resolution. This ensures Reasonix interprets `Host`, `HostName`, `Port`, `User`, `IdentityFile`, and `ProxyJump` directives exactly as OpenSSH would.

### Resolved Host Targets ([`internal/remote/host.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/remote/host.go))

The **ResolvedHost** struct in [`host.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/host.go) represents a fully-resolved target containing `HostName`, `Port`, `User`, identity files, and ProxyJump chains. The `ResolveHost()` function merges fields from [`reasonix.toml`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/reasonix.toml) entries with the effective SSH configuration, while `ResolveJumpHosts()` builds hop chains for bastion host scenarios.

## Connection Lifecycle

The Remote-SSH module follows an eight-stage supervised lifecycle that handles complex networking scenarios and user interactions.

### Initialization and Dialing

The process begins when `remote.New(opts)` constructs a Client with populated **Options**, **BackoffPolicy**, and **KeepalivePolicy**. Calling `Client.Start()` initiates a non-blocking dial sequence that traverses ProxyJump chains if configured. The client spawns background goroutines for keep-alive probes using the configured interval (default 30 seconds) and miss tolerance (default 3).

### Authentication and Host Key Verification

Authentication supports multiple methods including password, public key, and keyboard-interactive flows. When encountering unknown host keys, the client emits a **StatusEvent** with `state="pending_hostkey"` and a **RemoteFingerprintView** containing the SHA256 hash. The UI prompts the user via the `HostKeyPrompt` callback, implementing TOFU (trust-on-first-use) semantics. Similarly, secret prompts trigger `state="pending_secret"` events handled by the `SecretPrompt` callback.

### Automatic Reconnection and Keep-Alive

Network resilience is handled by the `classifyDialError()` function, which determines whether a failure warrants aborting (e.g., **ErrAuthFailed**) or retrying. Transient failures trigger the **BackoffPolicy** (default 1 second initial delay, max 60 seconds), while the **KeepalivePolicy** monitors connection health. The client automatically reconnects using exponential backoff until explicitly stopped or unrecoverable authentication errors occur.

## File System and Port Forwarding

Once connected, the module exposes remote resources through standardized interfaces.

### SFTP Integration (`internal/remote/sftpfs/`)

The **sftpfs** sub-package wraps the `sftp` library to provide a read-only filesystem view. Accessed via `Client.SFTP()`, this powers the "Files" tab in the desktop UI, allowing recursive directory browsing and file retrieval without maintaining separate SSH sessions.

### Port Forward Registry (`internal/remote/forward/`)

The **forward** sub-package manages port-forward lifecycles through a `forward.Set` that persists for the duration of the Client. The `Client.Forwards()` method returns this registry, which the UI syncs to its Zustand store for the "Ports" tab. Forwards survive reconnections and respect the same backoff policies as the main connection.

## Implementing Remote Development

Developers can interact with the Remote-SSH module programmatically from both Go backends and TypeScript frontends.

Starting a remote client from Go:

```go
import (
    "reasonix/internal/remote"
    "reasonix/internal/config"
)

func startRemote(hostName string) error {
    cfg, _ := config.Load()                     // load reasonix.toml
    sshCfg, _ := remote.LoadUserSSHConfig()    // read ~/.ssh/config
    target, err := remote.ResolveHost(cfg, hostName, sshCfg)
    if err != nil { return err }

    opts := remote.Options{
        Host:        target,
        Auth:        remote.AuthOptions{SecretPrompt: promptSecret},
        HostKeys:    &remote.HostKeyPolicy{},
        Keepalive:   remote.KeepalivePolicy{}, // defaults (30s interval, 3 misses)
        Backoff:     remote.BackoffPolicy{},   // defaults (1s initial, max 60s)
    }

    client, err := remote.New(opts)
    if err != nil { return err }
    go client.Start() // non‑blocking; client will reconnect automatically
    return nil
}

```

Waiting for connections in the frontend:

```ts
import { waitForRemoteConnection } from "./store/remote";

async function openExplorer(hostId: string) {
  try {
    await waitForRemoteConnection(hostId);
    // now the UI can safely show the file explorer or ports list
  } catch (e) {
    console.error("Failed to connect:", e);
  }
}

```

Handling host-key verification in React:

```tsx
import { useRemoteStore } from "./store/remote";

export function FingerprintPrompt() {
  const { pendingFingerprint, clearPendingFingerprint } = useRemoteStore(
    (s) => ({
      pendingFingerprint: s.pendingFingerprint,
      clearPendingFingerprint: s.clearPendingFingerprint,
    })
  );

  if (!pendingFingerprint) return null;

  return (
    <div className="fingerprint-prompt">
      <p>Verify host key for {pendingFingerprint.hostId}:</p>
      <pre>{pendingFingerprint.sha256}</pre>
      <button onClick={() => clearPendingFingerprint(pendingFingerprint)}>
        Accept
      </button>
    </div>
  );
}

```

## Summary

- The **Remote-SSH module** in `internal/remote/` provides a VS Code-compatible SSH transport with automatic reconnection and keep-alive management.
- **Host resolution** merges TOML configuration with `~/.ssh/config` parsing via `SSHConfigSource` and produces `ResolvedHost` targets including ProxyJump chains.
- The **Client** struct manages the connection lifecycle through `New()`, `Start()`, and callback-driven authentication flows for host keys and secrets.
- **Resilience policies** including `BackoffPolicy` and `KeepalivePolicy` ensure connections survive network interruptions without manual intervention.
- **SFTP** and **port forwarding** registries provide filesystem access and tunneling capabilities that persist across reconnections.
- The **Zustand store** in [`desktop/frontend/src/store/remote.ts`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/frontend/src/store/remote.ts) exposes connection state to React components, enabling interactive prompts and status monitoring.

## Frequently Asked Questions

### How does Reasonix handle SSH configuration compared to standard OpenSSH clients?

Reasonix uses the **SSHConfigSource** type in [`internal/remote/sshconfig.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/remote/sshconfig.go) to parse `~/.ssh/config`, falling back to the system's `ssh -G` command for accurate value resolution. This ensures that `HostName`, `Port`, `User`, and `ProxyJump` directives behave identically to VS Code's Remote-SSH extension, supporting complex bastion host configurations without duplicating settings.

### What happens when a network connection drops during a remote session?

The **Client** automatically enters a reconnection loop governed by **BackoffPolicy** (exponential backoff with 1-second initial delay, max 60 seconds) and **KeepalivePolicy** (30-second probes, 3-miss tolerance). The `classifyDialError()` function distinguishes between authentication failures (which abort the connection) and transient network issues (which trigger retry). Existing port forwards and SFTP sessions reconnect transparently once the network recovers.

### How does the Remote-SSH module support multiple frontend interfaces?

All interactivity flows through callbacks defined in `remote.Options`. When the engine encounters an unknown host key or requires a password, it emits **StatusEvent** objects (e.g., `state="pending_hostkey"`) rather than writing to stdin/stdout. The CLI, TUI, and Wails desktop UI each implement these callbacks differently—prompting via terminal or React components—while sharing the same `Client` backend logic.

### Where does the module store connection state for the desktop UI?

The desktop frontend maintains a synchronized state through the Zustand store defined in [`desktop/frontend/src/store/remote.ts`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/frontend/src/store/remote.ts). This store tracks `RemoteHostEntry` configurations, connection statuses, pending fingerprints, and active port forwards. Components like `FingerprintPrompt` subscribe to this store to render interactive prompts, while the `waitForRemoteConnection()` utility provides Promise-based waiting for specific connection states.