import { machineIdSync } from 'node-machine-id'; import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; import { DATA_DIR } from '@/lib/dataDir'; const MACHINE_ID_FILE = path.join(DATA_DIR, 'machine-id'); const AUTH_DIR = path.join(DATA_DIR, 'auth'); const CLI_SECRET_FILE = path.join(AUTH_DIR, 'cli-secret'); const CLI_AUTH_SALT = '9r-cli-auth'; let cachedRawId = null; let cachedCliSecret = null; // Persist raw machine ID to file → guarantees CLI/server/middleware see same value // even when machineIdSync fails or returns inconsistent values across runtimes. function loadRawMachineId() { if (cachedRawId) return cachedRawId; try { cachedRawId = fs.readFileSync(MACHINE_ID_FILE, 'utf8').trim(); if (cachedRawId) return cachedRawId; } catch {} try { cachedRawId = machineIdSync(); } catch { cachedRawId = crypto.randomUUID(); } try { fs.mkdirSync(DATA_DIR, { recursive: true }); fs.writeFileSync(MACHINE_ID_FILE, cachedRawId, { mode: 0o600 }); } catch {} return cachedRawId; } // Random secret persisted on first run → unpredictable CLI token even when machineId leaks. function loadCliSecret() { if (cachedCliSecret) return cachedCliSecret; try { cachedCliSecret = fs.readFileSync(CLI_SECRET_FILE, 'utf8').trim(); if (cachedCliSecret) return cachedCliSecret; } catch {} cachedCliSecret = crypto.randomBytes(32).toString('hex'); try { fs.mkdirSync(AUTH_DIR, { recursive: true }); fs.writeFileSync(CLI_SECRET_FILE, cachedCliSecret, { mode: 0o600 }); } catch {} return cachedCliSecret; } export async function getConsistentMachineId(salt = null) { const saltValue = salt || process.env.MACHINE_ID_SALT || 'endpoint-proxy-salt'; const raw = loadRawMachineId(); const extra = saltValue === CLI_AUTH_SALT ? loadCliSecret() : ''; return crypto.createHash('sha256').update(raw + saltValue + extra).digest('hex').substring(0, 16); } export async function getRawMachineId() { return loadRawMachineId(); } /** * Check if we're running in browser or server environment * @returns {boolean} True if in browser, false if in server */ export function isBrowser() { return typeof window !== 'undefined'; }