You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On Windows, changing Gateway > Local client key and pressing Apply produced this sequence:
CodexHub persisted the new key, but the running Gateway continued accepting only the old key.
A subsequent Restart Gateway sent POST /shutdown with the newly persisted key to the still-running old-key Gateway, which returned 401; the lifecycle then reached its force-close fallback.
The replacement Gateway accepted the new key, while Codex Desktop continued sending the old managed experimental_bearer_token and received UnauthorizedLocalClient: missing or invalid local Gateway client key.
Re-authenticating Codex did not repair this local credential mismatch. Restarting the whole CodexHub app republished the managed Codex overlay, after which Codex Desktop requests succeeded.
Observed timeline on 2026-08-13 (Asia/Shanghai):
23:25:00 — new key persisted by CodexHub.
23:34:04 — old Gateway rejected /shutdown with 401.
23:34:14 — replacement Gateway started with the new key.
23:35–23:37 — the external DSH client already passed local Gateway authentication, while client_id=codex-app continued receiving UnauthorizedLocalClient.
23:38:04 — CodexHub app restarted.
23:40 onward — Codex Desktop requests returned 200.
No credential value is included here. Hash-only comparison after recovery confirmed that CodexHub settings, the DSH credential, and the managed Codex bearer projection were identical.
Root cause
This is one transaction split across three consumers:
Settings Apply persists gateway_client_key, but the frontend restart predicate omits it.
The Python Gateway reads CODEX_PROXY_GATEWAY_CLIENT_KEY only when its process starts.
Ordinary Settings Apply does not republish the managed Codex config overlay containing experimental_bearer_token; the full CodexHub startup/Official-refresh publication path does.
Stop/restart reads the current persisted key, not the key with which the running Gateway was launched, so the old process rejects authenticated shutdown after a key rotation.
Make a local Gateway key rotation one bounded, ownership-safe transaction. A successful Apply must not leave settings, the running Gateway, or any CodexHub-managed client on different key generations.
Required behavior
Treat a changed gateway_client_key as a running-Gateway restart requirement.
Stop the old owned Gateway using the credential generation with which that process was launched, while preserving the existing ownership verification and bounded force-close contract.
Start exactly one replacement Gateway with the new key.
Republish every CodexHub-managed client configuration that carries the local Gateway key, including Codex Desktop/CLI.
Read back the running Gateway/client projections before reporting Apply success.
If any stage fails, return a truthful partial/failed state with actionable recovery; do not claim that Apply completed.
Keep the local Gateway key distinct from upstream Provider credentials and never place key values in logs, telemetry, UI errors, or tests.
Do not require an OpenAI/Codex re-login for a local Gateway key rotation.
Acceptance criteria
Applying a changed local key while Gateway is running retires the old process, starts one replacement, and leaves settings/Gateway/PID/listener/UI on one reconciled generation.
The old process receives authenticated shutdown with its launch key; a changed persisted key does not cause a shutdown 401.
Codex Desktop/CLI and every enabled managed external client receive and read back the new local key generation before Apply reports success.
The first Codex Desktop request after successful Apply authenticates without restarting CodexHub or re-logging in.
A client that already has the new key can authenticate immediately after the replacement Gateway starts.
Failure injection covers settings write, old-process shutdown, replacement start, client publication, and readback; no path reports a false success.
UI/help text describes the real restart/synchronization behavior rather than claiming that key changes need no restart.
Verification
Focused frontend contract tests for the restart predicate and Apply result.
Rust lifecycle tests with distinct old/new key generations and a real authenticated shutdown fixture.
Managed Codex overlay plus external-client apply/readback tests.
Packaged Windows smoke: rotate the key with Codex Desktop and one external client configured, press Apply once, and prove both clients authenticate without restarting CodexHub.
Reporter evidence
On Windows, changing Gateway > Local client key and pressing Apply produced this sequence:
POST /shutdownwith the newly persisted key to the still-running old-key Gateway, which returned401; the lifecycle then reached its force-close fallback.experimental_bearer_tokenand receivedUnauthorizedLocalClient: missing or invalid local Gateway client key.Observed timeline on 2026-08-13 (Asia/Shanghai):
/shutdownwith 401.client_id=codex-appcontinued receivingUnauthorizedLocalClient.No credential value is included here. Hash-only comparison after recovery confirmed that CodexHub settings, the DSH credential, and the managed Codex bearer projection were identical.
Root cause
This is one transaction split across three consumers:
gateway_client_key, but the frontend restart predicate omits it.CODEX_PROXY_GATEWAY_CLIENT_KEYonly when its process starts.experimental_bearer_token; the full CodexHub startup/Official-refresh publication path does.Relevant seams on the current tree:
frontend/src/App.tsx:gatewayRuntimeSettingsChanged,saveSettings.src-tauri/src/config.rs:save_settings_with_paths, managed Codex overlay publication.src-tauri/src/proxy.rs:CODEX_PROXY_GATEWAY_CLIENT_KEY, stop/restart shutdown request.src-python/config_overlay.py: managedexperimental_bearer_token.Outcome
Make a local Gateway key rotation one bounded, ownership-safe transaction. A successful Apply must not leave settings, the running Gateway, or any CodexHub-managed client on different key generations.
Required behavior
gateway_client_keyas a running-Gateway restart requirement.Acceptance criteria
Verification
Related