How Munder Difflin Handles Unix Domain Sockets and Named Pipes for Agent Communication

Munder Difflin establishes a single process-wide IPC endpoint through HiveManager that exposes a Unix-domain socket on Linux/macOS and a named pipe on Windows, injecting the path into every agent via the HIVE_SOCK environment variable to enable real-time bidirectional communication between the main process and Claude Code hooks.

The chaitanyagiri/munder-difflin repository implements a unified cross-platform inter-process communication (IPC) layer that bridges the Electron main process with spawned agent processes. This architecture uses platform-native transport mechanisms—Unix domain sockets on POSIX systems and Windows named pipes—to create a fast, permission-free channel for lifecycle event streaming and cost tracking without exposing network ports.

Cross-Platform IPC Architecture

Munder Difflin abstracts platform differences behind a single API_surface in src/main/hive.ts. The HiveManager class determines the appropriate transport mechanism at runtime based on process.platform, ensuring agents receive a consistent interface regardless of the operating system.

Platform-Specific Endpoint Generation

The sockPath() method in src/main/hive.ts (lines 302-316) serves as the single source of truth for IPC endpoint generation:

  • POSIX (Linux/macOS): Returns a file system path joining the hive root with 'hooks.sock', creating a standard Unix-domain socket file.
  • Windows: Constructs a named-pipe URL using the format \\\\.\\pipe\\munder-difflin-<hash>, where the hash is derived from a SHA-1 hash of the hive root path (lines 311-315).

This approach ensures that the same codebase runs on both platforms while leveraging the native IPC primitives most appropriate for each operating system.

The HIVE_SOCK Environment Variable Contract

Every spawned agent receives the endpoint location through the HIVE_SOCK environment variable. In src/main/hive.ts (lines 679-680), the ensureAgent() method injects this variable:

const socketPath = hiveManager.sockPath();
injection.env.HIVE_SOCK = socketPath;  // Agents read this to locate the endpoint

By standardizing on a single environment variable, the rest of the codebase—including the cth-hook.cjs shim and third-party provider integrations—remains platform-agnostic.

Server-Side Implementation in HookServer

The HookServer class in src/main/hooks.ts manages the server-side socket lifecycle. Its start() method (lines 73-95) creates a Node.js net.Server instance that binds to the path returned by HiveManager.sockPath().

On POSIX systems, the implementation performs proactive cleanup to prevent "address already in use" errors. Before binding, the code removes any stale socket file:

// src/main/hooks.ts (lines 75-78)
if (existsSync(sock)) {
  rmSync(sock);  // Remove stale Unix-domain socket before binding
}

On Windows, the named pipe namespace automatically handles collisions, so no explicit deletion is required. The server then listens for line-delimited JSON messages from agent processes, routing them to the appropriate hive state management functions.

Agent-Side Communication Flow

When agents spawn, they inherit the HIVE_SOCK environment variable and use it to establish connections back to the main process. This bidirectional channel supports real-time avatar updates, cost ledger entries, and permission workflows.

Environment Variable Injection

The ensureAgent() method in src/main/hive.ts handles the bootstrapping sequence. It writes the hook shim to bin/cth-hook.cjs and configures the agent environment:

// Agent spawning with IPC endpoint injection
const injection = await hiveManager.ensureAgent(meta);
injection.env.HIVE_SOCK = hiveManager.sockPath();

This guarantees that every agent process, whether running Claude Code, Codex, or other LLM providers, knows exactly where to send lifecycle events.

The cth-hook.cjs Shim

The hook shim (cth-hook.cjs) acts as the client-side transport layer. It reads process.env.HIVE_SOCK and establishes a connection using Node.js net.createConnection():

// Minimal shim implementation pattern
const net = require('node:net');
const sock = process.env.HIVE_SOCK;

const client = net.createConnection(sock, () => {
  // Send lifecycle event as single JSON line
  client.write(JSON.stringify(payload) + '\n');
});

client.on('data', data => {
  const response = JSON.parse(data.toString());
  // Forward response to LLM client or handle permission denial
});

This shim forwards events such as PreToolUse, PostToolUse, Stop, Notification, and Status to the main process and awaits JSON responses that may include permission decisions or UI update instructions.

Message Protocol and State Management

The IPC channel uses a simple line-delimited JSON protocol. Each message represents a Claude Code lifecycle event, and the server responds with either an empty JSON object or hook-specific output.

When HookServer receives a message, it:

  1. Parses the JSON line from the socket data
  2. Updates hive state (session IDs, cost ledger entries)
  3. Notifies the UI via WebContents
  4. Returns a JSON response through the same socket connection

This design enables graceful degradation: if HiveManager cannot create the socket (e.g., due to permission errors), the main process logs the error and continues execution, while agents fall back to "bare" mode without hook communication.

Summary

  • Single endpoint strategy: HiveManager.sockPath() in src/main/hive.ts generates platform-appropriate paths, returning a .sock file on POSIX and a \\.\pipe\ URL on Windows.
  • Environment-based discovery: The HIVE_SOCK variable injected by ensureAgent() (line 679) provides the only transport configuration agents need.
  • Unified server implementation: HookServer.start() in src/main/hooks.ts (lines 73-95) binds to the endpoint using net.createServer(), with automatic stale socket cleanup on Unix systems.
  • Shim-based client: The cth-hook.cjs shim uses net.createConnection() to send line-delimited JSON events and receive permission responses.
  • Cross-platform parity: The architecture treats named pipes and Unix sockets identically at the application layer, delegating platform specifics to Node.js net module internals.

Frequently Asked Questions

How does Munder Difflin choose between Unix sockets and named pipes?

The HiveManager.sockPath() method checks process.platform at runtime. On Linux and macOS, it returns a file path ending in hooks.sock, while on Windows it returns a named-pipe URL containing a SHA-1 hash of the hive root. This logic is centralized in src/main/hive.ts (lines 302-316).

What happens if the Unix socket file already exists from a previous session?

Before binding, HookServer.start() in src/main/hooks.ts (lines 75-78) checks for file existence using existsSync() and removes stale sockets with rmSync(). On Windows, named pipes do not persist on the file system, so no cleanup is necessary.

Can agents communicate with the main process if the IPC endpoint fails to initialize?

Yes. According to the error handling in src/main/hooks.ts, if the server cannot bind to the socket or pipe, the main process logs the error and continues execution. Agents detect the absence of a valid HIVE_SOCK environment variable or connection failure and automatically fall back to "bare" mode, operating without hook integration.

How does the hook shim know where to connect without hardcoded paths?

The ensureAgent() method in src/main/hive.ts (lines 679-680) explicitly sets env.HIVE_SOCK to the value returned by sockPath() before spawning the agent process. The cth-hook.cjs shim reads this environment variable at runtime via process.env.HIVE_SOCK, ensuring the correct endpoint is used regardless of platform or installation location.

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 →