Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
b116468
feat(codex): prepare protected Windows desktop compatibility lifecycle
luvs01 Sep 27, 2026
e014a1b
feat(codex): add local certificate setup and trust management
luvs01 Sep 27, 2026
da36d50
test(cli): isolate remote connect lifecycle lock
luvs01 Sep 27, 2026
3569da4
feat(codex): add recoverable compatibility certificate renewal
luvs01 Sep 27, 2026
81d6f6a
feat(codex): wire bounded desktop compatibility runtime
luvs01 Sep 27, 2026
bf12705
feat(gui): add native desktop compatibility controls
luvs01 Sep 27, 2026
fea82e1
feat(codex): preserve desktop compatibility endpoints across restarts
luvs01 Sep 27, 2026
cf3d0dd
feat(codex): resume optional desktop observation on proxy startup
luvs01 Sep 27, 2026
2920779
feat(gui): persist desktop observation startup preference
luvs01 Sep 27, 2026
973ca48
fix(codex): disarm desktop compatibility when native context changes
luvs01 Sep 27, 2026
f43a710
feat(codex): support proxy-aware desktop HTTP and upgraded traffic
luvs01 Sep 27, 2026
49d8bf6
Merge current dev into desktop compatibility integration
luvs01 Sep 27, 2026
e17f0ed
fix: recognize encoded Windows standalone module roots
luvs01 Sep 27, 2026
8f2b1ea
docs(gui): clarify managed desktop reconnection after updates
luvs01 Sep 27, 2026
3550dd0
Merge dev configuration healing before compatibility review
luvs01 Sep 27, 2026
1197c32
test: keep compatibility test mappings within the size ratchet
luvs01 Sep 27, 2026
d30de45
test: document desktop compatibility in GUI CLI inventory
luvs01 Sep 27, 2026
b2aff91
fix(codex): address desktop compatibility review findings
luvs01 Sep 27, 2026
d6bd88d
fix(codex): preserve unknown Windows relaunch context
luvs01 Sep 27, 2026
dc20960
fix(codex): fence desktop usage trials to native credential generation
luvs01 Sep 27, 2026
9068502
test(codex): assert desktop identity verification requests
luvs01 Sep 27, 2026
f122718
fix(codex): revoke compatibility trials after recovery or routing cha…
luvs01 Sep 28, 2026
6103a93
merge: integrate current dev into desktop compatibility
luvs01 Sep 28, 2026
51db026
fix(codex): constrain desktop compatibility CA to server authentication
luvs01 Sep 28, 2026
e999957
fix(codex): bind desktop compatibility to listener address family
luvs01 Sep 28, 2026
a19eb98
fix(gui): clarify pending native cache refresh after trial
luvs01 Sep 28, 2026
89f261b
fix(codex): retain desktop observation across MCP refreshes
luvs01 Sep 28, 2026
5e2370d
fix(codex): await native config readiness before desktop observation
luvs01 Sep 28, 2026
b8c38ac
test(codex): verify attachment passthrough over desktop relay TLS
luvs01 Sep 28, 2026
31084d5
fix(codex): attest compatibility PAC ownership across restart
luvs01 Sep 28, 2026
904cb8e
fix(codex): distinguish relay observations from composer recovery
luvs01 Sep 28, 2026
4c6f745
fix(codex): validate assessed package identity and host casing
luvs01 Sep 28, 2026
f8c611a
merge: synchronize desktop compatibility with current dev
luvs01 Sep 29, 2026
124c741
fix(codex): consume compatibility exhaustion evidence per trial
luvs01 Sep 29, 2026
4c79701
Merge dev into desktop compatibility PR, preserving both integration …
luvs01 Oct 1, 2026
56d6445
Merge latest dev into desktop compatibility lifecycle
luvs01 Oct 1, 2026
dd67f7e
Merge dev into desktop compatibility lifecycle
luvs01 Oct 2, 2026
822a4a9
docs(desktop): include renewal-required certificate state
luvs01 Oct 2, 2026
bfd76c9
Merge dev into desktop compatibility lifecycle
luvs01 Oct 2, 2026
791e2f4
Merge remote-tracking branch 'refs/remotes/lidge-jun/dev' into repair…
luvs01 Oct 3, 2026
fa9de51
fix: restore merged desktop compatibility docs and locale parity
luvs01 Oct 3, 2026
b92c249
fix(desktop-compatibility): pass oversized usage SSE records through
luvs01 Oct 3, 2026
3cac6c1
Merge remote-tracking branch 'upstream/dev' into repair/pr-6079
luvs01 Oct 3, 2026
e5354c9
Merge lidge-jun dev into PR 6079 branch
luvs01 Oct 3, 2026
0bed129
Merge lidge-jun dev into PR 6079 branch
luvs01 Oct 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Bun-native TypeScript with no separate server compile step.
through `tests/helpers/repo-root.ts` (`repoRoot()`, `repoPath()`,
`helperPath()`, `fixturePath()`), never `import.meta.dir + "/.."`. A new
test file lands in its domain directory and needs an entry in both
`layout.json` `explicit` and `tests/fixtures/test-layout-expected.json`
`layout.json` `explicit` and a `tests/fixtures/test-layout-expected*.json` shard
(`tests/test-layout-tooling.test.ts` names the missing one); the regex
seeds in `seeds.json` place a conventionally named file until then.
History: `devlog/_fin/260905_test_modularization_and_windows/`.
Expand Down Expand Up @@ -268,7 +268,7 @@ fails `file-size ratchet: repository` for your branch and for every branch cut f
Two branches can each stay under a cap alone and sum over it together; that is what
happened in #4908, #5011 and #5018. The remedy is always a move, never a number: put the
new case in a sibling file, byte for byte, and register it in **both**
`scripts/test-layout/layout.json` and `tests/fixtures/test-layout-expected.json`.
`scripts/test-layout/layout.json` and a `tests/fixtures/test-layout-expected*.json` shard.
`d3ca5522db` is the original precedent.

A moved test is not automatically the same test. One case moved out of
Expand Down
127 changes: 126 additions & 1 deletion docs-site/src/content/docs/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,131 @@ without requiring a separate Bun installation or a source checkout.
Use `ocx codex-shim status` to inspect it and `ocx codex-shim uninstall` to restore
the saved Codex launcher.

## Experimental Windows desktop compatibility

**Codex Set → Desktop compatibility** provides local controls for a bounded compatibility
trial while retaining the native Codex login. This is an experimental relay, not a guarantee
that every Codex build or exhausted-account state can recover its composer.

1. Inspect status, prepare the encrypted certificate and review its fingerprint before
registering trust. Windows may ask for confirmation. Trust applies to the current Windows
user and allows the local relay to handle `chatgpt.com` traffic.
2. Start observation. Save drafts, close Codex, then use **Open Codex** in this panel to launch
the packaged app with the managed connection. The panel never forcibly closes an existing app.
3. Only after eligible exhaustion is observed, **Run 3-minute trial** offers a separate account-wide
consent. It adjusts two UI flags across the signed-in account, not just the selected model.
Actual usage, credits, spending restrictions and server limits remain unchanged. A produced
response alone does not establish that the composer recovered.

An original response showing available usage or a protected state ends the trial immediately.
If eligible exhaustion appears again, another explicit trial is required; the earlier consent
does not silently reactivate correction.

**Return to observation** disarms correction. The three-minute limit bounds response correction,
not the time until Codex refreshes its display. Expiry and returning to observation request a
fresh usage response; the app may retain its previous display until that response arrives.
The panel shows this limitation before trial consent and while awaiting the original response;
it cannot confirm the app's cache refresh. **Stop service** closes its connections and leaves
the certificate for reuse; active connections may be interrupted. For renewal or trust removal,
stop the service and close Codex first. Renewal prepares a new untrusted certificate; review the
new fingerprint before registering it. An uncertain action is not automatically retried.
Certificates are restricted to TLS server authentication. If status shows `renewal-required`,
stop the service and close Codex, then renew the older certificate before starting again.
The native API URL must match the proxy's actual listener address and port. IPv4 and IPv6
loopback addresses are not interchangeable here; ambiguous `localhost` aliases are refused.
Codex's automatic MCP connection refresh and configuration formatting do not invalidate
observation. Model, authentication, profile and routing changes still require a fresh
observation context; the service does not silently adopt those changes during a trial.

The service preserves its PAC address and local connection ports for reuse after a restart.
With the same trusted certificate, an already configured app can reconnect through its cached
PAC; restarting the service never resumes a correction trial. If a saved port cannot be bound
or connection metadata is invalid, startup refuses rather than silently assigning another
address. The first successful start owns `codex-desktop-compatibility/connection.json` under
the OpenCodex home. Do not delete that file as a routine restart fix: a different address
requires launching the app with a fresh managed connection.

Current support is limited to the assessed Windows build `26.924.2738.0`, native file-based
login. HTTP/HTTPS proxies and authenticated SOCKS5 proxies are supported through the shared
outbound policy; NO_PROXY can explicitly select a direct route. Failed proxy connections do not
fall back to direct egress. OpenCodex managed client mode is not yet supported by these controls.
For this desktop relay, HTTP(S) `ALL_PROXY` also applies when `HTTPS_PROXY` is unset. An invalid
`HTTPS_PROXY` refuses the operation instead of falling back to `ALL_PROXY`.
Unknown app builds refuse correction; the integration does not patch application files or
switch to login-free mode.
The native root config and its selected profile must route `openai_base_url` to this process's
actual local data listener. A different provider, destination or unsupported override refuses
observation startup. This check does not determine a conversation's selected model or prove
that project-local overrides are absent. If routing or the installed app build changes during
a trial, correction is refused before the next usage response; periodic checks also return
an idle trial to observation. Original responses and other app traffic continue to relay.
Native login-file replacement or token rotation also invalidates the trial, even when the
same account returns. Stop and start observation to bind the current login before another
explicit trial; old responses cannot authorize the replacement session.
Changes to the root Codex config (except its MCP server table and formatting), provider configuration, model aliases, routing profiles,
fallback, compaction or memory-model routes also invalidate the observation context. Restoring the previous
settings does not restore its consent: stop and start observation, then confirm a new trial
when eligible. This detects changes to those configured routes, not which model a conversation
actually selects; an unchanged mixed-provider configuration is not proof of provider isolation.

After certificate preparation and a successful manual start, enable **Resume observation when
OpenCodex starts** in the same panel. The optional OpenCodex configuration
`"desktopCompatibility": { "startOnProxyStart": true }` resumes **observation only** when the model
proxy starts. Omission or `false` disables this automatic start. It reuses the saved endpoints
and trusted certificate, never launches Codex or enrolls trust, and never resumes an Apply trial.
Automatic observation waits for startup configuration sync to finish. Failed sync or a wait
longer than two minutes leaves observation off; inspect its status and start it manually after
resolving the startup problem. The model proxy continues to follow its own readiness policy.
If prerequisites are missing, observation stays off and the model proxy continues running.
Saving this preference does not start or stop the current service. Use its separate service
controls for immediate changes. If a save cannot be confirmed, refresh its status before retrying.

A running service does not establish that Codex is connected through it. A normal app launch or
an app updater can omit the managed connection argument. Save drafts, close Codex, then use
**Open Codex** in this panel to restore that connection. This control never closes the app for you.
After a Codex update, an unassessed app version refuses correction until its compatibility is
reviewed; reinstalling the certificate does not make an unassessed build supported.

On Windows, an explicit OpenCodex full-app restart preserves an already active loopback
compatibility PAC argument only when the current OpenCodex process owns the serving compatibility
runtime. A stale or foreign endpoint is refused before stopping Codex; replacement of the runtime
during restart also prevents managed relaunch. The app launches through Windows package activation. It checks the
package identity and routing argument after launch; unreadable or conflicting main-app routing arguments cause
a refusal before the restart. This does not enable a compatibility mode, install a certificate,
or watch and restart the app automatically. Normal launches without that routing argument keep
their existing behavior.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Transport coverage and recovery evidence

The compatibility panel reports identity-checked JSON/SSE usage responses and currently
bound streams separately from open connections. These are lifetime relay observations,
including requests made by diagnostic clients. They do not identify the sending native
process, prove that the app updated its authoritative cache, or prove composer recovery.
No request body, token, account ID or source IP is added to this diagnostic status.

[Issue #6196](https://github.com/lidge-jun/opencodex/issues/6196) reports a macOS build whose
authoritative gate requests originate in the bundled Rust app-server and bypass Chromium
PAC settings. A healthy PAC, certificate or synthetic request cannot establish coverage
of that transport. This Windows experiment is not a fix for that macOS design blocker.

Before adding another platform or transport, document and validate this sequence:

1. Identify the actual process and transport that sends each authoritative gate request.
2. Prove that an app-origin request reaches the intended relay, separately from test clients.
3. Verify the response schema and the app's cache/update path for the exact installed build.
4. On natural account exhaustion, verify original-composer submission, independent-provider
completion, and preservation of sign-in, Chat, existing threads and remote connections.
5. Verify shutdown, ordinary launch, restart and update behavior; do not infer one from another.

The present policy handles only `/backend-api/wham/usage` and its `/stream` endpoint.
Conversation initialization and other endpoints, including their `blocked_features` and
`limits_progress`, pass unchanged. Their presence is not permission to clear them: they may
represent unrelated restrictions. The Windows build's renderer-to-Electron fetch path does
not establish the network path of a different OS/build or every app-server RPC. App-server
routing requires its own reviewed design; changing model-provider URLs alone is not proof
that account, login or remote-control traffic follows the same route. No automatic app-server
wrapper, global proxy change or login rewrite is installed by these controls.

## Routed models during Codex reserve mode

Codex Pool can optionally protect stored pool accounts at a selected 5-hour or weekly usage
Expand All @@ -974,7 +1099,7 @@ When the ChatGPT 5-hour quota is exhausted, Codex may offer a reserve fallback m
**every other entry unselectable — including opencodex routed models**, even though those
run on independent providers and credentials and consume none of the exhausted quota.

**This is a Codex client behavior and the proxy cannot change it.** The reserve state
**This is a Codex client behavior that ordinary model-proxy routing cannot change.** The reserve state
arrives from the ChatGPT backend on the client's own authenticated connection, not through
the proxy. The desktop app polls `backend-api/wham/usage` and treats reserve as active when
the response carries `rate_limit_upsell.banner_type = "luna_reserve"`, the primary
Expand Down
78 changes: 78 additions & 0 deletions docs-site/src/content/docs/reference/management-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ route-specific results rather than repeating this table.
| `POST /api/anthropic/reset-grants/consume` | Spend one reset grant. Body `{ accountId, grantId, operationId }`; `operationId` is a UUIDv4 sent upstream as the request ID, so repeating it retries the same claim. Requires a dashboard session. | 400 invalid body; 401 re-authentication needed; 403 `session_required`; 409 `grant_not_usable`, `in_flight`, `unresolved_prior_operation`, `unknown_outcome_expired`, `operation_identity_mismatch`; 500 `journal_write_failed`; 502 `unknown_outcome`; 503 journal busy, unavailable, or full |
| `GET, PUT /api/claude-desktop` | Read or persist the Claude Desktop routed/native profile | 400 invalid or unavailable assignment |
| `POST /api/claude-desktop/apply` | Write the saved profile to Claude Desktop's managed config | 400/500 write failure |
| `GET, POST /api/codex/desktop-compatibility/certificate` | Inspect or explicitly prepare Windows compatibility certificate trust | 403 local dashboard required for POST; 409 stale, busy, or incomplete state |
| `GET, POST /api/codex/desktop-compatibility/runtime` | Inspect or explicitly start, stop, launch, or run a bounded native compatibility trial | 403 local dashboard required for POST; 409 unsupported build, untrusted certificate, or incomplete state |
| `GET, POST /api/codex/desktop-compatibility/settings` | Read or save the next-start observation preference with its current revision | 403 local dashboard required for POST; 409 stale, invalid, or unconfirmed configuration |
| `GET /api/claude-desktop/status` | Inspect saved-versus-applied profile and Desktop health | 400 status read failure |
| `GET, PUT /api/claude-code` | Read or update Claude Code gateway, auth-mode, model-map, context, agent, and sidecar settings | 400 invalid field or shape |

Expand Down Expand Up @@ -736,6 +739,81 @@ provider, account, path, or credential details; clients receive only the complet
an account retains its selector binding so exact routes fail closed while the account is absent and the
same selector is restored if that account id is added again.

## Windows compatibility certificate setup

`GET /api/codex/desktop-compatibility/certificate` returns public certificate status:
support, state, fingerprint, expiry, renewal notice, trust observation and any active operation.
It never generates a key, decrypts private material or registers OS trust. Certificate trust alone
does not mean a compatibility relay is running or the private key has been verified in this process.

POST accepts `action: "prepare" | "trust" | "remove-trust" | "renew"` and `confirmed: true`.
Trust actions also require the exact uppercase SHA-256 `fingerprint` returned by status.
These mutations require a GUI-session principal from trusted loopback ingress; an admin token alone
receives 403. They are intended for a local confirmation flow, not unattended certificate enrollment.

Prepare stores a constrained 30-day authority with a Windows CurrentUser-DPAPI-protected private key.
Subsequent preparations reuse it. Trust actions never create missing state or silently replace an
expired root. Removal refuses while Codex is running or process ownership cannot be established.
Cancellation or uncertain OS command completion is checked against the actual certificate store.
Unknown state remains an error rather than authorizing an automatic retry. Replies contain no PEM,
private keys, account data or subprocess output.

Renewal requires the current fingerprint and an absent Codex app. It verifies removal of
the old certificate's trust before publishing a validated encrypted replacement. Refused
or uncertain removal preserves the old identity. The replacement is prepared but untrusted;
register it with a separate `trust` confirmation using its new fingerprint. A stale retry
cannot replace the new identity again. No certificate backup chain is retained.

This setup API does not enable a relay, change login, alter usage, restart Codex or install a watcher.
CurrentUser protection does not isolate secrets from other processes running as the same OS user.

## Experimental Windows compatibility runtime

`GET /api/codex/desktop-compatibility/runtime` is an inert status read. POST requires a
local GUI session and `{ action, confirmed: true }`, where action is `start`, `stop`,
`observe`, `launch`, or `apply`. Only `apply` additionally requires `accountWideConsent: true`.
The endpoint is experimental. The dashboard exposes it under **Codex Set → Desktop compatibility**;
the **Resume observation when OpenCodex starts** toggle saves the optional
`desktopCompatibility.startOnProxyStart` preference. OpenCodex managed client mode does not offer these
local controls or forward them to the shared hub.

Start requires an already prepared, trusted certificate, a freshly verified native file-based
ChatGPT login and the assessed Codex Windows build `26.924.2738.0`. It begins in Observe mode.
HTTP/HTTPS and authenticated SOCKS5 outbound proxies use the same explicit selection for HTTP,
identity verification and upgraded sockets. NO_PROXY is honored; failures never retry directly.
An invalid or unsupported setting returns `egress_proxy_invalid`. Start never registers a certificate or changes
login. Launch preserves package identity and refuses an app that is already running.
PAC and CONNECT ports are persisted after successful first binding and reused on restart.
`connection_unavailable` means binding failed; `connection_invalid` or `connection_changed`
means stored endpoint identity could not be accepted. These failures never silently rotate
ports or overwrite the existing connection record. Restart returns to Observe, not Apply.
`native_routing_unverified` means the native root/profile routing cannot be matched to this
process's bound OpenCodex listener. Runtime status may expose `contextFailure` for this condition
or `build_unverified`. These states disable response correction without claiming an app repair.

Apply requires a recently observed eligible exhaustion snapshot and lasts at most three minutes
at the response layer. It changes two account-UI gate flags, not usage percentages, credits,
spending restrictions or server limits. The usage response does not identify the selected model:
this trial cannot promise an effect limited to external models. An accepted activation or produced
response does not prove the app accepted the new snapshot or enabled its composer.
An available or protected original usage record returns the controller to Observe and
invalidates pending corrections. Later exhaustion requires another explicit Apply request.
Changed root TOML or configured model/provider/fallback routing invalidates the runtime's
observation context until stop/start; reverting those settings does not reactivate it.

Observe disarms correction and refreshes only validated usage streams. Stop closes this runtime's
listeners and connections; the PAC includes `DIRECT` fallback. The certificate stays installed for
reuse. Stop the runtime and close Codex before removing trust or renewing the certificate.
Failed cleanup reports `cleanup-required`; it does not claim that the runtime is off. OS login,
system proxy settings and application files are not modified by these runtime commands.

The separate settings endpoint returns `{ startOnProxyStart, revision }`. POST requires
`{ startOnProxyStart: boolean, revision, confirmed: true }` from a local GUI session. A stale
revision refuses the write; a valid update preserves unrelated config fields and verifies
the persisted result. This preference affects the next proxy start, never current runtime
state, app launch or an Apply trial. An uncertain response must be followed by GET, not an
automatic POST retry. Invalid or missing configuration is preserved rather than recreated.

## Choosing a client

For ordinary administration, the [Web Dashboard](/guides/web-dashboard/) gives the safest guided
Expand Down
Loading
Loading