# How the Terminal Plane Works in Munder Difflin: Raw PTY Architecture Explained

> Explore the raw PTY architecture of Munder Difflin. Learn how the Terminal Plane streams byte-level data between tmux and Electron, guaranteeing CLI fidelity.

- Repository: [Chaitanya Giri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin)
- Tags: architecture
- Published: 2026-08-27

---

**The Terminal Plane in Munder Difflin is a raw byte-level conduit that streams pseudo-terminal (PTY) data between tmux-managed processes and the Electron renderer, ensuring byte-for-byte fidelity with the underlying command-line interface.**

Munder Difflin, an open-source multi-agent terminal interface developed by **chaitanyagiri/munder-difflin**, implements a dual-plane architecture to separate concerns. While the Event Plane manages structured hook events and metadata, the **Terminal Plane** exclusively handles raw I/O between real command-line processes and the visual terminal UI. This design guarantees accurate terminal emulation while enabling rich, avatar-driven interfaces.

## What Is the Terminal Plane?

The Terminal Plane constitutes one half of Munder Difflin’s logical data flow architecture. It operates as an unfiltered pipe that moves byte-level data between the operating system’s PTY layer and the Electron renderer process. Unlike the Event Plane—which parses and structures tool usage, notifications, and agent state—the Terminal Plane treats all output as opaque byte streams. This separation allows the system to render faithful terminal views via **xterm.js** while simultaneously extracting structured intelligence for the UI.

According to the project’s [`SPEC.md`](https://github.com/chaitanyagiri/munder-difflin/blob/main/SPEC.md), the Terminal Plane specifically manages:
- Spawning agents as isolated tmux panes (macOS/Linux) or `node-pty` processes (Windows)
- Capturing raw `stdout`/`stderr` streams via IPC channels (`pty:data:<id>`)
- Injecting user keystrokes back into the active process
- Broadcasting exit statuses (`pty:exit:<id>`) to the owning window only

## Core Components of the Terminal Plane

The implementation spans the main process, preload scripts, and renderer components, with platform-specific abstractions for Unix-like systems and Windows.

### Main Process and PtyManager

At the heart of the Terminal Plane lies [`src/main/pty.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/pty.ts), which exports a `PtyManager` class responsible for lifecycle management of pseudo-terminals. On macOS and Linux, `PtyManager` orchestrates tmux sessions rather than managing PTYs directly, leveraging tmux’s `pipe-pane` and `send-keys` commands for I/O.

The `spawnAgent` function creates a new tmux pane targeting a specific `session:window.pane` triple, then immediately initiates output capture:

```typescript
// src/main/pty.ts – simplified flow
import * as pty from 'node-pty';
import { spawnSync } from 'node:child_process';

export async function spawnAgent(opts: SpawnOptions) {
  // 1️⃣ Build the tmux target string
  const target = `${opts.session}:${opts.window}.${opts.pane}`;
  
  // 2️⃣ Launch the pane via tmux (or node-pty on Windows)
  const proc = pty.spawn('tmux', [
    'new-pane', 
    '-t', 
    target, 
    opts.command, 
    ...(opts.args ?? [])
  ], {
    cwd: opts.cwd,
    env: buildPtyEnv(opts.env),
    cols: opts.cols ?? 80,
    rows: opts.rows ?? 24,
  });
  
  // 3️⃣ Start pipe-pane to capture output
  spawnSync('tmux', [
    'pipe-pane',
    '-O',
    '-t',
    target,
    `cat >> /tmp/cth/${opts.id}.log`,
  ]);
  
  return proc;
}

```

The `-O` flag ensures the pipe-pane starts immediately, streaming all pane output to a temporary log file that the renderer tails.

### tmux Integration and Session Management

On Unix platforms, Munder Difflin uses tmux as the underlying terminal multiplexer. Each registered agent corresponds to a unique tmux session, window, and pane identifier. The Terminal Plane uses three core tmux features:

1. **Pane creation** via `tmux new-pane`
2. **Output capture** via `tmux pipe-pane`, which redirects stdout/stderr to `/tmp/cth/<id>.log`
3. **Input injection** via `tmux send-keys`, which simulates keystrokes in the target pane

The renderer watches the temporary log file using `fs.watch` and `fs.createReadStream`, feeding new bytes directly to the xterm.js instance without parsing or transformation.

### The IPC Bridge

The preload script at [`src/preload/index.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/preload/index.ts) exposes a typed bridge on `window.cth` that securely forwards PTY data between the main and renderer processes. This bridge prevents cross-floor leaks by ensuring that `pty:data:<id>` and `pty:exit:<id>` events route exclusively to the window owning that specific agent.

```typescript
// src/preload/index.ts
contextBridge.exposeInMainWorld('cth', {
  sendKeys: (id: string, payload: string) =>
    ipcRenderer.send('pty:send-keys', { id, payload }),
});

```

The `sendKeys` method transmits user input from the renderer to the main process, where it translates into the appropriate platform-specific command.

### Renderer and xterm.js Integration

The renderer component [`src/renderer/src/components/CommandBar.tsx`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/CommandBar.tsx) collects user input and forwards it through the preload bridge. When a user types a command, the component invokes:

```typescript
// src/renderer/src/components/CommandBar.tsx
const sendCommand = (agentId: string, text: string) => {
  window.cth.sendKeys(agentId, text);
};

```

The main process receives this via `ipcMain` and handles the platform-specific injection:

```typescript
// src/main/index.ts
ipcMain.on('pty:send-keys', (ev, { id, payload }) => {
  const pane = ptyManager.getPaneById(id);
  if (pane) {
    // tmux on *nix, node-pty on Windows
    spawnSync('tmux', [
      'send-keys', 
      '-t', 
      pane.tmuxTarget, 
      `"${payload}"`, 
      'Enter'
    ]);
  }
});

```

On Windows, the implementation bypasses tmux entirely, using the `node-pty` library’s native API to write directly to the PTY process stdin.

## Data Flow: From Keystroke to Terminal Output

The Terminal Plane operates through a four-stage pipeline that maintains byte-for-byte accuracy:

1. **Spawn** — `PtyManager.spawn()` creates a tmux pane (or Windows PTY) and registers its identifier with the IPC registry.
2. **Capture** — The main process executes `tmux pipe-pane -O` to redirect all output bytes to `/tmp/cth/<id>.log`.
3. **Tail** — The renderer uses Node.js file watching APIs to detect new bytes, streaming them unchanged to xterm.js.
4. **Inject** — User keystrokes travel from [`CommandBar.tsx`](https://github.com/chaitanyagiri/munder-difflin/blob/main/CommandBar.tsx) through `window.cth.sendKeys`, across the IPC bridge, and into `tmux send-keys` (or equivalent Windows API).

This pipeline ensures that escape sequences, color codes, and cursor positioning commands pass through unmodified, preserving the exact behavior of the underlying CLI tools like Claude Code or standard shell environments.

## Cross-Platform Implementation Details

Munder Difflin adapts the Terminal Plane to platform constraints while maintaining API consistency:

- **macOS/Linux**: Uses tmux as the terminal backend, with `node-pty` spawning tmux processes. This enables persistent sessions and standardized pane management.
- **Windows**: Uses `node-pty` directly without tmux intermediation, invoking the Windows PTY API for process spawning and input/output streaming.

Both implementations expose identical IPC interfaces (`pty:data:<id>`, `pty:send-keys`), allowing the renderer to remain platform-agnostic.

## Summary

The Terminal Plane in **chaitanyagiri/munder-difflin** provides a robust, low-latency conduit for raw terminal data:

- **Dual-plane separation**: Raw PTY streams (Terminal Plane) remain isolated from structured event metadata (Event Plane).
- **tmux orchestration**: On Unix systems, [`src/main/pty.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/pty.ts) leverages tmux for session management, output capture via `pipe-pane`, and input injection via `send-keys`.
- **Secure IPC**: The `window.cth` preload bridge routes data exclusively to owning windows, preventing cross-agent contamination.
- **Cross-platform support**: Windows users receive identical functionality through direct `node-pty` integration rather than tmux.

## Frequently Asked Questions

### What is the difference between the Terminal Plane and the Event Plane in Munder Difflin?

The **Terminal Plane** handles raw, unstructured byte streams between the operating system PTY and the Electron renderer, ensuring exact terminal emulation. The **Event Plane** parses structured data such as tool usage, agent notifications, and metadata hooks. This separation allows Munder Difflin to display faithful terminal output while simultaneously extracting actionable intelligence for the UI.

### How does Munder Difflin capture terminal output from tmux panes?

The system uses tmux’s `pipe-pane` command executed in [`src/main/pty.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/pty.ts) to redirect all output from a specific pane to a temporary file at `/tmp/cth/<id>.log`. The renderer process then watches this file using `fs.watch` and `fs.createReadStream`, feeding new bytes directly to the xterm.js terminal instance without intermediate processing.

### Why does the Terminal Plane use tmux on macOS and Linux instead of direct node-pty?

Munder Difflin uses tmux as an intermediary on Unix platforms to enable persistent session management and standardized pane addressing via the `session:window.pane` triple. This approach allows for advanced features like session resurrection, while `node-pty` is reserved for Windows where tmux is not natively available. The architecture abstracts these differences behind a uniform IPC interface.

### How does the Terminal Plane prevent data leaks between different agents?

The IPC bridge defined in [`src/preload/index.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/preload/index.ts) creates isolated channels using agent-specific identifiers (e.g., `pty:data:<id>`). The main process routes these messages exclusively to the renderer window that owns the specific agent ID, effectively preventing "cross-floor" leaks where one agent’s terminal output could appear in another agent’s window.