feat(desktop): codex attach-rung resolver (L4a) - #568
Merged
Conversation
L4's plan line specified a WebSocket attach to a `codex app-server`
daemon, authenticated with a bearer scheme, with stdio spawn-per-session
as "the fallback rung only". Measured against the installed codex-cli
0.147.0, all three parts are wrong, so this lands the corrected decision
as a pure, tested resolver before any driver is written against it.
- The shared-daemon transport is a UNIX DOMAIN SOCKET, not a
WebSocket, and there is no bearer scheme: `codex app-server daemon
start` brings the daemon up, `codex app-server proxy --sock <path>`
pipes stdio to its control socket at
<CODEX_HOME>/app-server-control/app-server-control.sock. The
`--code-mode-host <WS_URL>` flag is a different feature (where code
mode runs), not the session transport. The attach rung therefore
needs no WebSocket client and no token — it is the same
JSON-RPC-over-stdio we already speak, pointed at another process.
- "Spawn it detached if absent" is not available. `daemon start`
refuses unless codex was installed by the official installer script,
wanting the managed binary at
<CODEX_HOME>/packages/standalone/current/codex. An npm / homebrew /
distro codex — including this machine's — has no daemon rung at all.
- So the rung order inverts: per-session stdio is not a fallback, it
is the only rung for the common install, and the daemon is an
opportunistic upgrade.
Also measured: the control socket is subject to the platform SUN_LEN cap
(~104-108 bytes). A deeply relocated CODEX_HOME fails at connect time
with "path must be shorter than SUN_LEN", which nobody would attribute
to path length — so the resolver disqualifies the daemon rung up front
and returns a reason saying so. CODEX_HOME relocation is honoured for
both the socket and the managed-binary probe, the same way F4 honours
CLAUDE_CONFIG_DIR.
The probe (does the managed binary exist?) is an input, so the whole
decision table is unit-testable with no codex on the box. 7 tests, 5
mutations checked, all killed. Electron suite 634 pass / 0 fail, tsc and
doc lints green.
L4b — the app-server JSON-RPC driver over whichever argv this returns,
delta throttling, R1 approval cards, turn/interrupt, config seeding —
is still to do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dev machine was moved from the npm codex to the installer-managed
standalone, which makes the daemon rung reachable here for the first
time. Exercising it confirms both paths the L4a resolver computes,
exactly: `daemon start` reports managedCodexPath =
<CODEX_HOME>/packages/standalone/current/codex and socketPath =
<CODEX_HOME>/app-server-control/app-server-control.sock. The socket is
created srw------- — filesystem permissions are the auth, the positive
form of "there is no bearer scheme".
Records two things L4b must not inherit as assumptions:
- The proxy's DATA path is unproven. `codex app-server proxy --sock`
connects and exits 0 with no stdout and no stderr for the same
initialize handshake that works over a plain `codex app-server`
child, stdin held open. Candidates: the daemon may need
`enable-remote-control` (undocumented in its help, and the sibling
`bootstrap` frames it as SSH-driven, so it is a security-posture
call for the director, not a default), or the control socket may
want a framing the bare JSON-RPC line does not supply. The resolver
decides WHICH ARGV, never THAT IT SUCCEEDS.
- Discoverability regressed: the standalone codex lives at
~/.local/bin/codex with its PATH entry in .bashrc, which a
GUI-launched Electron app never sources — less discoverable than the
/usr/bin/codex package was. L4b should resolve the binary the way
kimiweb.ts already does for kimi, not trust PATH.
Doc-only; no version bump.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #567, which was based on the F4 branch and therefore got zero CI — this repo's workflows are
pull_request: branches: [main], so a stacked PR is never verified. Now based onmain(which has both #565 and #566).L4's plan line specified a WebSocket attach to a
codex app-serverdaemon, authenticated with a bearer scheme, with stdio spawn-per-session as "the fallback rung only".Measured against the installed codex-cli 0.147.0, all three parts are wrong. This lands the corrected decision as a pure, tested resolver before any driver is written against it — building the WebSocket client the plan describes would have been a dead end.
What codex actually ships
The shared-daemon transport is a Unix domain socket, not a WebSocket, and there is no bearer scheme.
codex app-server daemon startbrings the daemon up;codex app-server proxy --sock <path>pipes stdio to its control socket at<CODEX_HOME>/app-server-control/app-server-control.sock. The--code-mode-host <WS_URL>flag that does exist is a different feature (where code mode runs), not the session transport. So the attach rung needs no WebSocket client and no token — it's the same JSON-RPC-over-stdio we already speak, pointed at another process."Spawn it detached if absent" isn't available.
daemon startrefuses unless codex was installed by the official installer script, wanting the managed binary at<CODEX_HOME>/packages/standalone/current/codex:An npm / homebrew / distro codex — including this machine's
/usr/lib/node_modules/@openai/codex— has no daemon rung at all.The rung order inverts. Per-session stdio isn't a fallback; for the common install it's the only rung. The daemon is an opportunistic upgrade when the managed install is present.
Also measured: the control socket is subject to the platform
SUN_LENcap (~104–108 bytes). A deeply relocatedCODEX_HOMEfails at connect time withpath must be shorter than SUN_LEN— a message nobody would attribute to path length — so the resolver disqualifies the daemon rung up front and returns a reason saying why.CODEX_HOMErelocation is honoured for both the socket and the managed-binary probe, the same way #566 honoursCLAUDE_CONFIG_DIR.(Incidentally: the CLI here is 0.147.0, not the 0.133.0 E3 was written against — it moved under us mid-plan.)
Scope
The install probe is an input, so the whole decision table is unit-testable with no codex on the box. Every rung returns a
reasonsentence: a user whose session isn't shared with their TUI should be able to find out why without reading code.7 tests, 5 mutations checked, all killed. Electron suite 635 pass / 0 fail,
tscclean, doc lints green.L4b is still to do — the app-server JSON-RPC driver over whichever argv this returns, delta throttling, R1 approval cards,
turn/interrupt, config seeding.