wiwiway / src /shared /utils /machineId.ts
Claude Code
Sync gateway codebase without binaries
116b4cb
Raw
History Blame Contribute Delete
4.84 kB
import { execSync, execFileSync } from "child_process";
import { existsSync, readFileSync } from "fs";
/**
* Get raw machine ID using OS-specific methods.
*
* We use try/catch waterfall: try each OS method and fall through
* to the next on failure. Platform checks are INSIDE try blocks so they
* run at RUNTIME (not build time), avoiding Next.js SWC dead-code elimination.
*
* On Linux: skips Windows (REG.exe) and macOS (ioreg) strategies entirely.
*/
function getMachineIdRaw(): string {
// Strategy 1: Windows β€” REG.exe query for MachineGuid
try {
if (process.platform !== "win32") {
throw new Error("Not Windows");
}
const sysRoot = process.env.SystemRoot || process.env.windir || "C:\\Windows";
const regPath = `${sysRoot}\\System32\\REG.exe`;
if (existsSync(regPath)) {
const output = execFileSync(
regPath,
["QUERY", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
{ encoding: "utf8", timeout: 5000 }
);
const id = output
.split("REG_SZ")[1]
?.replace(/\r+|\n+|\s+/gi, "")
?.toLowerCase();
if (id && id.length > 8) return id;
}
} catch {
// Not Windows or REG.exe failed β€” continue
}
// Strategy 2: macOS β€” ioreg IOPlatformUUID
try {
if (process.platform !== "darwin") {
throw new Error("Not macOS");
}
const output = execSync("ioreg -rd1 -c IOPlatformExpertDevice", {
encoding: "utf8",
timeout: 5000,
});
if (output.includes("IOPlatformUUID")) {
const id = output
.split("IOPlatformUUID")[1]
?.split("\n")[0]
?.replace(/=|\s+|"/gi, "")
?.toLowerCase();
if (id && id.length > 8) return id;
}
} catch {
// Not macOS or ioreg not available β€” continue
}
// Strategy 3: Linux β€” read machine-id files directly (no `head` or pipe)
try {
for (const filePath of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
if (existsSync(filePath)) {
const content = readFileSync(filePath, "utf8").trim().toLowerCase();
if (content.length > 8) return content;
}
}
} catch {
// Files not readable β€” continue
}
// Strategy 4: Hostname fallback (works on all platforms)
try {
const hostname = execSync("hostname", { encoding: "utf8", timeout: 5000 });
const id = hostname.trim().toLowerCase();
if (id) return id;
} catch {
// hostname failed β€” continue
}
// Strategy 5: Node.js os.hostname() (no exec needed)
try {
const os = require("os");
return os.hostname().toLowerCase();
} catch {
// Final fallback
}
return "unknown-machine";
}
/**
* Get consistent machine ID using native registry/OS query with salt
* This ensures the same physical machine gets the same ID across runs
*
* @param {string} salt - Optional salt to use (defaults to environment variable)
* @returns {Promise<string>} Machine ID (16-character base32)
*/
export async function getConsistentMachineId(salt = null) {
const saltValue = salt || process.env.MACHINE_ID_SALT || "endpoint-proxy-salt";
try {
const rawMachineId = getMachineIdRaw();
// Create consistent ID using salt
const crypto = await import("crypto");
const hashedMachineId = crypto
.createHash("sha256")
.update(rawMachineId + saltValue)
.digest("hex");
// Return only first 16 characters for brevity
return hashedMachineId.substring(0, 16);
} catch (error) {
console.log("Error getting machine ID:", error);
// Fallback to random ID if node-machine-id fails
try {
const cryptoFallback = await import("crypto");
return cryptoFallback.randomUUID();
} catch {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0;
const v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
}
}
/**
* Get raw machine ID without hashing (for debugging purposes)
* @returns {Promise<string>} Raw machine ID
*/
export async function getRawMachineId() {
try {
return getMachineIdRaw();
} catch (error) {
console.log("Error getting raw machine ID:", error);
// Fallback to random ID if node-machine-id fails
try {
const cryptoFallback = await import("crypto");
return cryptoFallback.randomUUID();
} catch {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0;
const v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
}
}
/**
* 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";
}