# How Process Spawning and Termination Works for the Codex App-Server in openai/codex-plugin-cc

> Learn how the Codex app server spawns child processes using nodejs child_process spawn and terminates them gracefully with SIGTERM or taskkill.

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

---

**The Codex app-server is launched as a child process using `node:child_process.spawn` and shut down through a graceful sequence that sends `SIGTERM` on POSIX systems or executes `taskkill` on Windows to eliminate the entire process tree.**

The `openai/codex-plugin-cc` repository manages the Codex app-server lifecycle through a specialized client abstraction that handles process spawning and termination across platforms. This implementation ensures that when the client closes, no orphaned processes remain, utilizing platform-specific APIs to guarantee complete cleanup on Linux, macOS, and Windows.

## Spawning the Codex App-Server

### Process Initialization

The spawning logic resides in `SpawnedCodexAppServerClient.initialize()` within [`plugins/codex/scripts/lib/app-server.mjs`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/app-server.mjs#L89-L96). This method uses `node:child_process.spawn` to execute the `codex app-server` binary with the current working directory and the caller’s environment.

The implementation configures the child process to stream data line-by-line:

- **stdout**: Consumed as JSON-L (JSON Lines) format for structured message parsing
- **stderr**: Collected into a buffer for error reporting and diagnostics

This creates a bidirectional communication channel where the parent process can send requests and receive structured responses from the app-server.

## Terminating the Codex App-Server Process

### Graceful Shutdown Sequence

The termination protocol is implemented in `SpawnedCodexAppServerClient.close()` at [`app-server.mjs` lines 44-63](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/app-server.mjs#L44-L63). The method follows a specific shutdown sequence:

1. Ends the stdin stream to signal the child process to terminate gracefully
2. Waits 50 milliseconds for voluntary exit
3. If the process persists, executes platform-specific termination

### POSIX Process Termination

On POSIX platforms (Linux and macOS), the code calls `process.kill("SIGTERM")` to request termination. As implemented in [`process.mjs` lines 100-108](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/process.mjs#L100-L108), this attempts to terminate the process group, allowing the operating system to clean up related child processes.

### Windows Process Tree Termination

Windows requires special handling to prevent orphaned processes. The system invokes `terminateProcessTree(pid)` from [`process.mjs` lines 66-78](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/process.mjs#L66-L78), which executes the native command:

```bash
taskkill /PID <pid> /T /F

```

The `/T` flag ensures the entire process tree terminates, while `/F` forces the action. If `taskkill` is unavailable or fails, the code falls back to `process.kill(pid)` at [`process.mjs` lines 81-91](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/process.mjs#L81-L91). The helper recognizes "process not found" messages and reports whether the kill was delivered or merely attempted.

## Cross-Platform Process Termination Implementation

The `terminateProcessTree` function in `plugins/codex/scripts/lib/process.mjs` provides the core abstraction for cross-platform termination. This module also exports supporting utilities including `runCommand`, `runCommandChecked`, and `binaryAvailable`, which the termination logic uses to verify `taskkill` availability on Windows before execution.

The entire lifecycle is encapsulated behind the public API `CodexAppServerClient.connect(...).close()`, meaning callers interact with a simple JavaScript interface while the library manages all OS-specific details internally.

## Practical Usage Examples

### Standard Client Lifecycle

The typical workflow abstracts the spawning and termination complexity:

```javascript
import { CodexAppServerClient } from "./plugins/codex/scripts/lib/app-server.mjs";

async function run() {
  // Spawns the codex app-server process
  const client = await CodexAppServerClient.connect(process.cwd());

  // Interact with the server via JSON-L protocol
  // await client.request(...);

  // Graceful shutdown with automatic process tree cleanup
  await client.close();
}
run();

```

### Manual Process Termination

For scenarios requiring direct termination of a specific process ID (such as error recovery or external process management), you can invoke the termination helper directly:

```javascript
import { terminateProcessTree } from "./plugins/codex/scripts/lib/process.mjs";

// pid obtained from a previous spawn operation
await terminateProcessTree(pid);

```

## Summary

- **Spawning**: The `CodexAppServerClient` uses `node:child_process.spawn` in `app-server.mjs` to launch the binary, capturing stdout as JSON-L and stderr for error reporting.
- **Graceful Termination**: The `close()` method first ends stdin, waits 50ms, then escalates to forceful termination if the process remains alive.
- **Platform Handling**: POSIX systems receive `SIGTERM`, while Windows uses `taskkill /T /F` via `terminateProcessTree()` to eliminate entire process trees.
- **Fallback Mechanisms**: If native Windows tools fail, the code falls back to Node.js `process.kill()`, checking for "process not found" errors.
- **Developer Abstraction**: All complexity is hidden behind `CodexAppServerClient.connect().close()`, requiring no platform-specific knowledge from callers.

## Frequently Asked Questions

### How does the Codex plugin prevent orphaned processes on Windows?

The plugin uses `terminateProcessTree()` in `process.mjs` to execute `taskkill /PID <pid> /T /F`, where the `/T` flag instructs Windows to terminate the entire process tree. If this native command fails, it falls back to `process.kill(pid)` before reporting the termination status.

### What is the timeout duration before forceful termination occurs?

The implementation waits **50 milliseconds** after closing the stdin stream before initiating forceful termination. This brief window allows the app-server to perform voluntary cleanup, after which the code sends `SIGTERM` (POSIX) or executes `taskkill` (Windows).

### Which source file contains the platform-specific termination logic?

The cross-platform termination logic lives in `plugins/codex/scripts/lib/process.mjs`. This file handles the `taskkill` invocation for Windows (lines 66-78), the fallback to `process.kill()` (lines 81-91), and POSIX process group termination (lines 100-108).

### How is the app-server process output consumed by the client?

The client reads the spawned process's stdout line-by-line as JSON-L (JSON Lines) format, parsing each line as a discrete message. Stderr is accumulated into a buffer for error reporting, allowing the parent process to distinguish between structured protocol messages and diagnostic error output.