import { execFileSync } from 'node:child_process' import { createHash, randomUUID } from 'node:crypto' import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { hostname, networkInterfaces } from 'node:os' import type { NetworkInterfaceInfo } from 'node:os' import { join } from 'node:path' import { app } from 'electron/main' const STABLE_CLIENT_NAMESPACE = 'geo-rankly.desktop-client.v1' const FALLBACK_INSTALLATION_ID_FILE = 'machine-id-fallback.json' export interface DesktopDeviceInfo { device_name: string os: string cpu_arch: string } export interface DesktopClientIDScope { tenant_id: number workspace_id: number user_id: number } export function resolveDesktopDeviceInfo(): DesktopDeviceInfo { return { device_name: hostname() || `Desktop ${process.platform}`, os: process.platform, cpu_arch: process.arch, } } export function resolveDesktopClientID(scope: DesktopClientIDScope): string { const tenantID = normalizePositiveInteger(scope.tenant_id) const workspaceID = normalizePositiveInteger(scope.workspace_id) const userID = normalizePositiveInteger(scope.user_id) if (tenantID === null || workspaceID === null || userID === null) { throw new Error('desktop_client_id_scope_invalid') } return uuidFromSeed( `${STABLE_CLIENT_NAMESPACE}:${stableMachineIdentitySeed()}:account:${tenantID}:${workspaceID}:${userID}`, ) } function stableMachineIdentitySeed(): string { const machineID = resolveOSMachineID() if (machineID) { return `machine:${machineID}` } const networkFingerprint = resolveNetworkHardwareFingerprint() if (networkFingerprint) { return `network:${networkFingerprint}` } return `installation:${resolvePersistedInstallationID()}` } function resolveOSMachineID(): string | null { if (process.platform === 'darwin') { return normalizeMachineID(readMacOSPlatformUUID()) } if (process.platform === 'win32') { return normalizeMachineID(readWindowsMachineGuid()) } if (process.platform === 'linux') { return normalizeMachineID(readLinuxMachineID()) } return null } function readMacOSPlatformUUID(): string | null { try { const output = execFileSync('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], { encoding: 'utf8', timeout: 1500, }) return output.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/)?.[1] ?? null } catch { return null } } function readWindowsMachineGuid(): string | null { try { const output = execFileSync( 'reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\Cryptography', '/v', 'MachineGuid'], { encoding: 'utf8', timeout: 1500, windowsHide: true, }, ) return output.match(/MachineGuid\s+REG_SZ\s+([^\r\n]+)/i)?.[1] ?? null } catch { return null } } function readLinuxMachineID(): string | null { for (const path of ['/etc/machine-id', '/var/lib/dbus/machine-id']) { try { if (existsSync(path)) { const value = readFileSync(path, 'utf8').trim() if (value) { return value } } } catch { // Try the next known location. } } return null } function normalizeMachineID(value: string | null): string | null { const normalized = (value ?? '').trim().toLowerCase() if (!normalized || normalized === 'unknown') { return null } return createHash('sha256').update(normalized).digest('hex') } function resolveNetworkHardwareFingerprint(): string | null { const macs = Object.values(networkInterfaces()) .flatMap((items) => items ?? []) .filter(isStableHardwareNetworkInterface) .map((item) => normalizeMacAddress(item.mac)) .filter((mac): mac is string => Boolean(mac)) const uniqueMacs = [...new Set(macs)].sort() if (!uniqueMacs.length) { return null } return createHash('sha256').update(uniqueMacs.join('|')).digest('hex') } function isStableHardwareNetworkInterface(item: NetworkInterfaceInfo): boolean { const mac = normalizeMacAddress(item.mac) if (!mac || item.internal) { return false } const bytes = mac.split(':').map((part) => Number.parseInt(part, 16)) if (bytes.length !== 6 || bytes.some((byte) => !Number.isFinite(byte))) { return false } const firstByte = bytes[0] ?? 0 const isMulticast = (firstByte & 0x01) === 0x01 const isLocallyAdministered = (firstByte & 0x02) === 0x02 return !isMulticast && !isLocallyAdministered } function normalizeMacAddress(value: string | undefined): string | null { const normalized = (value ?? '').trim().toLowerCase() if (!normalized || normalized === '00:00:00:00:00:00') { return null } if (!/^[0-9a-f]{2}(:[0-9a-f]{2}){5}$/.test(normalized)) { return null } return normalized } function resolvePersistedInstallationID(): string { const existing = readPersistedInstallationID() if (existing) { return existing } const generated = randomUUID() persistInstallationID(generated) return generated } function readPersistedInstallationID(): string | null { try { const data = JSON.parse(readFileSync(fallbackInstallationIDPath(), 'utf8')) as { installation_id?: unknown } return normalizeInstallationID(data.installation_id) } catch { return null } } function persistInstallationID(installationID: string): void { const dir = app.getPath('userData') mkdirSync(dir, { recursive: true }) writeFileSync( fallbackInstallationIDPath(), JSON.stringify({ installation_id: installationID }), 'utf8', ) } function fallbackInstallationIDPath(): string { return join(app.getPath('userData'), FALLBACK_INSTALLATION_ID_FILE) } function normalizeInstallationID(value: unknown): string | null { if (typeof value !== 'string') { return null } const normalized = value.trim().toLowerCase() if ( !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(normalized) ) { return null } return normalized } function normalizePositiveInteger(value: unknown): number | null { if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) { return null } return value } function uuidFromSeed(seed: string): string { const chars = createHash('sha256').update(seed).digest('hex').slice(0, 32).split('') chars[12] = '5' chars[16] = (((Number.parseInt(chars[16] ?? '0', 16) || 0) & 0x3) | 0x8).toString(16) return [ chars.slice(0, 8).join(''), chars.slice(8, 12).join(''), chars.slice(12, 16).join(''), chars.slice(16, 20).join(''), chars.slice(20, 32).join(''), ].join('-') }