# Lifecycle of the App Server Broker in the OpenAI Codex Plugin

> Explore the eight phase lifecycle of the app server broker in the OpenAI Codex plugin. Learn how it manages session creation, endpoint negotiation, and graceful teardown.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: internals
- Published: 2026-07-14

---

**The Codex plugin orchestrates an eight-phase lifecycle for its JSON-RPC broker process, managing session creation, endpoint negotiation, process spawning, readiness polling, persistence, reuse, and graceful teardown through `broker-lifecycle.mjs` and `app-server-broker.mjs`.**

The `openai/codex-plugin-cc` repository implements a lightweight mediator architecture that connects the Codex companion to app servers via a dedicated broker process. Understanding the lifecycle of the app server broker is essential for debugging connection failures, managing resources, and extending the plugin's capabilities.

## Broker Lifecycle Overview

The broker acts as a bridge between the Codex companion and the target application server, handling JSON-RPC message routing. The lifecycle ensures that only one broker runs per workspace while providing robust cleanup mechanisms to prevent orphaned processes or socket files. The implementation spans three primary files: `broker-lifecycle.mjs` for orchestration, `broker-endpoint.mjs` for transport abstraction, and `app-server-broker.mjs` for the runtime process.

## Phase-by-Phase Breakdown

### 1. Session Directory Creation

When a new broker session is required, `createBrokerSessionDir()` generates a temporary directory (e.g., `/tmp/cxc-xxxx`) to house the Unix socket or named pipe, PID file, and log files. This isolation prevents collisions between concurrent workspaces. In `broker-lifecycle.mjs` at lines 15-17, the function creates this directory before any process spawning occurs.

### 2. Endpoint Generation

The `createBrokerEndpoint()` function in `broker-endpoint.mjs` (lines 10-16) constructs a platform-specific endpoint string. On Windows, it returns a `pipe:` prefixed path for named pipes, while on Unix-like systems, it generates a `unix:` prefixed socket path within the session directory. This abstraction ensures cross-platform compatibility without modifying the core logic.

### 3. Process Spawning

The `spawnBrokerProcess()` function launches `app-server-broker.mjs` as a detached child process. In `broker-lifecycle.mjs` at lines 59-66, the parent passes `--endpoint`, `--cwd`, and file paths for the PID and log files via command-line arguments. The parent immediately redirects the child's stdio to the log file to capture initialization errors and runtime diagnostics.

### 4. Readiness Polling

After spawning, `waitForBrokerEndpoint()` (lines 24-41 in `broker-lifecycle.mjs`) enters a retry loop attempting to connect to the generated endpoint. The function uses a default 2-second timeout per attempt, polling until the broker has successfully bound its socket or named pipe. This synchronizes the parent process with the broker's initialization without requiring complex locking mechanisms.

### 5. Session Persistence

Once the socket responds, session metadata—including the `endpoint`, `pidFile`, `logFile`, `sessionDir`, and `pid`—is serialized to `${stateDir}/broker.json` (lines 62-70). This persistence layer enables quick recovery of broker connections across plugin restarts and provides the necessary state for reuse detection.

### 6. Existing Session Reuse

Before creating new resources, `ensureBrokerSession()` calls `loadBrokerSession()` to check for existing [`broker.json`](https://github.com/openai/codex-plugin-cc/blob/main/broker.json) files. If a persisted session exists and `isBrokerEndpointReady()` confirms the endpoint is still responsive (lines 13-22), the plugin returns the existing broker instead of spawning a new process. This eliminates the overhead of repeated process creation for frequent operations within the same workspace.

### 7. Teardown and Resource Cleanup

When the plugin shuts down or encounters fatal errors, `teardownBrokerSession()` (lines 73-89) executes a coordinated cleanup sequence. The function optionally terminates the child process via a configurable `killProcess` callback, removes the PID file, deletes the log file, unlinks the Unix socket or named pipe, and finally removes the session directory. This ensures no orphaned resources remain on the filesystem.

### 8. Broker-Side Shutdown Handling

The broker executable itself handles graceful termination through signal handlers and JSON-RPC methods. In `app-server-broker.mjs` at lines 36-44, the process listens for `SIGTERM` and `SIGINT` signals. Additionally, it exposes the `broker/shutdown` JSON-RPC method (lines 60-64), which triggers a graceful closure of the client connection, server socket, and PID file removal before process exit.

## Key Implementation Files

The lifecycle is distributed across modular components:

- **`plugins/codex/scripts/lib/broker-lifecycle.mjs`**: Core orchestration utilities including `ensureBrokerSession()`, `spawnBrokerProcess()`, and `teardownBrokerSession()`.
- **`plugins/codex/scripts/lib/broker-endpoint.mjs`**: Platform-aware endpoint creation and parsing logic for `pipe:` and `unix:` transports.
- **`plugins/codex/scripts/app-server-broker.mjs`**: The standalone broker executable that manages the JSON-RPC server, connection forwarding, and shutdown handling.
- **`plugins/codex/scripts/lib/app-server.mjs`**: Provides `CodexAppServerClient` used by the broker to forward requests to the actual application server.
- **`plugins/codex/scripts/lib/state.mjs`**: Supplies `resolveStateDir()` for locating the persistent [`broker.json`](https://github.com/openai/codex-plugin-cc/blob/main/broker.json) storage location.

## Practical Usage Examples

### Obtaining and Managing a Broker Session

```javascript
import { ensureBrokerSession, teardownBrokerSession } from "./lib/broker-lifecycle.mjs";

async function getBroker(cwd) {
  const session = await ensureBrokerSession(cwd, {
    // optional overrides
    platform: process.platform,
    // custom kill function – uses `process.kill` by default
    killProcess: (pid) => process.kill(pid, "SIGTERM"),
  });
  if (!session) throw new Error("Failed to start broker");
  return session;          // { endpoint, pidFile, logFile, sessionDir, pid }
}

// Later, when done:
function closeBroker(session) {
  teardownBrokerSession({
    endpoint: session.endpoint,
    pidFile: session.pidFile,
    logFile: session.logFile,
    sessionDir: session.sessionDir,
    pid: session.pid,
    killProcess: (pid) => process.kill(pid, "SIGTERM"),
  });
}

```

### Requesting Broker Shutdown via JSON-RPC

```javascript
import net from "node:net";
import { parseBrokerEndpoint } from "./lib/broker-endpoint.mjs";

async function requestShutdown(endpoint) {
  const target = parseBrokerEndpoint(endpoint);
  const socket = net.createConnection({ path: target.path });
  socket.setEncoding("utf8");
  socket.write(`${JSON.stringify({ id: 1, method: "broker/shutdown", params: {} })}\n`);
  socket.end();
}

```

## Summary

- The **app server broker lifecycle** spans eight distinct phases from session creation to graceful shutdown.
- **Session isolation** is achieved through temporary directories created by `createBrokerSessionDir()` before process spawning.
- **Cross-platform transport** uses `pipe:` endpoints on Windows and `unix:` sockets on Unix systems via `broker-endpoint.mjs`.
- **Process coordination** relies on polling-based readiness detection in `waitForBrokerEndpoint()` rather than complex IPC mechanisms.
- **Resource reuse** is implemented through [`broker.json`](https://github.com/openai/codex-plugin-cc/blob/main/broker.json) persistence and `isBrokerEndpointReady()` checks in `ensureBrokerSession()`.
- **Cleanup guarantees** are provided by `teardownBrokerSession()`, which handles process termination, file deletion, and socket removal.

## Frequently Asked Questions

### How does the Codex plugin prevent multiple broker processes for the same workspace?

The plugin implements **session reuse** through `ensureBrokerSession()`, which first calls `loadBrokerSession()` to read existing [`broker.json`](https://github.com/openai/codex-plugin-cc/blob/main/broker.json) metadata. If the saved endpoint responds to `isBrokerEndpointReady()`, the function returns the existing session immediately instead of spawning a new process. This singleton pattern ensures only one broker mediates connections per workspace.

### What triggers the graceful shutdown of the app server broker?

The broker responds to three shutdown signals: the JSON-RPC method `broker/shutdown` (handled at lines 60-64 in `app-server-broker.mjs`), `SIGTERM`, and `SIGINT` (handled at lines 36-44). Upon receiving any of these, the broker closes its client connection, shuts down the server socket, removes its PID file, and exits cleanly. The parent process can also trigger termination via the `killProcess` callback passed to `teardownBrokerSession()`.

### Where does the plugin store broker session information?

Session metadata is persisted in `${stateDir}/broker.json`, where `stateDir` is resolved by the `state.mjs` module. This JSON file contains the endpoint string, PID file path, log file location, session directory, and process ID. The plugin uses this file to recover broker connections after restarts and to validate reuse eligibility before spawning new processes.

### How does the broker handle different operating systems?

The `broker-endpoint.mjs` module abstracts platform differences through `createBrokerEndpoint()`, which generates `pipe:` prefixed paths for Windows named pipes and `unix:` prefixed paths for Unix domain sockets. The `parseBrokerEndpoint()` function handles the inverse, allowing the rest of the lifecycle code to remain platform-agnostic while the transport layer adapts to the underlying operating system's IPC mechanisms.