openai / openai/codex-plugin-cc
feat: expose `disableBroker` through an env var — there is no supported way to opt out of the shared app-server broker
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 33.3k
- Forks
- 2.3k
- PR merge metrics
- No merged PRs in 30d
Description
feat: expose disableBroker through an env var — there is no supported way to opt out of the shared app-server broker
Repo: openai/codex-plugin-cc
Version: codex plugin 1.0.6
Files: scripts/lib/app-server.mjs, scripts/lib/codex.mjs
Summary
CodexAppServerClient.connect() already supports disableBroker: true, and it is a
documented field in the protocol types. But it is only reachable from inside the plugin —
there is no env var, config key, or CLI flag that lets an integrator say "never use the
shared broker for my session; spawn the app-server directly."
Callers who need strict per-job process lifetime are therefore forced to abuse
CODEX_COMPANION_APP_SERVER_ENDPOINT: point it at a path that can never be a socket
(e.g. unix:/nonexistent/codex-broker-disabled.sock) so that connect() skips
ensureBrokerSession(), fails with ENOENT, and lands in the existing direct-spawn
fallback. It works, but it is a semantic inversion — the env's meaning is "use this
broker", not "use no broker".
Why direct spawn matters
With the broker, the app-server (and every MCP server it starts) is a detached grandchild
whose lifetime is owned by the broker, not by the job. With disableBroker: true the
app-server is a direct child of the companion process, so closing the client reclaims the
whole subtree.
Measured on 1.0.6 with the dead-socket workaround in place: descendants go 4 → 18 during
thread.start, and back to 0 after close(). Without it, a single review run
(4 codex call sites) left 121 MCP processes parented to the broker, which also blocked
deletion of the git worktree they had been started in.
This is the same failure surface as #543 (brokers never self-terminate) and #540 (ending
one Claude session kills a broker other sessions are mid-turn on), but the ask is
different and complementary:
- #543 asks the broker to heal itself after it leaks. Useful, but the broker is still
started, and processes still live until the idle threshold elapses. - This issue asks for a supported way to never start one, which removes the class
outright for integrators that do not want cross-session sharing.
Current behaviour (1.0.6)
scripts/lib/app-server.mjs:335-353:
export class CodexAppServerClient {
static async connect(cwd, options = {}) {
let brokerEndpoint = null;
if (!options.disableBroker) {
brokerEndpoint = options.brokerEndpoint ?? options.env?.[BROKER_ENDPOINT_ENV] ?? process.env[BROKER_ENDPOINT_ENV] ?? null;
...
if (!brokerEndpoint && !options.reuseExistingBroker) {
const brokerSession = await ensureBrokerSession(cwd, { env: options.env });
brokerEndpoint = brokerSession?.endpoint ?? null;
}
}
...
options.disableBroker is reachable only from in-tree callers
(scripts/lib/codex.mjs:635, codex.mjs:645, scripts/app-server-broker.mjs:68).
Nothing maps an environment variable onto it, and scripts/lib/app-server-protocol.d.ts:55
declares disableBroker?: boolean; with no public entry point.
Proposal
Add a boolean env var alongside the existing CODEX_COMPANION_APP_SERVER_* family
(_ENDPOINT, _PID_FILE, _LOG_FILE), e.g. CODEX_COMPANION_APP_SERVER_DISABLE_BROKER.
When truthy, connect() behaves as if disableBroker: true was passed.
export const BROKER_DISABLE_ENV = "CODEX_COMPANION_APP_SERVER_DISABLE_BROKER";
function brokerDisabledByEnv(env) {
const raw = env?.[BROKER_DISABLE_ENV] ?? process.env[BROKER_DISABLE_ENV];
return raw === "1" || raw === "true";
}
export class CodexAppServerClient {
static async connect(cwd, options = {}) {
let brokerEndpoint = null;
const disableBroker = options.disableBroker || brokerDisabledByEnv(options.env);
if (!disableBroker) {
// unchanged
}
...
Explicit options.disableBroker keeps precedence, so no in-tree caller changes behaviour.
A config-file key instead of (or in addition to) the env var would work equally well — the
requirement is only that it be a supported, named toggle rather than a malformed endpoint.
Why the workaround is not a stable substitute
The dead-socket trick depends on three implementation details that can change without any
visible error — it would silently start leaking brokers again on a plugin auto-update:
BROKER_ENDPOINT_ENVbeing renamed.- The resolution order in
connect()changing so a set-but-unreachable endpoint no longer
short-circuitsensureBrokerSession(). - A well-intentioned fix that treats an unreachable configured endpoint as "stale" and
falls through toensureBrokerSession()instead of the direct-spawn fallback in
codex.mjs:612-641.
Each of those is a reasonable change on its own. A named flag makes the intent explicit and
survives all three.
Related
#543 (broker idle self-exit), #540 (SessionEnd kills shared broker mid-turn),
#526 (client/idle-shutdown race). Not a duplicate of any — those harden the shared broker;
this one asks for a supported way to not use it.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read scripts/lib/app-server.mjs:335-353 and the existing callers in scripts/lib/codex.mjs:635 and codex.mjs:645; compare how environment values are resolved with options.disableBroker. Confirm the existing direct-spawn fallback in codex.mjs:612-641, then verify that the new named environment setting skips broker startup while preserving explicit options.disableBroker behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100