/* * Veskslop daemon — self-signed TLS for the web UI. * * Browser media APIs need a secure context. The daemon generates one * self-signed certificate (openssl) covering localhost and the host's LAN * addresses, and serves the UI over HTTPS. WEBUI_TLS=0 disables this. */ import { execFileSync } from "node:child_process"; import { existsSync, mkdirSync } from "node:fs"; import { networkInterfaces } from "node:os"; import { join } from "node:path"; export interface TlsPaths { key: string; cert: string; } function localAddresses(): string[] { const names = ["DNS:localhost", "IP:127.0.0.1"]; for (const list of Object.values(networkInterfaces())) { for (const info of list ?? []) { if (info.family === "IPv4" && !info.internal) names.push(`IP:${info.address}`); } } return names; } export function ensureTls(storeDir: string): TlsPaths | undefined { if (process.env.WEBUI_TLS === "0") return undefined; const keyOverride = process.env.WEBUI_TLS_KEY; const certOverride = process.env.WEBUI_TLS_CERT; if (keyOverride && certOverride) { return existsSync(keyOverride) && existsSync(certOverride) ? { key: keyOverride, cert: certOverride } : undefined; } const dir = join(storeDir, "tls"); const key = join(dir, "key.pem"); const cert = join(dir, "cert.pem"); if (existsSync(key) && existsSync(cert)) return { key, cert }; try { mkdirSync(dir, { recursive: true }); execFileSync( "openssl", [ "req", "-x509", "-newkey", "rsa:2048", "-nodes", "-keyout", key, "-out", cert, "-days", "825", "-subj", "/CN=veskslop", "-addext", `subjectAltName=${localAddresses().join(",")}` ], { stdio: "ignore" } ); return { key, cert }; } catch (error) { console.warn("[webui] self-signed certificate generation failed:", (error as Error).message); return undefined; } }