From ea6d4c4d434c23e8495b7fb135c4fb105e23c0b7 Mon Sep 17 00:00:00 2001 From: yslee Date: Mon, 23 Mar 2026 22:48:04 +0900 Subject: [PATCH] feat(server): support configurable hostname for managed OpenCode server spawn (#599) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(server): support configurable hostname for managed OpenCode server spawn Allow the managed OpenCode server bind hostname to be configured via OPENCHAMBER_OPENCODE_HOSTNAME environment variable (default: 127.0.0.1). This enables LAN/Tailscale access without a reverse proxy by setting the hostname to 0.0.0.0. Closes #597 * fix: address review feedback — input validation, port probe hostname, security docs - Add defensive parsing for OPENCHAMBER_OPENCODE_HOSTNAME with trim/empty check and warning log, matching OPENCODE_HOST validation pattern - Pass configured hostname to resolveManagedOpenCodePort() so port availability is probed on the actual bind address, avoiding EADDRINUSE - Add security note in README docs warning about 0.0.0.0 exposure on untrusted networks --- README.md | 5 +++++ docker-compose.yml | 1 + packages/web/README.md | 28 ++++++++++++++++++++++++++++ packages/web/bin/cli.js | 1 + packages/web/server/index.js | 23 +++++++++++++++++++---- 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5aa52046..edcd8404 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,11 @@ OPENCODE_PORT=4096 OPENCODE_SKIP_START=true openchamber OPENCODE_HOST=https://myhost:4096 OPENCODE_SKIP_START=true openchamber ``` +Bind managed OpenCode server to all interfaces (use only on trusted networks): +```bash +OPENCHAMBER_OPENCODE_HOSTNAME=0.0.0.0 openchamber --port 3000 +``` +
diff --git a/docker-compose.yml b/docker-compose.yml index e1366b49..f2a0da46 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,4 +26,5 @@ services: # OH_MY_OPENCODE: true # enable oh-my-opencode # OPENCODE_HOST: http://172.17.0.1:4096 # Connect to external OpenCode server # OPENCODE_SKIP_START: true # skip start opencode + # OPENCHAMBER_OPENCODE_HOSTNAME: 0.0.0.0 # Bind OpenCode to all interfaces restart: unless-stopped diff --git a/packages/web/README.md b/packages/web/README.md index 94031174..50259dc3 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -46,6 +46,34 @@ openchamber update # Update to latest version - Replacing or stopping a tunnel revokes existing connect links and invalidates remote tunnel sessions. - Connect links are one-time tokens; generating a new link revokes the previous unused link. +
+Connect to external OpenCode server + +```bash +OPENCODE_PORT=4096 OPENCODE_SKIP_START=true openchamber +OPENCODE_HOST=https://myhost:4096 OPENCODE_SKIP_START=true openchamber +``` + +| Variable | Description | +|----------|-------------| +| `OPENCODE_HOST` | Full base URL of external server (overrides `OPENCODE_PORT`) | +| `OPENCODE_PORT` | Port of external server | +| `OPENCODE_SKIP_START` | Skip starting embedded OpenCode server | +| `OPENCHAMBER_OPENCODE_HOSTNAME` | Bind hostname for managed OpenCode server (default: `127.0.0.1`, use `0.0.0.0` for LAN/remote access — trusted networks only) | + +
+ +
+Bind managed OpenCode to LAN / Tailscale + +```bash +OPENCHAMBER_OPENCODE_HOSTNAME=0.0.0.0 openchamber --port 3000 +``` + +**Security note:** binding to `0.0.0.0` exposes the server on all network interfaces — use only on trusted networks and protect with firewall rules or `--ui-password`. + +
+ **Optional env vars:** ```yaml environment: diff --git a/packages/web/bin/cli.js b/packages/web/bin/cli.js index a5753645..72a9c015 100644 --- a/packages/web/bin/cli.js +++ b/packages/web/bin/cli.js @@ -864,6 +864,7 @@ ENVIRONMENT: OPENCODE_HOST External OpenCode server base URL, e.g. http://hostname:4096 OPENCODE_PORT Port of external OpenCode server to connect to OPENCODE_SKIP_START Skip starting OpenCode, use external server + OPENCHAMBER_OPENCODE_HOSTNAME Bind hostname for managed OpenCode server (default: 127.0.0.1) EXAMPLES: openchamber # Start in daemon mode on default port 3000 (or free port) diff --git a/packages/web/server/index.js b/packages/web/server/index.js index 47830dc0..76733a16 100644 --- a/packages/web/server/index.js +++ b/packages/web/server/index.js @@ -3788,6 +3788,21 @@ const ENV_CONFIGURED_OPENCODE_HOST = (() => { // OPENCODE_HOST takes precedence over OPENCODE_PORT when both are set const ENV_EFFECTIVE_PORT = ENV_CONFIGURED_OPENCODE_HOST?.port ?? ENV_CONFIGURED_OPENCODE_PORT; +const ENV_CONFIGURED_OPENCODE_HOSTNAME = (() => { + const raw = process.env.OPENCHAMBER_OPENCODE_HOSTNAME; + if (typeof raw !== 'string') { + return '127.0.0.1'; + } + const trimmed = raw.trim(); + if (!trimmed) { + console.warn( + `[config] Ignoring OPENCHAMBER_OPENCODE_HOSTNAME=${JSON.stringify(raw)}: empty after trimming`, + ); + return '127.0.0.1'; + } + return trimmed; +})(); + const ENV_SKIP_OPENCODE_START = process.env.OPENCODE_SKIP_START === 'true' || process.env.OPENCHAMBER_SKIP_OPENCODE_START === 'true'; const ENV_DESKTOP_NOTIFY = process.env.OPENCHAMBER_DESKTOP_NOTIFY === 'true'; @@ -6049,7 +6064,7 @@ async function createManagedOpenCodeServerProcess({ }; } -async function resolveManagedOpenCodePort(requestedPort) { +async function resolveManagedOpenCodePort(requestedPort, hostname = '127.0.0.1') { if (typeof requestedPort === 'number' && Number.isFinite(requestedPort) && requestedPort > 0) { return requestedPort; } @@ -6079,13 +6094,13 @@ async function resolveManagedOpenCodePort(requestedPort) { }); }); - server.listen(0, '127.0.0.1'); + server.listen(0, hostname); }); } async function startOpenCode() { const desiredPort = ENV_CONFIGURED_OPENCODE_PORT ?? 0; - const spawnPort = await resolveManagedOpenCodePort(desiredPort); + const spawnPort = await resolveManagedOpenCodePort(desiredPort, ENV_CONFIGURED_OPENCODE_HOSTNAME); console.log( desiredPort > 0 ? `Starting OpenCode on requested port ${desiredPort}...` @@ -6100,7 +6115,7 @@ async function startOpenCode() { try { const serverInstance = await createManagedOpenCodeServerProcess({ - hostname: '127.0.0.1', + hostname: ENV_CONFIGURED_OPENCODE_HOSTNAME, port: spawnPort, timeout: 30000, cwd: openCodeWorkingDirectory,