Skip to content

[Bug]: ocx opencode reads the management route GET /api/models with the data-plane admission key and exits 401 #4315

Closed

Description

@cortes-felipe

Client or integration

Other (the ocx opencode launcher for the OpenCode CLI)

Area

CLI

Summary

ocx opencode reads the model catalog from GET /api/models with the data-plane admission key. That route is part of the management API, which requireManagementAuth (src/server/management-auth.ts:539, gated at src/server/index.ts:1289) admits only with the admin credential. On a hardened install the proxy therefore answers 401 {"error":"opencodex admin token required"}, the launcher prints ❌ Could not fetch the model catalog from the proxy: … and returns 1, and OpenCode is never launched.

Verified against a live 2.51.0 proxy on 127.0.0.1:10100 with an admin-api-token present:

$ curl -s -o /dev/null -w '%{http_code}\n' -H 'X-OpenCodex-API-Key: <data-plane admission key>' http://127.0.0.1:10100/api/models
401
{"error":"opencodex admin token required"}

$ curl -s -o /dev/null -w '%{http_code}\n' -H 'X-OpenCodex-API-Key: <admin token from ~/.opencodex/admin-api-token>' http://127.0.0.1:10100/api/models
200

Every other headless management caller already sends the admin credential — runningProxyUpdateHeaders in src/oauth/login-cli.ts, src/cli/claude.ts, and the GUI. cmdOpencode is the outlier: it calls fetchOpencodeProxyModels(live, apiKey) where apiKey = opencodeApiKey(startupConfig) resolves the admission key that is handed to the child OpenCode process.

Expected: the launcher authenticates its one management read with the configured admin token, keeping the admission key for the child process (and as a fallback for a host that truly has no admin credential).

Reproduction

  1. Use a hardened install where ocx start has minted ~/.opencodex/admin-api-token.
  2. With the proxy running, execute ocx opencode.
  3. Observe ❌ Could not fetch the model catalog from the proxy: and exit code 1; OpenCode never starts.

Code path: src/cli/opencode.ts — const apiKey = opencodeApiKey(startupConfig); then fetchOpencodeProxyModels(live, apiKey).

The same request with the admin token returns 200 and the catalog, which is what the launcher needs.

Version

@bitkyc08/opencodex 2.51.0 installed; the call site is unchanged on dev at 7a0513c (2.52.0).

Operating system

macOS 26.6.2 arm64 (proxy on loopback, port 10100).

Provider and model

Not provider-specific. The failing request is the management catalog read performed by the ocx opencode launcher.

Logs or error output

$ ocx opencode
❌ Could not fetch the model catalog from the proxy: GET /api/models responded 401: {"error":"opencodex admin token required"}

Screenshots and supporting files

A fix with regression coverage is linked below as a pull request.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, personal data, local paths, session IDs, and request IDs.

Activity

  1. added
    bugSomething isn't working
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    catalogModel catalog, slugs, visibility, routed entries
    cliCLI, config inject, packaging flags
    proxyHTTP proxy, routing, reverse-proxy / management auth
    on Sep 12, 2026
  2. lidge-jun commented on Sep 12, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 60 / 80

    이 이슈는 hardened 프록시에서 ocx opencode가 모델 카탈로그를 못 읽어 종료 코드 1로 죽는 문제입니다. 지금 dev(8fc63277d, #4321 이후)에서도 같은 구조입니다. 런처 src/cli/opencode.ts의 cmdOpencode가 fetchOpencodeProxyModels(live, opencodeApiKey(...))로 GET /api/models를 치는데, 서버는 src/server/index.ts에서 url.pathname.startsWith("/api/")이면 requireManagementAuth를 탑니다. 데이터 플레인 입장키는 여기서 거절되고, 응답은 opencodex admin token required입니다. 자식 OpenCode가 대화할 때 쓰는 키와, 런처가 관리 목록을 읽을 때 써야 하는 키가 원래 다른 층입니다.

    재현 설명도 코드와 맞습니다. 같은 관리 읽기를 이미 올바르게 하는 호출자는 runningProxyUpdateHeaders(src/oauth/login-cli.ts)와 Claude 런처(src/cli/claude.ts)이고, 둘 다 configuredAdminToken(src/lib/admin-secrets.ts: env OPENCODEX_ADMIN_AUTH_TOKEN 또는 admin-api-token 파일)을 씁니다. OpenCode 런처만 입장키를 그대로 넣은 구멍입니다. /v1/models 같은 데이터 플레인 목록 API와 /api/models 관리 목록을 섞어 생각하면 이 버그가 다시 납니다.

    영향 범위는 "OpenCode를 ocx로 띄우는 사람 + admin 토큰이 켜진 프록시"입니다. 키풀(#4292)이나 Z.AI Responses 저장(#4321)과는 별개입니다. 고치지 않으면 OpenCode 통합 자체가 hardened 기본 설정에서 시작조차 못 합니다.

    이미 수정 PR #4317이 열려 있습니다. opencodeManagementToken으로 관리 토큰을 고르고 카탈로그 fetch에만 쓰며, 자식 env의 입장키는 유지합니다. 테스트도 토큰 우선순위와 헤더 assertion을 넣었습니다. 이슈 본문의 before/after(입장키 401 / admin 200)와 PR 설명이 일치합니다.

    src/cli/opencode.ts (fetchOpencodeProxyModels 호출부) - 지금도 HEAD에서는 입장키를 그대로 넘깁니다. #4317이 랜딩되기 전에는 재현이 그대로입니다.

    src/server/index.ts (/api/* + requireManagementAuth) - 서버 쪽이 잘못된 것이 아닙니다. 관리 게이트가 의도대로 막은 결과입니다. "서버에서 입장키도 받게" 완화하는 방향은 보안 모델을 깨므로 비추천입니다.

    configuredAdminToken / 다른 런처 - 수정 방향의 정답 패턴이 이미 레포 안에 있습니다. 새 설계보다 런처 정렬이 맞습니다.

    중복 - #4315와 같은 증상을 다른 이슈로 열린 최근 항목은 스캔 범위에서 보이지 않았고, 수정은 #4317 하나로 묶는 편이 맞습니다.

    메인테이너의 판단이 필요한 지점

    너의 추천
    서버를 느슨하게 만들지 말고 #4317을 랜딩해 이슈를 닫으세요. 재현·원인·수정이 이미 한 줄로 연결돼 있어 추가 조사 이슈로 둘 필요가 없습니다. types/config 스플릿과 무관합니다.

    이 댓글은 grok-bot이 작성했습니다

  3. Ingwannu commented on Sep 12, 2026

    @Ingwannu
    Owner

    Confirmed the launcher credential mismatch and reviewed the proposed fix in #4317. I requested changes on head 27f577a because the new admin credential still travels through the old global-fetch destination path; the existing proxy/redirect and local-destination boundaries must be fixed before landing. The direct-local HTTP helper avoids proxy routing but is not itself a loopback allowlist, so both transport and destination checks matter. The child still needs the separate admission credential. @lidge-jun keep this issue open until the corrected exact-head PR is verified; no production configuration or tokens were changed.

  4. cortes-felipe commented on Sep 12, 2026

    @cortes-felipe
    ContributorAuthor

    Corrected PR #4317 is up at d8555e412, rebased onto dev dcd13b435. What changed against the review on this issue:

    • the catalogue read still presents the management credential (env OPENCODEX_ADMIN_AUTH_TOKEN, then the hardened admin-api-token file), but the destination is now refused unless it is loopback, and that refusal happens before any request is built. The origin comes from localManagementOrigin, and probeHostname keeps the supported wildcard/IPv4/IPv6 spellings dialing 127.0.0.1;
    • the transport is directLocalHttpFetch rather than global fetch: no proxy-environment routing, no redirect following, proxy headers dropped. The destination check is a separate caller-level step, since that helper is a transport and not a loopback allowlist;
    • for a process-attested proxy the read goes through the single-use local management capability with /api/models added to the read allowlist (route registered mutates: false; server still admits only a bodyless, query-free GET bound to the attested pid and port, replay-protected), so no reusable credential is sent at all. An older proxy, or a hub ingress on a different port, falls back to the loopback token read;
    • the child process still receives the data-plane admission key through buildOpencodeEnv, and the tests now assert that on the spawned env and the inline config — the earlier claim that an admin env variable never reaches the child was wrong (that inheritance predates this PR) and the description is corrected;
    • the admission-key fallback is kept and documented, and structure/runtime.md, structure/config.md, structure/clients/claude-desktop.md, structure/ops/docs-and-release.md, plus the public docs-site/.../guides/opencode.md record the management-credential versus admission-key contract.

    Verification on that head: bun test tests/providers/opencode-cli.test.ts 63 pass / 0 fail; bun test tests/server/server-management-auth.test.ts 47 pass / 0 fail; bun run test:changed 14,565 pass / 0 fail across 647 files; bun run typecheck, bun run structure:check, bun run privacy:scan clean.

    The PR stays in draft until the reviewer verifies this exact head, so this issue stays open until then.

  5. lidge-jun commented on Sep 13, 2026

    @lidge-jun
    Owner

    Resolved on dev by #4402, landed today as part of a 36-pull-request batch.

    src/cli/opencode.ts resolves configuredAdminToken() before calling fetchOpencodeProxyModels, so ocx opencode reads the management route GET /api/models with the admin token over loopback while the spawned child still receives the admission key.

    The admission-key fallback was intentionally dropped during review, so the hardened-proxy 401 in the report no longer occurs.

    Verified against origin/dev at 72601a173 after the batch landed, rather than from the pull request description. Closing on that evidence; reopen if the behavior does not match on your setup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't workingcatalogModel catalog, slugs, visibility, routed entriescliCLI, config inject, packaging flagsproxyHTTP proxy, routing, reverse-proxy / management auth

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions