Skip to content

[Bug]: Docker /v1/catalog uses an unpersisted read-only CODEX_HOME #3746

Closed

Description

@juzijia

Resolved in dev

#3788 (ad5285e415) persists the separate Codex home under the read-only container root. OCX and Codex authentication stores remain separate.

Verified against dev 5759d9ea2f1e7281cdc01eb9628f2e0a123fb59c. Original report by @juzijia. The attribution record was added in #3811.

Client or integration

Direct HTTP/API client

Area

Installation or packaging

Summary

The repository-maintained Docker deployment persists /home/bun/.opencodex, but the catalog resolver defaults to /home/bun/.codex/opencodex-catalog.json because CODEX_HOME is not set.

The Compose service also uses read_only: true and does not mount /home/bun/.codex. This leaves the default catalog location outside the only writable persistent volume.

The hub starts normally and /healthz and /readyz return 200, but the documented authenticated /v1/catalog acceptance check returns 404 even when a valid catalog exists in the persisted ocx-state directory.

I expected the repository-maintained Docker deployment to keep the model catalog in a writable persistent location that /v1/catalog can read.

Reproduction

  1. Check out tag v2.43.0.
  2. Generate the compatibility manifest, build the repository Docker image, bootstrap the data-plane token, and start the supplied Compose service.
  3. Place a valid opencodex-catalog.json in the persisted ocx-state directory at /home/bun/.opencodex.
  4. Confirm GET /healthz and GET /readyz return 200.
  5. Run an authenticated GET /v1/catalog request.

The request returns 404 because readCodexCatalogPath() resolves the catalog as /home/bun/.codex/opencodex-catalog.json, while the supplied Compose deployment persists only /home/bun/.opencodex.

Verified workaround:

services:
  hub:
    environment:
      CODEX_HOME: /home/bun/.opencodex

After recreating the container with this environment setting, the same authenticated /v1/catalog request returns 200. Real routed OpenAI Responses and Gemini requests also continue to work.

Version

OpenCodex v2.43.0, commit 06ec553630fa2ee51a96b5cbf694089021249194.

Operating system

Debian GNU/Linux 12 (bookworm), Linux x86_64, Docker Engine 28.5.2, Docker Compose 2.40.3.

Provider and model

Not provider-specific.

Logs or error output

GET /healthz       -> 200
GET /readyz        -> 200
GET /v1/catalog    -> 404

{"error":{"type":"invalid_request_error","code":"catalog_not_found","message":"no materialized catalog is available"}}

Redacted configuration

services:
  hub:
    read_only: true
    volumes:
      - ocx-state:/home/bun/.opencodex

The Dockerfile sets OPENCODEX_HOME=/home/bun/.opencodex but does not set CODEX_HOME.

Checks

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

Activity

  1. added
    catalogModel catalog, slugs, visibility, routed entries
    installInstallation or packaging
    on Sep 6, 2026
  2. github-actions commented on Sep 6, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  3. changed the title [-][Bug][Docker][v2.43.0] /v1/catalog uses an unpersisted read-only CODEX_HOME[/-] [+][Bug]: Docker /v1/catalog uses an unpersisted read-only CODEX_HOME[/+] on Sep 6, 2026
  4. lidge-jun commented on Sep 6, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 62 / 80

    이 이슈는 저장소가 직접 제공하는 Docker/Compose 배포에서 모델 카탈로그 파일이 쓰는 홈과 볼륨으로 남겨 두는 홈이 서로 어긋나는 문제입니다. Dockerfile은 OPENCODEX_HOME=/home/bun/.opencodex만 잡고, compose.yaml도 ocx-state를 /home/bun/.opencodex에만 붙입니다. 그런데 /v1/catalog가 읽는 카탈로그 경로는 Codex 쪽 홈인 CODEX_HOME을 따라갑니다. CODEX_HOME이 비어 있으면 src/codex/paths.ts의 resolveCodexHome()이 defaultCodexHome()으로 떨어지고, 그건 보통 /home/bun/.codex입니다. 그래서 기본 카탈로그는 /home/bun/.codex/opencodex-catalog.json이 됩니다.

    지금 dev(HEAD ef5a7e1c2, package 2.44.0)에서도 같은 구조입니다. Dockerfile ENV에 CODEX_HOME이 없고, Compose는 read_only: true에 /home/bun/.opencodex만 씁니다. /home/bun/.codex는 쓰기 가능 볼륨도 아니고 tmpfs도 아닙니다. 운영자가 opencodex-catalog.json을 영속 볼륨에 넣어도, src/server/catalog-download.ts의 serializePersistedCatalog() → readCodexCatalogPath()는 그 파일을 보지 못합니다. 그래서 /healthz·/readyz는 200인데 인증된 GET /v1/catalog만 catalog_not_found 404가 납니다. 이슈에 적힌 재현과 워크어라운드(CODEX_HOME: /home/bun/.opencodex)는 현재 HEAD 코드 경로와도 맞습니다.

    이건 release-244의 task-input/kiro 본선은 아닙니다. 그래도 공식 Docker 경로의 설치 계약 버그라서, 문서·이미지·Compose를 쓰는 사람한테는 바로 막히는 문제입니다. 고치는 범위는 작게 잡을 수 있습니다. 런타임 카탈로그 해석기를 바꾸기보다, 이미지/Compose가 Codex 홈을 영속 볼륨과 같게 맞추는 편이 안전합니다.

    Dockerfile / ENV - OPENCODEX_HOME만 설정하고 CODEX_HOME이 없음. 컨테이너 기본 유저 bun에서 카탈로그가 /home/bun/.codex/opencodex-catalog.json으로 감
    compose.yaml - read_only: true + ocx-state:/home/bun/.opencodex만 마운트. /home/bun/.codex는 쓰기·영속 모두 안 됨
    src/codex/paths.ts resolveCodexHome / DEFAULT_CATALOG_PATH - env 없으면 ~/.codex 계열 기본값. Docker와 계약을 맞추지 않은 채 그대로면 /v1/catalog가 빈 경로를 봄
    src/codex/catalog/parsing.ts readCodexCatalogPath - 설정 TOML의 model_catalog_json이 없을 때 위 기본 경로로 떨어짐. Compose 기본 이미지에는 그 override도 없음
    src/server/catalog-download.ts / src/server/index.ts /v1/catalog - 파일이 없으면 catalog_not_found 404. health와 분리되어 있어서 “살아 있는데 카탈로그만 없다” 상태가 됨

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

    • Docker에서 CODEX_HOME을 OPENCODEX_HOME과 같은 /home/bun/.opencodex로 고정할지, 아니면 .codex용 볼륨을 따로 둘지
    • 고정을 Dockerfile ENV에만 넣을지, compose.yaml environment에도 중복으로 명시해 문서화할지
    • 이미 ~/.codex에 파일을 둔 커스텀 이미지 사용자와의 호환을 changelog에 어떻게 적을지
    • 이 수정을 release-244 본선에 끼울지, 짧은 install/packaging 핫픽스로 따로 둘지

    너의 추천
    Dockerfile runtime ENV에 CODEX_HOME=/home/bun/.opencodex를 추가하고, compose.yaml에도 같은 environment를 적어 재현을 고정하세요. 가능하면 한 줄 smoke(영속 볼륨에 카탈로그 두고 인증 GET /v1/catalog 200)를 Docker 검증에 넣으세요. 카탈로그 해석기 본체를 바꾸지 마세요. 워크어라운드가 이미 원인과 처방을 증명합니다.

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

  5. Ingwannu commented on Sep 6, 2026

    @Ingwannu
    Owner

    Confirmed against current dev (ef5a7e1c2): the default Codex home is outside the supplied writable volume, so health/readiness can succeed while the catalog endpoint has no persisted catalog to read.

    I am preparing a packaging-only fix that retains /home/bun/.codex as the Codex home and gives it a separate persistent codex-state volume, alongside the existing ocx-state volume. I will not change the runtime catalog resolver or merge the two homes: OpenCodex's OAuth store and Codex's native credentials both use a file named auth.json with different formats. A shared home would create an avoidable storage collision.

    The fix will document the catalog's actual location, keep the read-only root/non-root user/authentication boundaries, and test the image/Compose storage contract. A new volume makes the correct location writable and persistent; it does not manufacture a catalog or migrate existing custom paths automatically. Existing custom CODEX_HOME deployments need to retain their explicit mapping until any deliberate migration is complete.

  6. Ingwannu commented on Sep 6, 2026

    @Ingwannu
    Owner

    Fix prepared in draft PR #3747, head 11248902d. It gives the existing /home/bun/.codex home its own persistent volume while preserving ocx-state and keeping the two incompatible auth stores separate. The runtime catalog resolver and authentication policy are unchanged.

    Verification includes a failing-before/passing-after deployment-contract regression (51 focused tests now pass), TypeScript, Compose validation, privacy scan, documentation build, and a successful production Dockerfile build. In the actual built image, a non-root/read-only/no-network container returned health 200, unauthorized catalog 401, and authorized catalog 200. After replacing the container while retaining its two disposable volumes, the same data and results survived.

    This does not automatically move a catalog previously placed in the OCX home. The PR documents the correct catalog location and explicit migration; an empty new Codex volume still correctly returns catalog_not_found until a valid catalog is materialized/imported there.

    The PR remains draft pending full CI and independent review. Keeping the issue open until the fix lands; no existing deployment has been changed.

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

    bugSomething isn't workingcatalogModel catalog, slugs, visibility, routed entriesinstallInstallation or packaging

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions