Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ RUN npm ci && npm run build
# ── Stage 2: Application ────────────────────────────────────────────
FROM node:20-slim

# The checked-in default is loopback-only for local source installs. Containers
# need to listen on all interfaces inside the network namespace so published
# ports and Docker health checks can reach the service.
ENV CODEX_PROXY_HOST=0.0.0.0

# curl: needed by full-update.ts
# unzip: needed by full-update.ts to extract Codex.app
# gosu: needed by entrypoint to drop from root to node user
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -572,14 +572,16 @@ model:

### 局域网访问

源码/容器默认配置监听 `::`(IPv6 unspecified,通常也覆盖本机访问);Electron 启动时会传入 `127.0.0.1`,除非 `data/local.yaml` 显式覆盖。建议需要仅本机访问时写入:
源码默认配置仅监听 `127.0.0.1`;Electron 也会传入 `127.0.0.1`,除非 `data/local.yaml` 显式覆盖。Docker 镜像会通过 `CODEX_PROXY_HOST=0.0.0.0` 在容器内监听所有接口,`docker-compose.yml` 默认仍只把宿主机端口绑定到 `127.0.0.1`。

需要仅本机访问时写入:

```yaml
server:
host: "127.0.0.1"
```

如需局域网内其他设备访问,在 `data/local.yaml` 中添加:
如需局域网内其他设备访问,在 `data/local.yaml` 中添加,并把 `docker-compose.yml` 的端口映射从 `127.0.0.1:${PORT:-8080}:8080` 改成 `${PORT:-8080}:8080`

```yaml
server:
Expand Down Expand Up @@ -711,6 +713,7 @@ curl -N http://localhost:8080/official-agent/threads/{threadId}/turns \
| 环境变量 | 覆盖配置 |
|---------|---------|
| `PORT` | `server.port` |
| `CODEX_PROXY_HOST` | `server.host`(仅当 `data/local.yaml` 未显式设置 `server.host` 时生效) |
| `CODEX_PLATFORM` | `client.platform` |
| `CODEX_ARCH` | `client.arch` |
| `HTTPS_PROXY` | `tls.proxy_url` |
Expand Down
2 changes: 1 addition & 1 deletion config/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ auth:
oauth_auth_endpoint: https://auth.openai.com/oauth/authorize
oauth_token_endpoint: https://auth.openai.com/oauth/token
server:
host: "::"
host: "127.0.0.1"
port: 8080
proxy_api_key: null
trust_proxy: false
Expand Down
5 changes: 4 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ services:
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "${PORT:-8080}:8080"
# Loopback-only on the host by default; change to "${PORT:-8080}:8080"
# if you intentionally want LAN access.
- "127.0.0.1:${PORT:-8080}:8080"
- "1455:1455"
# Optional Ollama-compatible bridge. Enable ollama.enabled and use
# ollama.host=0.0.0.0 inside the container before uncommenting.
Expand All @@ -22,6 +24,7 @@ services:
environment:
- NODE_ENV=production
- PORT=8080
- CODEX_PROXY_HOST=0.0.0.0

# -- Automatic updates (uncomment to enable) --
# watchtower:
Expand Down
7 changes: 7 additions & 0 deletions src/config-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ export function applyEnvOverrides(
(raw.server as Record<string, unknown>).port = parsed;
}
}
const serverHostEnv = process.env.CODEX_PROXY_HOST?.trim();
const localServer = localOverrides?.server as Record<string, unknown> | undefined;
const localHasServerHost = localServer !== undefined && "host" in localServer;
if (serverHostEnv && !localHasServerHost) {
if (!raw.server) raw.server = {};
(raw.server as Record<string, unknown>).host = serverHostEnv;
}
const ollamaEnabledEnv = process.env.OLLAMA_BRIDGE_ENABLED?.trim().toLowerCase();
const ollamaHostEnv = process.env.OLLAMA_BRIDGE_HOST?.trim();
const ollamaPortEnv = process.env.OLLAMA_BRIDGE_PORT?.trim();
Expand Down
2 changes: 1 addition & 1 deletion src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const ConfigSchema = z.object({
oauth_token_endpoint: z.string().default("https://auth.openai.com/oauth/token"),
}),
server: z.object({
host: z.string().default("0.0.0.0"),
host: z.string().default("127.0.0.1"),
port: z.number().min(1).max(65535).default(8080),
proxy_api_key: z.string().nullable().default(null),
trust_proxy: z.boolean().default(false),
Expand Down
16 changes: 16 additions & 0 deletions tests/unit/config-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ describe("applyEnvOverrides", () => {
savedEnv.CODEX_JWT_TOKEN = process.env.CODEX_JWT_TOKEN;
savedEnv.CODEX_PLATFORM = process.env.CODEX_PLATFORM;
savedEnv.CODEX_ARCH = process.env.CODEX_ARCH;
savedEnv.CODEX_PROXY_HOST = process.env.CODEX_PROXY_HOST;
savedEnv.PORT = process.env.PORT;
savedEnv.HTTPS_PROXY = process.env.HTTPS_PROXY;
savedEnv.https_proxy = process.env.https_proxy;
Expand All @@ -332,6 +333,7 @@ describe("applyEnvOverrides", () => {
delete process.env.CODEX_JWT_TOKEN;
delete process.env.CODEX_PLATFORM;
delete process.env.CODEX_ARCH;
delete process.env.CODEX_PROXY_HOST;
delete process.env.PORT;
delete process.env.HTTPS_PROXY;
delete process.env.https_proxy;
Expand Down Expand Up @@ -371,6 +373,20 @@ describe("applyEnvOverrides", () => {
expect((raw.server as Record<string, unknown>).port).toBe(3000);
});

it("applies CODEX_PROXY_HOST when local.yaml has no server.host", () => {
process.env.CODEX_PROXY_HOST = "0.0.0.0";
const raw = { server: { host: "127.0.0.1" }, auth: {} } as Record<string, unknown>;
applyEnvOverrides(raw, null);
expect((raw.server as Record<string, unknown>).host).toBe("0.0.0.0");
});

it("does not override explicit local.yaml server.host with CODEX_PROXY_HOST", () => {
process.env.CODEX_PROXY_HOST = "0.0.0.0";
const raw = { server: { host: "127.0.0.1" }, auth: {} } as Record<string, unknown>;
applyEnvOverrides(raw, { server: { host: "127.0.0.1" } });
expect((raw.server as Record<string, unknown>).host).toBe("127.0.0.1");
});

it("ignores non-numeric PORT", () => {
process.env.PORT = "abc";
const raw = { server: { port: 8080 }, auth: {} } as Record<string, unknown>;
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/config-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe("ConfigSchema", () => {
expect(result.api.base_url).toBe("https://chatgpt.com/backend-api");
expect(result.api.timeout_seconds).toBe(60);
expect(result.server.port).toBe(8080);
expect(result.server.host).toBe("0.0.0.0");
expect(result.server.host).toBe("127.0.0.1");
expect(result.server.proxy_api_key).toBeNull();
expect(result.auth.rotation_strategy).toBe("least_used");
expect(result.auth.refresh_concurrency).toBe(2);
Expand Down
Loading