From dc797c29560526c09df99e020810b735ee4a0f0e Mon Sep 17 00:00:00 2001 From: Michael Fork Date: Sat, 31 Jan 2026 21:46:43 +0000 Subject: [PATCH 1/2] feat: add HOST environment variable for configurable bind address Allow users to specify which host/IP address the servers should bind to via the HOST environment variable. Defaults to 0.0.0.0 (all interfaces) to maintain backward compatibility. This enables users to restrict the server to localhost only (127.0.0.1) for security purposes or bind to a specific network interface. Co-Authored-By: Claude Opus 4.5 --- .env.example | 4 ++++ server/index.js | 5 +++-- vite.config.js | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 4b3cbbc2a..119859930 100755 --- a/.env.example +++ b/.env.example @@ -21,6 +21,10 @@ PORT=3001 #Frontend port VITE_PORT=5173 +# Host/IP to bind servers to (default: 0.0.0.0 for all interfaces) +# Use 127.0.0.1 to restrict to localhost only +HOST=0.0.0.0 + # Uncomment the following line if you have a custom claude cli path other than the default "claude" # CLAUDE_CLI_PATH=claude diff --git a/server/index.js b/server/index.js index 1c42d305a..cc276e703 100755 --- a/server/index.js +++ b/server/index.js @@ -1798,6 +1798,7 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = } const PORT = process.env.PORT || 3001; +const HOST = process.env.HOST || '0.0.0.0'; // Initialize database and start server async function startServer() { @@ -1817,7 +1818,7 @@ async function startServer() { console.log(`${c.warn('[WARN]')} Note: Requests will be proxied to Vite dev server at ${c.dim('http://localhost:' + (process.env.VITE_PORT || 5173))}`); } - server.listen(PORT, '0.0.0.0', async () => { + server.listen(PORT, HOST, async () => { const appInstallPath = path.join(__dirname, '..'); console.log(''); @@ -1825,7 +1826,7 @@ async function startServer() { console.log(` ${c.bright('Claude Code UI Server - Ready')}`); console.log(c.dim('═'.repeat(63))); console.log(''); - console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://0.0.0.0:' + PORT)}`); + console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://' + HOST + ':' + PORT)}`); console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`); console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`); console.log(''); diff --git a/vite.config.js b/vite.config.js index ee2d943be..4c3e6d938 100755 --- a/vite.config.js +++ b/vite.config.js @@ -9,6 +9,7 @@ export default defineConfig(({ command, mode }) => { return { plugins: [react()], server: { + host: env.HOST || '0.0.0.0', port: parseInt(env.VITE_PORT) || 5173, proxy: { '/api': `http://localhost:${env.PORT || 3001}`, From dc06b6ab36c6b1404e68f61d8a049457986410ce Mon Sep 17 00:00:00 2001 From: Michael Fork Date: Sun, 1 Feb 2026 14:09:52 +0000 Subject: [PATCH 2/2] fix: use correct proxy host when HOST is set to specific interface When HOST is set to a specific interface (e.g., 192.168.1.100), the Vite proxy needs to connect to that same host, not localhost. This fix: - Adds proxyHost logic that uses localhost when HOST=0.0.0.0, otherwise uses the specific HOST value for proxy targets - Adds DISPLAY_HOST to show a user-friendly URL in console output (0.0.0.0 isn't a connectable address) Addresses CodeRabbit review feedback. Co-Authored-By: Claude Opus 4.5 --- server/index.js | 4 +++- vite.config.js | 17 +++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/server/index.js b/server/index.js index cc276e703..1c6bedce3 100755 --- a/server/index.js +++ b/server/index.js @@ -1799,6 +1799,8 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = const PORT = process.env.PORT || 3001; const HOST = process.env.HOST || '0.0.0.0'; +// Show localhost in URL when binding to all interfaces (0.0.0.0 isn't a connectable address) +const DISPLAY_HOST = HOST === '0.0.0.0' ? 'localhost' : HOST; // Initialize database and start server async function startServer() { @@ -1826,7 +1828,7 @@ async function startServer() { console.log(` ${c.bright('Claude Code UI Server - Ready')}`); console.log(c.dim('═'.repeat(63))); console.log(''); - console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://' + HOST + ':' + PORT)}`); + console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://' + DISPLAY_HOST + ':' + PORT)}`); console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`); console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`); console.log(''); diff --git a/vite.config.js b/vite.config.js index 4c3e6d938..48030b288 100755 --- a/vite.config.js +++ b/vite.config.js @@ -4,21 +4,26 @@ import react from '@vitejs/plugin-react' export default defineConfig(({ command, mode }) => { // Load env file based on `mode` in the current working directory. const env = loadEnv(mode, process.cwd(), '') - - + + const host = env.HOST || '0.0.0.0' + // When binding to all interfaces (0.0.0.0), proxy should connect to localhost + // Otherwise, proxy to the specific host the backend is bound to + const proxyHost = host === '0.0.0.0' ? 'localhost' : host + const port = env.PORT || 3001 + return { plugins: [react()], server: { - host: env.HOST || '0.0.0.0', + host, port: parseInt(env.VITE_PORT) || 5173, proxy: { - '/api': `http://localhost:${env.PORT || 3001}`, + '/api': `http://${proxyHost}:${port}`, '/ws': { - target: `ws://localhost:${env.PORT || 3001}`, + target: `ws://${proxyHost}:${port}`, ws: true }, '/shell': { - target: `ws://localhost:${env.PORT || 3001}`, + target: `ws://${proxyHost}:${port}`, ws: true } }