Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5f880b6
fix(security): harden management and service boundaries
invalid-email-address Jul 29, 2026
e5f2b53
test: avoid secret scanner false positive
invalid-email-address Jul 29, 2026
35a1031
fix(ci): make security hardening cross-platform
invalid-email-address Jul 29, 2026
33253df
fix(ci): stabilize cross-platform security checks
invalid-email-address Jul 29, 2026
142dfc0
test(gui): make copy feedback timing deterministic
invalid-email-address Jul 29, 2026
e3fd0db
test(usage): allow Windows log rotation budget
invalid-email-address Jul 29, 2026
7cedc58
test(claude): make idle ping timing deterministic
invalid-email-address Jul 29, 2026
693e915
test(kiro): allow concurrent replay budget
invalid-email-address Jul 29, 2026
a5f89d5
ci: retry after Bun runner crash
invalid-email-address Jul 29, 2026
25150ee
test(anthropic): allow manual pool selection budget
invalid-email-address Jul 29, 2026
607b11b
fix(security): harden config ownership lock lifecycle
invalid-email-address Jul 29, 2026
941b297
fix(security): harden service credential delivery
invalid-email-address Jul 29, 2026
d6cb049
fix(security): validate model probe destinations
invalid-email-address Jul 29, 2026
f147c53
fix(update): require executable posix npm candidates
invalid-email-address Jul 29, 2026
817c9b4
test: stabilize proxy environment coverage
invalid-email-address Jul 29, 2026
b06d95a
docs: clarify proxy and service token behavior
invalid-email-address Jul 29, 2026
fd38c42
test(ci): stabilize ownership recovery contention
invalid-email-address Jul 30, 2026
12ce0d9
test(ci): relax key failover e2e timeout
invalid-email-address Jul 30, 2026
d19a645
Merge origin/dev into security/260729-post-merge-hardening
invalid-email-address Jul 30, 2026
20d17bb
Merge origin/dev into security/260729-post-merge-hardening
invalid-email-address Jul 30, 2026
38618b9
fix: harden post-merge security paths
LeoWang331 Jul 30, 2026
537e6ed
Merge remote-tracking branch 'upstream/dev' into security/260729-post…
LeoWang331 Jul 30, 2026
93c8491
test: allow Windows usage debug IO headroom
LeoWang331 Jul 30, 2026
ddd49dc
fix: cancel pinned uploads on request teardown
LeoWang331 Jul 30, 2026
c809af0
test: allow Windows credential IO headroom
LeoWang331 Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,20 @@ jobs:
test:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
# Windows dominates this matrix: on run 30459554635 the same suite took
# ubuntu 4.6min / macos 5.6min / windows 11.8min. Against the previous
# 12-minute ceiling that left ~12s of headroom, so runner variance decided
# the result rather than the code under review — #711's rerun finished at
# 11.8min and passed while #653's was killed at 12.0min (issue #717).
# A cancelled job renders as `fail` in `gh pr checks`, so that flakiness
# reads as a broken PR. 20 minutes keeps a green Windows run green with
# real margin; it is not a licence for the suite to grow into it. If
# Windows approaches this ceiling too, fix the 2.5x platform gap instead
# of raising the number again.
timeout-minutes: 20
# Windows dominates this matrix: recent green runs took about 11.8 minutes
# versus 4.6 on Linux and 5.6 on macOS. Keep platform-specific ceilings so
# POSIX regressions remain bounded while Windows has enough runner margin.
timeout-minutes: ${{ matrix.timeout }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
timeout: 12
- os: macos-latest
timeout: 12
- os: windows-latest
timeout: 20
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
Expand Down
60 changes: 46 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ ocx init # interactive setup
ocx start [--port 10100] # start the proxy; falls back to a free port if busy
ocx stop # stop + restore native Codex
ocx restore # restore without stopping (alias: ocx eject)
ocx uninstall # remove service/shim/config and restore native Codex
ocx uninstall # remove service/shim/owned state and restore native Codex
ocx ensure # start if needed + refresh Codex config/cache
ocx sync # refresh models + re-inject into Codex
ocx codex-shim install # run `ocx ensure` whenever `codex` is launched
Expand Down Expand Up @@ -384,8 +384,11 @@ ocx uninstall
npm uninstall -g @bitkyc08/opencodex
```

`ocx uninstall` stops the proxy, removes any installed service, removes the Codex shim, restores
native Codex config/catalog/history, and deletes `~/.opencodex`.
`ocx uninstall` stops the proxy, removes any installed service, removes the Codex shim, and restores
native Codex config/catalog/history. It removes local state only through the installation ownership
manifest: entries not recorded there are preserved. A legacy non-empty config directory without a
manifest, or a directory that still contains foreign files after owned entries are removed, is left
in place for manual review. Inspect the path reported by the command before deleting any remainder.

## Configuration

Expand Down Expand Up @@ -466,26 +469,55 @@ WebSocket transport is off by default. Set `"websockets": true` only if you want

### Remote access

By default opencodex binds to `127.0.0.1` (loopback) and requires no extra authentication.
If you set `"hostname": "0.0.0.0"` to expose the proxy on the LAN, opencodex requires a bearer token
to protect both the management API (`/api/*`) and the data-plane (`/v1/responses`,
`/v1/images/generations`, and `/v1/images/edits`):
By default opencodex binds to `127.0.0.1` (loopback). Authentication is split by role:

- **Data-plane credentials** — `OPENCODEX_API_AUTH_TOKEN`, the protected `service-api-token` file,
and dashboard-generated `config.apiKeys` authorize only `/v1/*` and the corresponding WebSocket
handshakes. A non-loopback bind requires either `OPENCODEX_API_AUTH_TOKEN` (set directly or
delivered through the protected service file) or at least one `config.apiKeys` entry.
- **Management credentials** — `OPENCODEX_ADMIN_AUTH_TOKEN` or the independent protected
`admin-api-token` file authorize only `/api/*`. The server creates the file when no management
environment token is configured. Service installs persist an explicit management token through
the protected `service-admin-token` delivery file; this is still the same management credential
class, not a fourth credential type.
- **Dashboard sessions** — a legal same-origin loopback dashboard entry receives a short-lived,
Origin-bound session that authorizes only `/api/*`. Remote operators and scripts must use the
management token instead.

**Upgrade note:** earlier releases also admitted `OPENCODEX_API_AUTH_TOKEN` and `config.apiKeys` to
`/api/*`. They are data-plane-only after this split. Update existing management scripts to use
`OPENCODEX_ADMIN_AUTH_TOKEN` or the protected `admin-api-token`; data-plane clients do not need to
change.

For a LAN bind, configure either form of data-plane credential before starting. The environment-token
form is shown below. Set an explicit management token as well when remote operators or automation
need a stable credential:

```bash
export OPENCODEX_API_AUTH_TOKEN="your-secret-token"
export OPENCODEX_API_AUTH_TOKEN="your-data-plane-token"
export OPENCODEX_ADMIN_AUTH_TOKEN="your-management-token"
ocx start
```

The proxy refuses to start without this variable when binding beyond loopback. If you install a
background service for LAN access, export the same variable before `ocx service install` so the
service manager receives it.
Clients (scripts, remote machines) must include the token in every request:
Export the same variables before `ocx service install`. The installer writes their values to the
protected `service-api-token` and `service-admin-token` delivery files; service definitions store
only those file paths, never the raw token values.
Data-plane clients and management clients use the same header name, but their credential values are
not interchangeable:

```
x-opencodex-api-key: your-secret-token
x-opencodex-api-key: the-token-for-this-request-plane
```

The token is compared in constant time to prevent timing attacks.
Use `x-opencodex-api-key` for compatibility across the data plane. `Authorization: Bearer …` is
also accepted by management routes and data routes that permit bearer admission, but
`/v1/responses`, `/v1/responses/compact`, `/v1/chat/completions`, and the Responses WebSocket
handshake require the dedicated `x-opencodex-api-key` header.

If the management token cannot be created, permission-hardened, or read, `/api/*` returns `503` and
never falls back to loopback trust. `/v1/*` and the unauthenticated `/healthz` endpoint continue to
operate. Long-lived data-plane and management secrets, plus GUI CSRF tokens, use constant-time
comparisons; short-lived high-entropy GUI session ids use an in-memory lookup.

opencodex automatically remaps Codex resume history so old OpenAI chats and opencodex-created project
threads stay visible in Codex App while the proxy is active. opencodex records the original provider/source metadata in
Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/content/docs/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ they expire or the proxy restarts. Only a dashboard bound to a non-loopback host
the admin token (`OPENCODEX_ADMIN_AUTH_TOKEN`, or the auto-generated
`~/.opencodex/admin-api-token` file).

Dashboard responses set `X-Frame-Options: DENY` and CSP `frame-ancestors 'none'` to prevent
clickjacking. Embedding the dashboard in an `iframe` is intentionally unsupported; open it as a
top-level page through `ocx gui`.

## What you can do

| Area | What it does |
Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/content/docs/ja/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ ocx start
bun run dev:gui
```

クリックジャッキングを防ぐため、ダッシュボード応答には `X-Frame-Options: DENY` と CSP
`frame-ancestors 'none'` が設定されます。ダッシュボードの `iframe` 埋め込みは意図的に
サポートされません。`ocx gui` からトップレベルページとして開いてください。

## できること

| 領域 | 機能 |
Expand Down
5 changes: 4 additions & 1 deletion docs-site/src/content/docs/ja/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ ocx status --json
### `ocx uninstall`  ·  `ocx remove`

サービスとプロキシを停止し、サービスと Codex shim を削除したのちネイティブ Codex を復元します。すべての
復元ステップが成功した場合にのみ opencodex のローカル設定まで消します。`remove` は `uninstall` の別名です。
復元ステップが成功した場合にのみ opencodex 所有のローカル状態を削除します。削除はインストール所有権
マニフェストに従い、記録されていない項目は保持します。マニフェストがない旧版の空でない設定ディレクトリや、
所有項目の削除後も外部ファイルが残るディレクトリは手動確認のためそのまま残ります。残りを手動削除する前に
報告されたパスを確認してください。`remove` は `uninstall` の別名です。

## モデルと Codex

Expand Down
Loading
Loading