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

> Discover how Munder Difflin uses Unix domain sockets and named pipes for efficient agent communication, enabling real-time bidirectional data flow.

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

---

**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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts) (lines 679-680), the `ensureAgent()` method injects this variable:

```typescript
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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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:

```typescript
// 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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts) handles the bootstrapping sequence. It writes the hook shim to `bin/cth-hook.cjs` and configures the agent environment:

```typescript
// 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()`:

```javascript
// 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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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.