Skip to content
Merged
6 changes: 4 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ WORKDIR /home/bun/app

ENV NODE_ENV=production \
OPENCODEX_HOME=/home/bun/.opencodex \
CODEX_HOME=/home/bun/.codex \
OCX_API_TOKEN_FILE=/home/bun/.opencodex/service-api-token

RUN install -d -m 0700 -o bun -g bun /home/bun/.opencodex
# These homes have incompatible auth.json formats; persist them without combining them.
RUN install -d -m 0700 -o bun -g bun /home/bun/.opencodex /home/bun/.codex
COPY --chown=bun:bun --chmod=0600 docker/config.json /home/bun/.opencodex/config.json

COPY --from=build --chown=bun:bun /home/bun/app/package.json ./package.json
Expand All @@ -46,7 +48,7 @@ COPY --from=build --chown=bun:bun /home/bun/app/gui/dist ./gui/dist
USER bun
RUN ["bun", "docker/verify-compatibility.ts"]
RUN ["bun", "-e", "import { readOpenCodexCompatibilityVersion } from './src/routing/compatibility/version.ts'; if (!/^[0-9a-f]{64}$/.test(readOpenCodexCompatibilityVersion() ?? '')) throw new Error('Missing or invalid generated compatibility manifest');"]
VOLUME ["/home/bun/.opencodex"]
VOLUME ["/home/bun/.opencodex", "/home/bun/.codex"]
EXPOSE 10100

HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
Expand Down
5 changes: 5 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ services:
target: runtime
init: true
read_only: true
environment:
# A custom CODEX_HOME also requires a matching writable volume target below.
CODEX_HOME: /home/bun/.codex
ports:
- "${OPENCODEX_BIND_ADDRESS:-127.0.0.1}:${OPENCODEX_PORT:-10100}:10100"
volumes:
- ocx-state:/home/bun/.opencodex
- codex-state:/home/bun/.codex
tmpfs:
- /tmp:size=64m,mode=1777
security_opt:
Expand All @@ -24,3 +28,4 @@ services:

volumes:
ocx-state:
codex-state:
21 changes: 21 additions & 0 deletions docs-site/src/content/docs/fr/guides/remote-hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ La rotation garde les deux clés valides sous le même `apiKeyId` pendant dix mi

## Docker, retour arrière et dépannage

Lors d'un retour arrière, conservez les deux volumes et leurs points de montage. Les droits des volumes existants ne sont pas corrigés automatiquement. Consultez le [guide canonique](/guides/remote-hub/#docker-compose) pour les montages nommés hors Compose et les chemins d'état personnalisés.

Deux volumes distincts conservent l'état : `ocx-state` pour
`OPENCODEX_HOME=/home/bun/.opencodex` et `codex-state` pour
`CODEX_HOME=/home/bun/.codex`. Leurs fichiers `auth.json` ont des formats incompatibles :
ne fusionnez pas ces répertoires. Ils restent accessibles en écriture malgré la racine en lecture seule.

Le catalogue n'est pas généré automatiquement. Avant de tester `/v1/catalog` avec authentification,
créez ou importez un fichier valide dans `/home/bun/.codex/opencodex-catalog.json`.
Un répertoire vide renvoie normalement 404 `catalog_not_found`. Une mise à jour conserve
`ocx-state` et ajoute `codex-state`, sans déplacer les fichiers. Sauvegardez tout catalogue
précédemment placé dans `.opencodex`, puis transférez seulement ce catalogue avec des permissions
réservées au propriétaire ; ne remplacez pas un `auth.json` par celui de l'autre produit.
Si vous redéfinissez `CODEX_HOME`, montez ce répertoire exact en écriture et placez le catalogue
par défaut dans `${CODEX_HOME}/opencodex-catalog.json`. Si `model_catalog_json` désigne un autre
fichier, son chemin résolu doit aussi être persistant. Conservez les variables et montages
personnalisés jusqu'à la fin d'une migration explicite.
`docker compose down` conserve les deux volumes ; `docker compose down --volumes` supprime
`ocx-state` et `codex-state`, avec les identifiants, l'historique d'utilisation, la clé de données,
l'état et le catalogue Codex. Ce n'est pas une commande de mise à jour ou de redémarrage.

Il n’existe pas d’image Docker officielle, mais le dépôt fournit un `Dockerfile` et un `compose.yaml` maintenus pour construire localement une image Bun épinglée par digest. Initialisez une seule fois la clé de données via stdin ; elle est enregistrée avec des permissions réservées au propriétaire dans le volume `ocx-state` et n’est jamais affichée.

Installez Git et Bun sur l’hôte. Avant chaque construction, générez le manifeste canonique depuis les sources suivies par Git, sans modifier les sources entre la génération et la construction. Le JSON généré reste non suivi ; `.git` est exclu du contexte Docker. Le port hôte est lié à `127.0.0.1` par défaut. Pour un accès distant, utilisez explicitement `OPENCODEX_BIND_ADDRESS=<IP-LAN-ou-Tailscale> docker compose up -d` ; `0.0.0.0` expose toutes les interfaces. Protégez cet accès par un pare-feu et un frontal TLS/tailnet authentifié.
Expand Down
49 changes: 45 additions & 4 deletions docs-site/src/content/docs/guides/remote-hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,45 @@ Before the first normal start, stream a freshly generated data-plane token into
The helper accepts at most one 4096-byte line, never prints the token, refuses to replace an existing
token, and persists it as the canonical owner-only `service-api-token` in the `ocx-state` volume.

The deployment persists two separate homes: `ocx-state` at `/home/bun/.opencodex` for
OpenCodex configuration, provider credentials and usage, and `codex-state` at
`/home/bun/.codex` for Codex state and `opencodex-catalog.json`. The image and Compose
explicitly set `CODEX_HOME=/home/bun/.codex`, so this catalog path remains writable
with `read_only: true` and survives container recreation. The image creates both
directories for the non-root `bun` user with mode `0700`; existing volume
ownership and permissions are not migrated automatically.

Do not combine `CODEX_HOME` and `OPENCODEX_HOME`: both products use an `auth.json`
filename with different formats. This packaging change adds persistence, not a
catalog generator. Materialize or import a valid catalog into
`/home/bun/.codex/opencodex-catalog.json` before the catalog acceptance check below;
without one, `catalog_not_found` remains the expected response.

Upgrading preserves the existing `ocx-state` volume and adds `codex-state`; no files
are migrated automatically. If a previous workaround placed a catalog directly
under `/home/bun/.opencodex`, back it up and deliberately copy only the catalog to
the new Codex home, preserving owner-only access. Do not copy either product's
`auth.json` over the other. Deployments with a custom `CODEX_HOME` should retain
their explicit environment and writable volume mapping until migration is complete.
When overriding `CODEX_HOME`, mount that exact directory writable and persist the
default catalog at `${CODEX_HOME}/opencodex-catalog.json`. If `model_catalog_json`
explicitly selects another file, that resolved path must also be persisted.

Keep the Compose project name stable during upgrades so the same named volumes are reused.
Mounts with existing foreign ownership, read-only mounts, and mounts using `volume-nocopy`
are not repaired by the image's directory setup. Persist separately selected catalog or SQLite
paths separately; an OS credential store is not backed up by these two volumes.

When running without Compose, explicitly supply both named mounts. Dockerfile `VOLUME`
declarations alone create anonymous volumes that a later `docker run` does not automatically
reuse. These mount options use standalone example names; to reuse Compose data, substitute
its actual project-prefixed volume names:

```sh
--mount type=volume,src=ocx-state,dst=/home/bun/.opencodex \
--mount type=volume,src=codex-state,dst=/home/bun/.codex
```

Install Git and Bun on the host first. Before **every** image build, run the existing canonical
generator from this Git checkout. It hashes Git-tracked working-tree sources (stage any newly
added source files first), not an arbitrary directory scan. Do not change source files between
Expand Down Expand Up @@ -227,7 +266,7 @@ docker compose restart hub
```

Do not put a token in `ARG`, `ENV`, `COPY`, Compose YAML, image history, or command arguments. Do not
mount the Docker socket, host home, Codex home, SSH agent, or provider-key files. A management
mount the Docker socket, the host's home or Codex home, SSH agent, or provider-key files. A management
ingress bound to `127.0.0.1:10101` inside the container is reachable only by a TLS/tailnet frontend
in the same network namespace; never publish `10101` as a shortcut.

Expand All @@ -244,9 +283,9 @@ docker compose exec hub bun -e \
Then send one real authenticated routed response with a configured model. If the secret is absent or
unreadable, a non-loopback hub must not be accepted as ready. Never treat liveness alone as proof.

`docker compose down` removes the container and network but retains the named volume. Treat
`docker compose down` removes the container and network but retains both named volumes. Treat
`docker compose down --volumes` as destructive: it deletes configuration, OAuth credentials, usage
history, and the data-plane token together.
history, the data-plane token, and persisted Codex state together.

## Rollback

Expand All @@ -260,7 +299,9 @@ ocx config set hub.managementIngress '{"enabled":false}'
ocx service repair
```

For a container rollback, remove or replace the container while retaining the named state volume.
For a container rollback, retain both named state volumes and their mappings. An older image
can still use `CODEX_HOME=/home/bun/.codex` when that directory remains mounted; do not revert
to an older Compose file that drops the Codex mount. Do not merge the homes or rerun token bootstrap.
For a service rollback, stop the branch service and repair the prior release against the same
`OPENCODEX_HOME`. Disabling management ingress or Serve does not require changing the data listener.

Expand Down
22 changes: 22 additions & 0 deletions docs-site/src/content/docs/ja/guides/remote-hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,28 @@ OAuth は `POST /api/oauth/login` で開始し、コールバックできない

## Docker とトラブルシューティング

ロールバック時も両方のボリュームとマウント先を維持してください。既存ボリュームの所有者や権限は自動修復されません。Compose を使わない場合の名前付きマウントと独自の状態パスについては、[正本ガイド](/guides/remote-hub/#docker-compose)を参照してください。

状態は二つのボリュームに分けて永続化します。`ocx-state` は
`OPENCODEX_HOME=/home/bun/.opencodex`、`codex-state` は
`CODEX_HOME=/home/bun/.codex` に対応します。両製品の `auth.json` は形式が
異なるため、ホームを同じディレクトリにしないでください。読み取り専用の
ルートでも、この二つのホームは書き込み可能です。

カタログは自動生成されません。認証付き `/v1/catalog` の確認前に、有効な
`/home/bun/.codex/opencodex-catalog.json` を生成または取り込んでください。
空のホームでは `catalog_not_found` の 404 が正常です。アップグレードは既存の
`ocx-state` を保持して `codex-state` を追加しますが、ファイルは自動移行しません。
以前 `.opencodex` に置いたカタログはバックアップし、カタログだけを所有者限定の
権限で移してください。`auth.json` を相互に上書きしないでください。
`CODEX_HOME` を変更する場合は、そのディレクトリ自体を書き込み可能なボリュームに
マウントし、既定のカタログを `${CODEX_HOME}/opencodex-catalog.json` に置きます。
`model_catalog_json` で別のファイルを指定した場合は、その解決先も永続化します。
カスタム構成は、明示的な移行が完了するまで環境変数とボリュームの対応を維持します。
`docker compose down` は両ボリュームを保持しますが、`docker compose down --volumes`
は `ocx-state` と `codex-state` の両方を削除し、認証情報・使用履歴・データキー・
Codex の状態とカタログも失われます。更新や再起動の代わりに使わないでください。

公式 Docker イメージはありませんが、リポジトリには digest 固定の Bun イメージをローカルビルドするための、管理された `Dockerfile` と `compose.yaml` があります。初回起動前にデータキーを stdin から一度だけ初期化します。キーは表示されず、`ocx-state` ボリューム内に所有者限定の権限で保存されます。

ホストに Git と Bun が必要です。イメージをビルドするたびに、Git 管理下のソースから正規のマニフェストを生成し、生成後はビルドまでソースを変更しないでください。生成 JSON は Git に追加せず、`.git` は Docker コンテキストから除外します。ホスト側は既定で `127.0.0.1` にバインドします。リモート公開は `OPENCODEX_BIND_ADDRESS=<LANまたはTailscaleのIP> docker compose up -d` で明示的に指定し、`0.0.0.0` は全インターフェースを公開します。ファイアウォールと認証付き TLS/tailnet フロントエンドで保護してください。
Expand Down
22 changes: 20 additions & 2 deletions docs-site/src/content/docs/ko/guides/remote-hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ ocx connect rotate --admin-token-stdin

## Docker

롤백할 때도 두 볼륨과 마운트 경로를 유지하세요. 기존 볼륨의 소유권과 권한은 자동으로 복구되지 않습니다. Compose 없이 실행할 때의 named volume 지정과 별도 상태 경로는 [영문 기준 가이드](/guides/remote-hub/#docker-compose)를 참고하세요.

상태는 두 볼륨에 분리해 보관합니다. `ocx-state`는
`OPENCODEX_HOME=/home/bun/.opencodex`, `codex-state`는
`CODEX_HOME=/home/bun/.codex`에 연결됩니다. 두 제품의 `auth.json` 형식이 다르므로
홈을 같은 폴더로 합치지 마세요. 루트 파일 시스템이 read-only여도 이 두 홈은 쓰기 가능합니다.

카탈로그는 자동 생성되지 않습니다. 인증된 `/v1/catalog` 검사 전에 유효한
`/home/bun/.codex/opencodex-catalog.json`을 생성하거나 가져와야 합니다.
빈 홈에서 `catalog_not_found` 404는 정상입니다. 업그레이드는 기존 `ocx-state`를
유지하고 `codex-state`를 추가하지만 파일을 자동 이동하지 않습니다. 이전 우회 설정으로
`.opencodex`에 둔 카탈로그는 백업한 뒤 카탈로그만 owner-only 권한으로 옮기세요.
두 제품의 `auth.json`을 서로 덮어쓰면 안 됩니다. 사용자 지정 `CODEX_HOME`은 그 정확한
디렉터리를 쓰기 가능한 볼륨에 연결하고, 기본 카탈로그를
`${CODEX_HOME}/opencodex-catalog.json`에 준비해야 합니다. `model_catalog_json`으로
별도 파일을 지정했다면 그 경로도 영속 보관하세요. 명시적 이전이 완료되기 전까지는
기존 사용자 지정 환경 변수와 볼륨 경로의 대응을 유지하세요.

opencodex는 공식 컨테이너 이미지를 배포하지 않지만, 저장소 루트의 `Dockerfile`과 `compose.yaml`로 digest가 고정된 소스 이미지를 직접 빌드할 수 있습니다. 최초 실행 전에 데이터 키를 stdin으로 초기화하세요. 키는 출력되지 않으며 `ocx-state` 볼륨의 owner-only `service-api-token`에 저장됩니다.

호스트에 Git과 Bun이 필요합니다. 이미지를 빌드할 때마다 Git이 추적하는 소스로 정식 매니페스트를 생성하고, 생성부터 빌드 사이에는 소스를 변경하지 마세요. 생성된 JSON은 Git에 추가하지 않으며 `.git`은 Docker 컨텍스트에서 제외됩니다. 호스트 포트는 기본적으로 `127.0.0.1`에 바인딩됩니다. 원격 공개는 `OPENCODEX_BIND_ADDRESS=<LAN-또는-Tailscale-IP> docker compose up -d`로 명시적으로 선택하며, `0.0.0.0`은 모든 인터페이스에 공개합니다. 방화벽과 인증된 TLS/tailnet 프런트엔드로 보호하세요.
Expand All @@ -101,11 +119,11 @@ openssl rand -hex 32 | docker compose run --rm -T hub bun run docker/bootstrap-t
docker compose up -d
```

이미지는 non-root `bun` 사용자로 실행되고 루트 파일 시스템은 read-only이며 공개 포트는 `10100` 하나뿐입니다. 토큰을 `ARG`, `ENV`, `COPY`, Compose YAML, 이미지 기록, 명령행에 넣지 마세요. Docker socket, 호스트 홈, Codex 홈, SSH agent, 프로바이더 키도 마운트하지 마세요. 컨테이너 안의 `127.0.0.1:10101` 관리 포트는 같은 네트워크 네임스페이스의 TLS/tailnet 프런트엔드로만 연결하고 직접 publish하지 마세요.
이미지는 non-root `bun` 사용자로 실행되고 루트 파일 시스템은 read-only이며 공개 포트는 `10100` 하나뿐입니다. 토큰을 `ARG`, `ENV`, `COPY`, Compose YAML, 이미지 기록, 명령행에 넣지 마세요. Docker socket, 호스트의 홈이나 Codex 홈, SSH agent, 프로바이더 키도 마운트하지 마세요. 컨테이너 안의 `127.0.0.1:10101` 관리 포트는 같은 네트워크 네임스페이스의 TLS/tailnet 프런트엔드로만 연결하고 직접 publish하지 마세요.

컨테이너 healthcheck의 `/healthz`가 통과한 뒤 `/readyz`, 인증된 `/v1/catalog`, 실제 모델 응답을 별도로 확인하세요.

`docker compose down`은 `ocx-state` 볼륨을 보존합니다. `docker compose down --volumes`는 설정, OAuth 인증 정보, 사용량 기록, 데이터 키를 함께 삭제하므로 파괴적 작업으로 취급하세요.
`docker compose down`은 `ocx-state`와 `codex-state`를 모두 보존합니다. `docker compose down --volumes`는 두 볼륨을 모두 삭제하여 설정, OAuth 인증 정보, 사용량 기록, 데이터 키, Codex 상태와 카탈로그를 지웁니다. 업그레이드나 재시작 대신 사용하지 마세요.

## 롤백과 문제 해결

Expand Down
Loading
Loading