# Broker Lifecycle Management and Endpoint Handling in the OpenAI Codex Plugin

> Understand OpenAI Codex plugin's broker lifecycle management and endpoint handling. Learn about IPC endpoints, session state, child processes, and graceful shutdown.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: deep-dive
- Published: 2026-08-05

---

**The OpenAI Codex plugin manages broker processes through a cross-platform lifecycle system that creates IPC endpoints, persists session state, spawns detached child processes, and handles graceful shutdown via JSON-RPC.**

The `openai/codex-plugin-cc` repository implements a sophisticated broker architecture to mediate JSON-RPC traffic between the host IDE and the Codex app-server. Understanding broker lifecycle management and endpoint handling is essential for developers extending or debugging the plugin, as it abstracts OS-specific IPC mechanisms while ensuring reliable session persistence and clean teardown.

## What Is the Broker Process?

The **broker process** (`app-server-broker.mjs`) acts as a message router between two endpoints:

- The **host IDE** (VS Code, JetBrains, etc.)
- The **Codex app-server** (provides LLM completions)

The broker enforces a **single active request policy** — concurrent requests receive a busy-RPC error — and handles streaming notifications back to the client.

## Endpoint Creation and Platform Abstraction

Cross-platform IPC requires different transport mechanisms. The plugin solves this through `createBrokerEndpoint` in `broker-endpoint.mjs`.

### Platform-Specific Endpoint Generation

| Platform | Transport | Endpoint Format |
|----------|-----------|-----------------|
| Windows | Named pipe | `pipe:\\\\.\\pipe\\<sanitized-name>` |
| macOS/Linux | Unix domain socket | `unix:<sessionDir>/broker.sock` |

The `sanitizePipeName` helper normalizes pipe names to alphanumerics, hyphens, underscores, and periods to avoid Windows naming restrictions.

```javascript
import { createBrokerEndpoint, parseBrokerEndpoint } from
  "./plugins/codex/scripts/lib/broker-endpoint.mjs";

// Linux/macOS example
const unixEndpoint = createBrokerEndpoint("/tmp/cxc-abc123", "darwin");
console.log(unixEndpoint); // → "unix:/tmp/cxc-abc123/broker.sock"

const parsed = parseBrokerEndpoint(unixEndpoint);
// → { kind: "unix", path: "/tmp/cxc-abc123/broker.sock" }

// Windows example
const winEndpoint = createBrokerEndpoint("C:\\Temp\\cxc-abc123", "win32");
console.log(winEndpoint);
// → "pipe:\\\\.\\pipe\\cxc-abc123-codex-app-server"

```

The `parseBrokerEndpoint` function validates endpoint strings and returns a normalized object with `kind` and `path` properties. Invalid or malformed strings throw descriptive errors.

## Session Persistence and State Management

The plugin stores broker metadata in a **per-workspace state directory**, enabling fast reconnection to already-running brokers across IDE restarts.

### Key Session Files

- [`broker.json`](https://github.com/openai/codex-plugin-cc/blob/main/broker.json) — Serialized session containing endpoint, PID, log file path, and session directory
- PID file — Contains the broker process ID for signal-based termination
- Log file — Captures broker stdout/stderr for debugging

### Session API in broker-lifecycle.mjs

```javascript
// Load existing session if broker.json exists
const session = await loadBrokerSession(cwd);

// Persist new session after successful spawn
await saveBrokerSession(cwd, {
  endpoint: "unix:/tmp/cxc-abc123/broker.sock",
  pid: 12345,
  pidFile: "/tmp/cxc-abc123/broker.pid",
  logFile: "/tmp/cxc-abc123/broker.log",
  sessionDir: "/tmp/cxc-abc123"
});

// Clear session record without cleanup (used when broker died unexpectedly)
await clearBrokerSession(cwd);

```

## Broker Startup and Health Verification

The `ensureBrokerSession` function in `broker-lifecycle.mjs` implements the complete startup flow with built-in fault tolerance.

### Startup Sequence

1. **Load** — Attempt to read existing [`broker.json`](https://github.com/openai/codex-plugin-cc/blob/main/broker.json)
2. **Validate** — Ping the endpoint via `isBrokerEndpointReady`
3. **Reuse** — Return valid existing session immediately
4. **Spawn** — Create new temporary directory, generate endpoint, launch broker
5. **Wait** — Poll endpoint with `waitForBrokerEndpoint` until accepting connections
6. **Persist** — Save session metadata for future reuse

```javascript
import { ensureBrokerSession } from
  "./plugins/codex/scripts/lib/broker-lifecycle.mjs";

async function startBroker(cwd) {
  const session = await ensureBrokerSession(cwd, {
    // Optional: custom endpoint factory
    // createBrokerEndpoint: (dir, plat) => `tcp:localhost:${port}`,
    
    // Optional: custom process termination
    // killProcess: (pid) => process.kill(pid, "SIGTERM")
  });

  if (!session) {
    throw new Error("Failed to start the Codex broker");
  }
  
  console.log("Broker ready at:", session.endpoint);
  console.log("PID:", session.pid);
  return session;
}

```

### Health Check Implementation

The `waitForBrokerEndpoint` function uses Node's `net.createConnection` to verify IPC readiness with configurable retry logic:

```javascript
// Simplified conceptual flow
async function waitForBrokerEndpoint(endpoint, options) {
  const { kind, path } = parseBrokerEndpoint(endpoint);
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const conn = net.createConnection({ path });
      await once(conn, "connect");
      conn.end();
      return true;
    } catch {
      await setTimeout(delayMs);
    }
  }
  throw new Error("Broker endpoint never became ready");
}

```

## Graceful Shutdown and Teardown

The plugin implements **two-phase shutdown** for reliability: cooperative JSON-RPC request followed by forced cleanup if necessary.

### Shutdown Flow

```javascript
import { sendBrokerShutdown, teardownBrokerSession } from
  "./plugins/codex/scripts/lib/broker-lifecycle.mjs";

async function stopBroker(session) {
  // Phase 1: Cooperative shutdown via JSON-RPC
  try {
    await sendBrokerShutdown(session.endpoint);
  } catch (err) {
    console.warn("Broker shutdown RPC failed, proceeding to forced cleanup");
  }

  // Phase 2: Filesystem and process cleanup
  teardownBrokerSession({
    endpoint: session.endpoint,
    pidFile: session.pidFile,
    logFile: session.logFile,
    sessionDir: session.sessionDir,
    pid: session.pid,
    killProcess: (pid) => process.kill(pid, "SIGTERM")
  });
}

```

### Cleanup Responsibilities of teardownBrokerSession

1. **Signal termination** — Execute `killProcess` callback if provided
2. **File removal** — Delete PID file and log file
3. **Endpoint deletion** — Remove Unix socket or named pipe
4. **Directory removal** — Delete temporary session directory

The **order matters**: signal before unlinking files, ensuring the broker can exit cleanly before its resources disappear.

## Broker Request Routing and Concurrency Control

The actual broker implementation in `app-server-broker.mjs` handles connection acceptance and request forwarding to `CodexAppServerClient`.

### Key Behaviors

- **Single active request** — Second concurrent request receives `{"code": -32000, "message": "Broker busy"}` busy-RPC error
- **Streaming support** — Forwards `$/progress` notifications from app-server to IDE client
- **Graceful degradation** — Connection errors propagate as JSON-RPC error responses

## Cross-Platform Testing Coverage

The repository includes platform-specific test suites:

| Test File | Coverage |
|-----------|----------|
| `broker-endpoint.test.mjs` | Endpoint creation and parsing on Windows and Unix |
| `broker-session.test.mjs` | Full lifecycle via `ensureBrokerSession` integration |

Tests validate that `sanitizePipeName` correctly handles edge cases like spaces, special characters, and long paths on Windows.

## Summary

- **Platform abstraction** — `broker-endpoint.mjs` unifies Unix sockets and Windows named pipes behind a single string-based endpoint format.
- **Session durability** — `broker-lifecycle.mjs` persists broker metadata to workspace state, enabling fast reconnection across IDE sessions.
- **Robust startup** — `ensureBrokerSession` implements health-checked startup with automatic fallback to spawning new brokers.
- **Clean shutdown** — Two-phase teardown combines cooperative JSON-RPC shutdown with forced process termination and complete filesystem cleanup.
- **Single-request policy** — The broker enforces sequential request processing to prevent resource contention.

## Frequently Asked Questions

### How does the Codex plugin handle broker crashes or unexpected termination?

Before reusing a saved session, `ensureBrokerSession` calls `isBrokerEndpointReady` to ping the endpoint. If the connection fails, the session is discarded and a new broker is spawned. Zombie PID files and stale socket files are removed during `teardownBrokerSession`.

### Can I customize the IPC transport mechanism?

Yes. Pass a custom `createBrokerEndpoint` function to `ensureBrokerSession`. The function receives `(sessionDir, platform)` and must return a string parseable by `parseBrokerEndpoint`. However, the broker executable `app-server-broker.mjs` must also support the chosen transport.

### Why does the broker enforce a single active request?

This prevents resource exhaustion on the app-server and ensures deterministic behavior for streaming completions. Concurrent IDE requests receive an immediate busy-RPC error with code `-32000`, allowing the client to implement retry logic with exponential backoff.

### Where is session state stored?

Sessions persist to `<workspace>/.codex/broker.json` via `saveBrokerSession`. The temporary directory containing PID files, logs, and socket files is created under the system temp directory with a `cxc-` prefix and random suffix.