# How Nodeterm Handles SSH Projects and Remote Synchronization

> Discover how Nodeterm manages SSH projects with persistent connections and tmux for seamless file operations, terminal streaming, and automatic synchronization. Learn more!

- Repository: [eneskirca/nodeterm](https://github.com/eneskirca/nodeterm)
- Tags: how-to-guide
- Published: 2026-08-26

---

**Nodeterm handles SSH projects by establishing a persistent OpenSSH ControlMaster connection that maintains remote tmux sessions, enabling transparent file system operations, terminal I/O streaming, and automatic workspace resynchronization through a periodic watchdog mechanism.**

Nodeterm is an open-source terminal workspace manager that treats remote SSH environments as first-class citizens within its project system. When you open an SSH project, the application spawns a dedicated **ControlMaster** process to maintain a long-lived, multiplexed connection to the remote host, allowing seamless access to remote files and terminals as if they were local resources. This architecture ensures that workspace layouts, scrollback history, and terminal sessions remain synchronized even across network interruptions or application restarts.

## The SSH Project Architecture

Nodeterm's SSH implementation relies on three tightly integrated layers that abstract remote resources into local equivalents.

- **ControlMaster Management Layer:** Implemented in [`src/main/remote-ssh/ssh-project.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-ssh/ssh-project.ts), this layer persists connection details and manages the OpenSSH ControlMaster socket lifecycle. It stores the `host`, `user`, and optional `identityFile`, then spawns the master connection using `ssh -M -N` to create a reusable socket at a temporary `controlPath`.

- **Remote Filesystem Abstraction:** The [`src/main/ssh-fs.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/ssh-fs.ts) module exposes a standard `FsApi` interface that translates local file operations into remote commands executed over the ControlMaster. Every `readFile`, `writeFile`, or `stat` call becomes an `ssh -S <controlPath>` command streamed to the remote host.

- **Terminal Transport Layer:** Located in [`src/core/pty-manager.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/pty-manager.ts), this component detects when a terminal node belongs to an SSH project via the `sshRemote` metadata field. Rather than spawning a local shell, it launches an SSH client connected to the ControlMaster socket, which then attaches to a persistent remote tmux session managing the actual shell.

## Establishing the ControlMaster Connection

When you initiate an SSH project, the `SshProjectManager` class in [`src/main/remote-ssh/ssh-project.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-ssh/ssh-project.ts) creates a background SSH master process. This process runs `ssh -M -N -S <controlPath> user@host`, establishing a socket-based multiplexing connection that subsequent SSH commands reuse without re-authenticating.

The [`src/core/remote-ssh/control-master.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/remote-ssh/control-master.ts) module wraps low-level socket operations, providing methods to check master status using `ssh -O check` and gracefully terminate connections with `ssh -O exit`. This architecture minimizes connection overhead and supports persistent remote state across multiple terminal instances.

## Remote File System Operations

All file interactions for SSH projects route through the `sshFs(projectId)` façade defined in [`src/main/ssh-fs.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/ssh-fs.ts). This module executes filesystem commands across the ControlMaster connection, treating remote paths as if they were local.

When the renderer requests a file operation, the main process translates it into shell commands. For example, reading [`/src/app/main.ts`](https://github.com/eneskirca/nodeterm/blob/main//src/app/main.ts) executes `ssh -S <controlPath> cat /src/app/main.ts` on the remote host. The module handles directory creation, file statistics, and recursive operations, ensuring the Explorer panel and text editors function transparently against remote repositories.

## Terminal I/O and Remote Tmux Integration

Terminal nodes in SSH projects carry a `sshRemote` property specifying the ControlMaster socket path and remote working directory. When [`src/core/pty-manager.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/pty-manager.ts) detects this property, it spawns a local PTY running the SSH client in ControlMaster mode, which immediately attaches to a remote tmux session.

This design places the actual shell and scrollback buffer on the remote server rather than the local machine. The PTY manager synchronizes terminal output using `tmux capture-pane -e` to retrieve scrollback history, ensuring that reconnecting to a project restores your exact terminal state including color codes and cursor position. Clipboard operations via OSC 52 sequences also execute remotely, maintaining consistency with the server environment.

## Workspace Synchronization Protocol

Nodeterm synchronizes workspace layouts between local and remote environments using [`src/main/remote-workspace-io.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-workspace-io.ts). When you save a project, the system serializes the node tree and writes it to `<remoteCwd>/.nodeterm/project.json` via the ControlMaster connection.

This approach ensures your terminal layouts, environment variables, and working directories persist on the remote filesystem. On application startup, the renderer loads this remote configuration file to reconstruct the exact canvas layout, enabling seamless project portability across different client machines accessing the same SSH host.

## Reconnection and Watchdog Mechanics

To handle network instability, [`src/main/remote-ssh/ssh-project.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-ssh/ssh-project.ts) implements a watchdog timer (`MASTER_WATCHDOG_MS`) that periodically validates the ControlMaster socket. The manager runs `ssh -O check` at regular intervals to verify connectivity.

If the socket disappears or the check fails, the manager automatically respawns the ControlMaster process and recreates the remote tmux session. This reconnection logic, detailed in [`docs/superpowers/specs/2026-08-09-ssh-reconnect-resync-design.md`](https://github.com/eneskirca/nodeterm/blob/main/docs/superpowers/specs/2026-08-09-ssh-reconnect-resync-design.md), ensures minimal disruption by restoring both the filesystem mount and terminal sessions without manual intervention.

## SSH Authentication and Passphrase Handling

For encrypted private keys, Nodeterm implements an **SSH_ASKPASS** shim in [`src/main/remote-ssh/ssh-askpass.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-ssh/ssh-askpass.ts). When the SSH client requires authentication, this script triggers an IPC message to the renderer process.

The renderer displays a modal via [`src/renderer/components/SshPassphrasePrompt.tsx`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/components/SshPassphrasePrompt.tsx), allowing secure passphrase entry. The passphrase returns to the main process through `ssh-project:passphrase-submit`, enabling the ControlMaster to restart with proper credentials. Failed attempts surface error hints through the `IdentityAgent` interface, providing clear feedback on authentication issues.

## Practical Implementation Examples

The following patterns demonstrate how to interact with SSH projects programmatically.

**Opening an SSH project via the API:**

```typescript
await window.nodeTerminal.sshProject.open({
  server: { host: 'example.com', user: 'alice' },
  remoteCwd: '/home/alice/workspace',
});

```

This triggers `SshProjectManager.connect()`, which spawns the ControlMaster and initializes the remote workspace.

**Creating a remote terminal node:**

```typescript
createTerminalNode({
  title: 'remote shell',
  cwd: '/home/alice/workspace',
  ssh: { host: 'example.com', user: 'alice' },
  sshRemoteTmux: true,
});

```

The `sshRemote` metadata routes I/O through the ControlMaster to the remote tmux session.

**Reading remote files from an editor:**

```typescript
const content = await window.nodeTerminal.fs.readFile(
  '/src/app/main.ts',
  { projectId: currentProject.id }
);

```

The call executes `ssh -S <controlPath> cat` on the remote host via [`ssh-fs.ts`](https://github.com/eneskirca/nodeterm/blob/main/ssh-fs.ts).

**Synchronizing workspace state:**

```typescript
await window.nodeTerminal.workspace.save();

```

This serializes the current layout to the remote [`.nodeterm/project.json`](https://github.com/eneskirca/nodeterm/blob/main/.nodeterm/project.json) file.

**Requesting passphrase input:**

```typescript
const pass = await window.nodeTerminal.sshProject.promptPassphrase({
  host: 'example.com',
  user: 'alice',
});

```

The renderer displays the passphrase modal, returning the value securely to the SSH manager.

## Summary

- **Persistent Connections:** Nodeterm uses OpenSSH ControlMaster sockets maintained by [`src/main/remote-ssh/ssh-project.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-ssh/ssh-project.ts) to create long-lived, multiplexed connections to remote hosts.
- **Transparent Filesystem:** The `sshFs` abstraction in [`src/main/ssh-fs.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/ssh-fs.ts) translates local file API calls into remote SSH commands, enabling seamless editing of remote files.
- **Remote Terminal State:** Terminal I/O routes through [`src/core/pty-manager.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/pty-manager.ts) to remote tmux sessions, preserving scrollback and state on the server rather than locally.
- **Automatic Recovery:** A watchdog mechanism (`MASTER_WATCHDOG_MS`) in the SSH project manager automatically reconnects dropped connections and resynchronizes the workspace.
- **Secure Authentication:** Passphrase handling occurs through an SSH_ASKPASS shim and secure IPC to [`src/renderer/components/SshPassphrasePrompt.tsx`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/components/SshPassphrasePrompt.tsx), supporting encrypted keys without terminal interaction.

## Frequently Asked Questions

### How does nodeterm maintain persistent SSH connections?

Nodeterm maintains persistent connections by spawning an OpenSSH ControlMaster process using `ssh -M -N` when you open an SSH project. This creates a Unix domain socket at a temporary path that all subsequent SSH operations reuse, eliminating the overhead of re-authentication and keeping the connection alive even when individual terminals close.

### What happens when an SSH connection drops in nodeterm?

When a connection drops, the watchdog timer in [`src/main/remote-ssh/ssh-project.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-ssh/ssh-project.ts) detects the failure during its periodic `ssh -O check` validation. The manager automatically respawns the ControlMaster, recreates the remote tmux session, and resynchronizes the workspace files from [`src/main/remote-workspace-io.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-workspace-io.ts), restoring your terminal state without manual reconnection steps.

### How does nodeterm handle private key passphrases?

Nodeterm handles passphrases through a custom SSH_ASKPASS implementation in [`src/main/remote-ssh/ssh-askpass.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-ssh/ssh-askpass.ts). When the SSH client encounters an encrypted key, it triggers an IPC message to the renderer, which displays the `SshPassphrasePrompt` modal. The entered passphrase returns securely to the main process to restart the authentication flow, supporting secure key storage without requiring terminal-based input.

### Where does nodeterm store SSH project workspace data?

Workspace data for SSH projects stores locally in the application's project registry, while the actual workspace layout persists remotely in `<remoteCwd>/.nodeterm/project.json`. The [`src/main/remote-workspace-io.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/remote-workspace-io.ts) module handles bidirectional synchronization of this file, ensuring your terminal layouts and node configurations survive application restarts and allow access from multiple client machines.