How Nodeterm Replays Scrollback on Cold Start: Restoring Terminal State After Reboot

Nodeterm replays terminal scrollback on cold start by periodically saving tmux pane snapshots to disk and restoring them into the xterm instance when the system detects no existing session.

When the host operating system reboots, the tmux server backing nodeterm terminals terminates and all active sessions are lost. To maintain user context across restarts, the eneskirca/nodeterm repository implements a three-stage scrollback persistence mechanism that captures, detects, and replays terminal output. This article examines the precise implementation details of how nodeterm replays scrollback on cold start by analyzing the core TypeScript modules responsible for state recovery.

The Cold Start Challenge for Persistent Terminals

Nodeterm relies on tmux to provide persistent terminal sessions, but a system reboot destroys the tmux server process. Without intervention, users would see empty terminals after restarting their machines, losing the context of previous commands and output. The application solves this by treating the absence of an existing tmux session as a cold start trigger, which initiates a scrollback restore sequence from locally saved snapshots.

Capturing Periodic Scrollback Snapshots

The first stage of the replay mechanism involves continuous background capture of terminal output. In src/core/scrollback-store.ts, a timer governed by SCROLLBACK_SNAPSHOT_MS periodically executes tmux capture-pane -e to extract the current pane content.

The implementation writes a byte-capped (approximately 256 KB) representation of the last lines to a snapshot file:

// src/core/scrollback-store.ts
export async function captureScrollback(sessionId: string) {
  const snapshotPath = path.join(userDataPath, 'terminal-scrollback', `${sessionId}.snapshot`);
  await execFileAsync('tmux', ['capture-pane', '-t', `nt-${sessionId}`, '-e', '-J', '-b', 'tmp']);
  await fs.promises.writeFile(snapshotPath, capturedBytes, { flag: 'w' });
}

Snapshots persist under <userData>/terminal-scrollback/<sessionId>.snapshot, ensuring the recent terminal history survives a complete system shutdown.

Detecting a Cold Start via Session Validation

When a terminal node initializes, the core must determine whether to attach to an existing session or initiate a cold start restore. In src/core/pty-manager.ts, the system checks for the presence of a tmux session using the persist key:

// src/core/pty-manager.ts
const has = await execFileAsync('tmux', ['has-session', '-t', `nt-${persistKey}`]);
const fresh = has.code !== 0;   // 0 = session exists → warm attach, non‑zero = cold start
return { fresh, ...otherCreateResult };

A non-zero exit code from tmux has-session indicates the session no longer exists, setting the fresh flag to true in the PtyCreateResult. This boolean serves as the definitive signal that nodeterm should replay scrollback on cold start rather than attempt a warm attach.

Replaying Scrollback into the Terminal

The final stage occurs in the renderer process when the terminal component mounts. In src/renderer/nodes/TerminalNode.tsx, a useEffect hook monitors the createResult.fresh flag. When true, the component calls the IPC method pty.readScrollback(sessionId) to retrieve the saved bytes and injects them into the xterm instance:

// src/renderer/nodes/TerminalNode.tsx
useEffect(() => {
  if (createResult.fresh) {
    // Cold start – restore previous output
    const data = await window.nodeTerminal.pty.readScrollback(sessionId);
    if (data) {
      xterm.write('\r\n--- session restored ---\r\n');
      xterm.write(data);
    }
  }
  // Normal attach follows…
}, [sessionId, createResult.fresh]);

The readScrollback method (implemented in src/core/pty-manager.ts) reads the snapshot file from the user data directory and returns the buffer to the renderer. The xterm instance displays a visual separator ("--- session restored ---") before the historical content, clearly demarcating where the replay begins.

Summary

  • Periodic snapshots: src/core/scrollback-store.ts runs tmux capture-pane -e on a timer and writes up to 256 KB of output to <userData>/terminal-scrollback/<sessionId>.snapshot.
  • Cold start detection: src/core/pty-manager.ts uses tmux has-session to set a fresh flag when no existing session is found, indicating a post-reboot scenario.
  • Visual replay: src/renderer/nodes/TerminalNode.tsx checks the fresh flag on mount, calls pty.readScrollback via IPC, and writes the saved content into xterm with a session separator.

Frequently Asked Questions

Where does nodeterm store scrollback snapshots?

Nodeterm writes scrollback snapshots to the user data directory under the path <userData>/terminal-scrollback/<sessionId>.snapshot. This location ensures persistence across application restarts and system reboots.

How often does nodeterm capture scrollback?

The capture frequency is controlled by the SCROLLBACK_SNAPSHOT_MS constant, which schedules periodic execution of tmux capture-pane. The exact interval is defined in src/core/scrollback-store.ts to balance disk I/O with data freshness.

What is the maximum size of a scrollback snapshot?

Each snapshot is byte-capped at approximately 256 KB, capturing only the most recent lines of the tmux pane. This limit prevents excessive disk usage while preserving enough context for meaningful post-reboot recovery.

Does replaying scrollback restore the actual tmux session or running processes?

No, replaying scrollback only restores the visual output of the previous terminal session. The actual tmux session and any running processes are terminated during the reboot. The mechanism restores the text history to provide context while a new, clean tmux session initializes in the background.

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 →