From 8c8815b00ba2275b177a5e8a209aa409d5003993 Mon Sep 17 00:00:00 2001 From: Michael Fork Date: Sat, 31 Jan 2026 21:58:22 +0000 Subject: [PATCH 1/3] feat: add optional SSL/TLS support for HTTPS Add support for running the server with HTTPS by configuring SSL_CERT and SSL_KEY environment variables. When both variables are set and the certificate files exist, the server automatically uses HTTPS instead of HTTP. This enables secure connections for deployments that require encryption, while maintaining full backward compatibility (HTTP remains the default). Co-Authored-By: Claude Opus 4.5 --- .env.example | 9 +++++++++ server/index.js | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 4b3cbbc2a..2646db367 100755 --- a/.env.example +++ b/.env.example @@ -24,6 +24,15 @@ VITE_PORT=5173 # Uncomment the following line if you have a custom claude cli path other than the default "claude" # CLAUDE_CLI_PATH=claude +# ============================================================================= +# SSL/TLS CONFIGURATION (Optional - for HTTPS) +# ============================================================================= + +# To enable HTTPS, provide paths to your SSL certificate and private key. +# When both are set and files exist, the server will use HTTPS instead of HTTP. +# SSL_CERT=/path/to/your/certificate.crt +# SSL_KEY=/path/to/your/private.key + # ============================================================================= # DATABASE CONFIGURATION # ============================================================================= diff --git a/server/index.js b/server/index.js index 1c42d305a..06ca84a3d 100755 --- a/server/index.js +++ b/server/index.js @@ -50,6 +50,7 @@ import express from 'express'; import { WebSocketServer, WebSocket } from 'ws'; import os from 'os'; import http from 'http'; +import https from 'https'; import cors from 'cors'; import { promises as fsPromises } from 'fs'; import { spawn } from 'child_process'; @@ -188,7 +189,22 @@ async function setupProjectsWatcher() { const app = express(); -const server = http.createServer(app); + +// Create HTTP or HTTPS server based on SSL configuration +let server; +const SSL_CERT = process.env.SSL_CERT; +const SSL_KEY = process.env.SSL_KEY; + +if (SSL_CERT && SSL_KEY && fs.existsSync(SSL_CERT) && fs.existsSync(SSL_KEY)) { + const sslOptions = { + cert: fs.readFileSync(SSL_CERT), + key: fs.readFileSync(SSL_KEY) + }; + server = https.createServer(sslOptions, app); + console.log('[INFO] HTTPS enabled with SSL certificates'); +} else { + server = http.createServer(app); +} const ptySessionsMap = new Map(); const PTY_SESSION_TIMEOUT = 30 * 60 * 1000; @@ -1825,7 +1841,8 @@ 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)}`); + const protocol = (SSL_CERT && SSL_KEY) ? 'https' : 'http'; + console.log(`${c.info('[INFO]')} Server URL: ${c.bright(protocol + '://0.0.0.0:' + 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(''); From b742b8a88fd499b35abc68bd9e7c8ef8f6940065 Mon Sep 17 00:00:00 2001 From: Michael Fork <1575815+mjfork@users.noreply.github.com> Date: Sat, 31 Jan 2026 22:10:57 +0000 Subject: [PATCH 2/3] fix: add error handling for SSL certificate loading - Wrap SSL certificate reading and HTTPS server creation in try/catch - Add useHttps flag to track actual server state - Update protocol log to use useHttps flag instead of env vars - Gracefully fallback to HTTP if certificate loading fails Addresses code review feedback from CodeRabbit. Co-Authored-By: Claude Opus 4.5 --- server/index.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/server/index.js b/server/index.js index 06ca84a3d..e8afc876c 100755 --- a/server/index.js +++ b/server/index.js @@ -192,16 +192,23 @@ const app = express(); // Create HTTP or HTTPS server based on SSL configuration let server; +let useHttps = false; const SSL_CERT = process.env.SSL_CERT; const SSL_KEY = process.env.SSL_KEY; if (SSL_CERT && SSL_KEY && fs.existsSync(SSL_CERT) && fs.existsSync(SSL_KEY)) { - const sslOptions = { - cert: fs.readFileSync(SSL_CERT), - key: fs.readFileSync(SSL_KEY) - }; - server = https.createServer(sslOptions, app); - console.log('[INFO] HTTPS enabled with SSL certificates'); + try { + const sslOptions = { + cert: fs.readFileSync(SSL_CERT), + key: fs.readFileSync(SSL_KEY) + }; + server = https.createServer(sslOptions, app); + useHttps = true; + console.log('[INFO] HTTPS enabled with SSL certificates'); + } catch (err) { + console.warn('[WARN] Failed to enable HTTPS, falling back to HTTP:', err.message); + server = http.createServer(app); + } } else { server = http.createServer(app); } @@ -1841,7 +1848,7 @@ async function startServer() { console.log(` ${c.bright('Claude Code UI Server - Ready')}`); console.log(c.dim('═'.repeat(63))); console.log(''); - const protocol = (SSL_CERT && SSL_KEY) ? 'https' : 'http'; + const protocol = useHttps ? 'https' : 'http'; console.log(`${c.info('[INFO]')} Server URL: ${c.bright(protocol + '://0.0.0.0:' + PORT)}`); console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`); console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`); From 5f683829e8aba60b5a256149b8ba4a4da3cc0590 Mon Sep 17 00:00:00 2001 From: Michael Fork <1575815+mjfork@users.noreply.github.com> Date: Sat, 31 Jan 2026 22:22:05 +0000 Subject: [PATCH 3/3] feat: add SSL_PORT support for separate HTTPS port Allow configuring a separate port for HTTPS via SSL_PORT environment variable. When SSL is enabled and SSL_PORT is set, the server listens on that port. Otherwise, it falls back to PORT. This enables common setups where HTTP runs on port 3001 and HTTPS runs on port 443. Co-Authored-By: Claude Opus 4.5 --- .env.example | 3 +++ server/index.js | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 2646db367..87837f063 100755 --- a/.env.example +++ b/.env.example @@ -33,6 +33,9 @@ VITE_PORT=5173 # SSL_CERT=/path/to/your/certificate.crt # SSL_KEY=/path/to/your/private.key +# Optional: Specify a different port for HTTPS (defaults to PORT if not set) +# SSL_PORT=443 + # ============================================================================= # DATABASE CONFIGURATION # ============================================================================= diff --git a/server/index.js b/server/index.js index e8afc876c..811299b9d 100755 --- a/server/index.js +++ b/server/index.js @@ -1821,6 +1821,7 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = } const PORT = process.env.PORT || 3001; +const SSL_PORT = process.env.SSL_PORT || PORT; // Initialize database and start server async function startServer() { @@ -1840,7 +1841,8 @@ 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 () => { + const listenPort = useHttps ? SSL_PORT : PORT; + server.listen(listenPort, '0.0.0.0', async () => { const appInstallPath = path.join(__dirname, '..'); console.log(''); @@ -1849,7 +1851,7 @@ async function startServer() { console.log(c.dim('═'.repeat(63))); console.log(''); const protocol = useHttps ? 'https' : 'http'; - console.log(`${c.info('[INFO]')} Server URL: ${c.bright(protocol + '://0.0.0.0:' + PORT)}`); + console.log(`${c.info('[INFO]')} Server URL: ${c.bright(protocol + '://0.0.0.0:' + listenPort)}`); console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`); console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`); console.log('');