import { existsSync, readFileSync, unlinkSync } from 'node:fs' import { join, dirname } from 'node:path' import { tmpdir } from 'node:os' import { execSync } from 'node:child_process' import { fileURLToPath } from 'node:url' const __dirname = dirname(fileURLToPath(import.meta.url)) /** Default password for the CoT trust store (document in atak-itak.md). */ export const TRUSTSTORE_PASSWORD = 'kestrelos' /** Default CoT server port. */ export const DEFAULT_COT_PORT = 8089 /** * CoT port from env or default. * @returns {number} Port number (COT_PORT env or DEFAULT_COT_PORT). */ export function getCotPort() { return Number(process.env.COT_PORT ?? DEFAULT_COT_PORT) } /** Message when an endpoint requires TLS but server is not using it. */ export const COT_TLS_REQUIRED_MESSAGE = 'Only available when the server runs with SSL (e.g. .dev-certs or COT_SSL_*).' /** * Resolve CoT server TLS cert and key paths (for plugin and API). * @param {{ cotSslCert?: string, cotSslKey?: string }} [config] - Runtime config (optional) * @returns {{ certPath: string, keyPath: string } | null} Paths when TLS is configured, else null. */ export function getCotSslPaths(config = {}) { if (process.env.COT_SSL_CERT && process.env.COT_SSL_KEY) { return { certPath: process.env.COT_SSL_CERT, keyPath: process.env.COT_SSL_KEY } } if (config.cotSslCert && config.cotSslKey) { return { certPath: config.cotSslCert, keyPath: config.cotSslKey } } const candidates = [ join(process.cwd(), '.dev-certs', 'cert.pem'), join(__dirname, '../../.dev-certs', 'cert.pem'), ] for (const certPath of candidates) { const keyPath = certPath.replace('cert.pem', 'key.pem') if (existsSync(certPath) && existsSync(keyPath)) { return { certPath, keyPath } } } return null } /** * Build a P12 trust store from a PEM cert path (for truststore download and server package). * @param {string} certPath - Path to cert.pem * @param {string} password - P12 password * @returns {Buffer} P12 buffer * @throws {Error} If openssl fails */ export function buildP12FromCertPath(certPath, password) { const outPath = join(tmpdir(), `kestrelos-cot-p12-${Date.now()}.p12`) try { execSync( `openssl pkcs12 -export -nokeys -in "${certPath}" -out "${outPath}" -passout pass:${password}`, { stdio: 'pipe' }, ) const p12 = readFileSync(outPath) unlinkSync(outPath) return p12 } catch (err) { if (existsSync(outPath)) unlinkSync(outPath) throw err } }