How Nodeterm Handles SSH Projects and Remote Synchronization

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, 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 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, 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 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 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. 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 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 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. 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 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, 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. 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, 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:

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:

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:

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.

Synchronizing workspace state:

await window.nodeTerminal.workspace.save();

This serializes the current layout to the remote .nodeterm/project.json file.

Requesting passphrase input:

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 to create long-lived, multiplexed connections to remote hosts.
  • Transparent Filesystem: The sshFs abstraction in 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 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, 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 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, 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. 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 module handles bidirectional synchronization of this file, ensuring your terminal layouts and node configurations survive application restarts and allow access from multiple client machines.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →