Repository navigation
Headless hub: WebUI cannot reauth native main (deviceauth is pool-only) #3898
Description
Area
Dashboard / Authentication and account pool
What are you trying to accomplish?
Reauthenticate the native Codex App login (__main__, $CODEX_HOME/auth.json) from the OpenCodex WebUI on a headless hub (Docker/k3s, no local Codex App, oauthOpenBrowser: false).
This is not client pairing (ocx connect / pairing grant). Pairing only attaches a client to the hub. The native ChatGPT/Codex grant on the hub itself can become unusable for WHAM quota (token_revoked) even after a successful stored-token refresh, and an operator then needs a new device login that writes the native main slot — the same way pool rows already work after #3366/#3369/#3385.
What prevents this today?
On Codex Auth the main card is a locked App login identity. When reauth is needed it only shows codexAuth.mainTokenExpired ("Token expired — sign in again via Codex App login"). There is no Re-login / device-code control on that card (gui/src/components/codex-account-pool-main-card.tsx).
The WebUI device flow (POST /api/codex-auth/login with device: true) is pool-only:
{ id: "__main__", reauth: true, device: true }→400 Invalid account id format{ id: "main", reauth: true, device: true }→404 Unknown pool account for reauth{ reauth: true, device: true }→400 id required for reauth{ device: true }with no id → starts a new pool account add
Pool rows can Re-login through the same modal (gui/src/components/use-add-codex-account-oauth.ts). That is the correct tool for extra ChatGPT accounts, not for replacing native __main__.
The documented native path is ocx account main add / /api/native-main-profiles/*, which launches official codex login in a staging home and needs the OS keyring. On the published hub image that path is not usable: codex is not on PATH, and GET /api/native-main-profiles/doctor reports keyStore: unavailable, vaultStatus: missing, profileCount: 0.
#3417 tracks exposing native-main profiles in the WebUI (list/register/switch/recover). That issue explicitly says a browser cannot run official codex login itself, and new-profile enrollment is a follow-up. It does not cover deviceauth reauth of the existing __main__ grant on a container hub that has no Codex App and no codex binary.
#3366 closed the pool deviceauth gap. Native main was left on "use Codex App".
What should OpenCodex do?
Give the Codex Auth main card a device-login reauth that overwrites native __main__ / $CODEX_HOME/auth.json, visually separate from pool Add / pool Re-login.
Suggested contract:
- Reuse the existing OpenAI deviceauth grant already used for pool add (
loginChatGPTDevice/auth.openai.com/codex/device). - Persist tokens to the native main slot, not as a 13th pool account.
- Refuse to treat
__main__as a poolidin/api/codex-auth/login(keep that 400), and instead add an explicit native-main device reauth route or a documentedocx account main reauth --devicethat the GUI can start/poll/cancel. - Do not require official
codexon PATH or an OS keyring for this one operation onruntimeRole=hub. - Keep tokens, emails, and raw account ids out of GUI payloads/logs; show only URL + device code + status.
Until that exists, headless operators have no first-party way to recover a WHAM-revoked native grant without adding a pool account by mistake or copying auth.json from a workstation.
Example usage or interface
Codex Auth
Main account (App login)
[Re-login with device code] ← missing today
copy: open https://auth.openai.com/codex/device and enter ABCD-12345
Account pool
[Add] ← exists; must stay a new pool row
row → [Re-login] ← exists; must not target __main__
$ ocx account main reauth --device --no-wait
url: https://auth.openai.com/codex/device
deviceCode: ABCD-12345Alternatives or workarounds
- Codex Auth → Add / pool Re-login: works, but adds or refreshes a pool account. Completing that flow does not replace native
__main__. ocx account main add: requires official Codex CLI + keyring; not present on the hub image.- Copy workstation
auth.jsoninto the hub volume: works, but fights hub-owned credentials and is easy to get wrong. - feat(gui): expose native main login profiles in the WebUI #3417 profile switcher: only helps after an encrypted native profile already exists.
Additional context
- Version: OpenCodex hub v2.44.0 (container,
runtimeRole=hub,oauthOpenBrowser: false). - Live check: stored refresh grant still produces a fresh access JWT via
getValidMainAccountToken(); WHAMGET https://chatgpt.com/backend-api/wham/usagethen returns HTTP 401token_revoked. A paused pool account on the same process/proxy/headers returns WHAM 200, so this is grant invalidation, not a broken quota probe. - Related: [Feature]: ChatGPT/Codex login via OpenAI deviceauth (device code) for headless/remote hubs #3366 (pool deviceauth, done), feat(gui): expose native main login profiles in the WebUI #3417 (native-main profiles GUI, open), [Bug]: pool quota WHAM 401 bypasses stored-token refresh after plan changes #3019 (pool WHAM 401 recovery, done), feat(codex): add encrypted native main profiles #863 / Switch a stored Codex account into the native main login from OpenCodex #656 (native profiles CLI/backend).
Checks
- I searched existing issues and documentation.
- This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
- I removed secrets and personal data.
Activity
lidge-jun commentedon Sep 7, 2026
리뷰 · 우선순위 62 / 80
이 이슈는 헤드리스 hub(Docker/k3s, Codex App 없음,
oauthOpenBrowser: false)에서 Codex Auth의 native main(__main__,$CODEX_HOME/auth.json) 을 WebUI로 다시 로그인할 방법이 없다는 제품 구멍입니다. 풀 계정은 #3366/#3369/#3385 이후 deviceauth로 Add/Re-login이 됩니다. 그런데 main 카드는 App login 전용으로 잠겨 있고, 토큰이 만료·철회되면codexAuth.mainTokenExpired(“Token expired — sign in again via Codex App login”) 문구만 보입니다. 운영자가 WHAM에서token_revoked를 맞아도, 같은 프록시의 paused pool은 WHAM 200이 나올 수 있어 메인 슬롯 자격 증명만 죽은 상태가 됩니다. 페어링(ocx connect)은 클라이언트를 hub에 붙이는 일이고, native grant를 갈아끼우는 일이 아닙니다.현재
dev(2.47.0)에서도 그 구조는 그대로입니다. GUI main 카드는 reauth가 필요할 때 안내 문구만 띄우고, device-code 버튼을 달지 않습니다. APIPOST /api/codex-auth/login의 device 플로우는 풀 계정 생성·풀 reauth용으로만 열려 있습니다.__main__을 id로 넣으면 계정 id 검증에서 거절되고,main은 풀에 없으니 404, id 없는 reauth는 400, id 없는 device는 새 풀 계정 추가로 갑니다. 문서화된 native 경로(ocx account main add//api/native-main-profiles/*)는 공식codex login+ OS keyring을 가정하는데, 공개 hub 이미지에는codex가 PATH에 없고 doctor는 keyStore unavailable / vault missing을 보고합니다. #3417은 native-main 프로필 GUI(목록/전환/복구)이고, “브라우저가 공식 codex login을 대신 실행할 수 없다”고 못 박아 두었을 뿐, 컨테이너 hub의 기존__main__deviceauth 재인증은 범위 밖입니다.경로·심볼로 오늘 코드가 하는 일과 구멍은 이렇게 정리됩니다.
gui/src/components/codex-account-pool-main-card.tsx—showReauth일 때t("codexAuth.mainTokenExpired")만 렌더. device Re-login 컨트롤 없음.gui/src/components/use-add-codex-account-oauth.ts/AddCodexAccountModal—device: true로/api/codex-auth/login을 치는 기존 UI.reauthAccountId는 풀 row용. main 카드와 연결되어 있지 않습니다.src/codex/auth-api.ts(POST /api/codex-auth/login) —device === true면startLoginFlow(..., { flow: "device" }). 하지만isValidCodexAccountId가__main__을 거부하고, reauth는configuredPoolAccount만 허용합니다. 성공 시에도 풀 credential/풀 row를 갱신하는 경로입니다.src/codex/account-id.ts—MAIN_CODEX_ACCOUNT_ID = "__main__",isValidCodexAccountId가 센티널을 명시적으로 제외. 풀 namespace에 main을 섞지 않으려는 설계이므로,__main__을 풀 id로 우회 허용하는 건 비추천입니다.src/oauth/chatgpt-device.ts의loginChatGPTDevice— OpenAI deviceauth(auth.openai.comdeviceauth usercode/token) 구현이 이미 있습니다. 풀 Add가 쓰는 그 grant를 main 슬롯 persist에만 다시 꽂으면 됩니다.src/cli/account-main.ts/src/codex/native-profile-api.ts—account main add|doctor|register|switch|recover와/api/native-main-profiles/*. device reauth 서브커맨드·전용 라우트는 없습니다.src/codex/main-account.ts— nativeauth.json읽기/쓰기·토큰 갱신 경계. device 성공 토큰을 여기로 커밋하는 명시 API가 WebUI에 노출되어 있지 않습니다.메인테이너의 판단이 필요한 지점
/api/codex-auth/main/reauth-device) vsocx account main reauth --deviceCLI를 GUI가 폴링하는 형태 vs 둘 다.runtimeRole=hub에서만 노출할지, desktop에서도 device main reauth를 허용할지.너의 추천
풀 login API에
__main__을 억지로 넣지 마세요(지금 400은 맞습니다). 대신 native-main 전용 device reauth를 하나 만드세요: 기존loginChatGPTDevicegrant 재사용 → 토큰을__main__/auth.json에만 persist → GUI main 카드에 “device code로 다시 로그인” 버튼 + URL/코드/상태만 표시. CLI는ocx account main reauth --device [--no-wait]로 같은 백엔드를 타면 Docker 운영자가 WebUI 없이도 복구할 수 있습니다. keyring/codex바이너리는 이 한 경로에서 요구하지 마세요. #3417은 프로필 전환 축으로 남기고, 이 이슈는 “죽은 main grant 복구”에 범위를 고정하면 mid-high(대략 60대)로 충분히 가치가 있습니다. 풀 Add를 임시 우회로 안내하면 자격 증명이 잘못된 버킷에 쌓이므로, 문서/카피에서도 “풀 Re-login ≠ main 복구”를 분명히 하세요.이 댓글은 grok-bot이 작성했습니다
feat(codex): native-main device reauth API for headless hubs (#3898)
feat(cli): ocx account main reauth --device with registry, docs, and …
fix(codex): close the cancel/commit race and fence the reauth commit (#…
test(oauth): name the composite-signal proof and bound the fetch dead…
test(codex): allow the transient cancelled read before publication wi…
test(server): stop reading a pure delegation guard as an unresolved r…
lidge-jun commentedon Sep 13, 2026
Landed via #4441 at 0c4f962
L2 #4433 (native-main reauth API/CLI) + L3 #4441 (main-card Re-login with device code) are on
dev. Headless hub operators can recover a WHAM-revoked native__main__grant from the Codex Auth main card without the pool login path.Related stack still open: #4191 (L1 instrumentation landed via #4427; root cause may remain) and #4311 (L4 paginated writer still planned).
이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 13, 2026
Closing as completed: native-main device reauth UI+API shipped on
devvia #4433+#4441 (merge 0c4f962).docs(devlog): diff-level roadmap for the unimplemented trio stack (li…
feat(gui): main-card Re-login with device code on the native reauth n…
docs(devlog): diff-level roadmap for the unimplemented trio stack (li…
feat(codex): native-main device reauth API for headless hubs (lidge-j…
feat(cli): ocx account main reauth --device with registry, docs, and …
fix(codex): close the cancel/commit race and fence the reauth commit (l…
test(oauth): name the composite-signal proof and bound the fetch dead…
test(codex): allow the transient cancelled read before publication wi…
feat(gui): main-card Re-login with device code on the native reauth n…
test(server): stop reading a pure delegation guard as an unresolved r…
fix(oauth): harden the ChatGPT refresh path