From d13423b9dd476b218ac1922efd65307e17e6a2c6 Mon Sep 17 00:00:00 2001 From: tw19880217-creator Date: Wed, 6 May 2026 02:24:34 -0500 Subject: [PATCH] Fix IPv6 listen default (#470) Co-authored-by: KK --- docs/docker.md | 1 + packages/server/src/config.ts | 8 ++++++++ packages/server/src/index.ts | 6 ++++-- tests/server/config.test.ts | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/server/config.test.ts diff --git a/docs/docker.md b/docs/docker.md index da95081..fef3cda 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -34,6 +34,7 @@ All key runtime settings are configured from compose variables. | Variable | Default | Description | |---|---|---| | `PORT` | `6060` | Web UI listen port | +| `BIND_HOST` | Node default | Optional Web UI bind host. Leave unset for IPv6 dual-stack when available, or set `0.0.0.0` / `::` explicitly. | | `UPSTREAM` | `http://hermes-agent:8642` | Hermes gateway URL (container internal) | | `HERMES_BIN` | `/opt/hermes/.venv/bin/hermes` | Path to Hermes CLI binary | | `HERMES_AGENT_IMAGE` | `nousresearch/hermes-agent:latest` | Hermes Agent base image | diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index d684910..7acfeaf 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -1,8 +1,16 @@ import { resolve } from 'path' import { homedir } from 'os' +export function getListenHost(env: Record = process.env): string | undefined { + const host = env.BIND_HOST?.trim() + return host || undefined +} + export const config = { port: parseInt(process.env.PORT || '8648', 10), + // Leave host undefined by default so Node binds to IPv6 when available, + // falling back to IPv4 on systems without IPv6 support. + host: getListenHost(), upstream: process.env.UPSTREAM || 'http://127.0.0.1:8642', uploadDir: process.env.UPLOAD_DIR || resolve(homedir(), '.hermes-web-ui', 'upload'), dataDir: resolve(__dirname, '..', 'data'), diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 7fca90b..10e27f1 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -102,8 +102,10 @@ export async function bootstrap() { console.log('[bootstrap] SPA fallback registered') // Start server - console.log(`[bootstrap] listening on port ${config.port}`) - server = app.listen(config.port, '0.0.0.0') + console.log(`[bootstrap] listening on ${config.host || 'default host'}:${config.port}`) + server = config.host + ? app.listen(config.port, config.host) + : app.listen(config.port) console.log('[bootstrap] app.listen called') setupTerminalWebSocket(server) diff --git a/tests/server/config.test.ts b/tests/server/config.test.ts new file mode 100644 index 0000000..63d60ab --- /dev/null +++ b/tests/server/config.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest' +import { getListenHost } from '../../packages/server/src/config' + +describe('server config', () => { + it('does not force an IPv4 bind host by default', () => { + expect(getListenHost({})).toBeUndefined() + }) + + it('uses BIND_HOST when provided', () => { + expect(getListenHost({ BIND_HOST: ' :: ' })).toBe('::') + }) + + it('ignores blank BIND_HOST values', () => { + expect(getListenHost({ BIND_HOST: ' ' })).toBeUndefined() + }) +})