import crypto from "node:crypto"; // Shared-secret auth for the /api/worker/* endpoints (the LAN remote-worker // protocol). The ACCEPTING instance validates the incoming bearer token against // its own WORKER_TOKEN env var — never against settings — so a leaked settings // file can't reveal the acceptor's secret. The endpoints are DISABLED unless // WORKER_TOKEN is set, so an instance is never an open transcription server by // accident; you opt in by setting the env var. export function getWorkerToken(): string { return process.env.WORKER_TOKEN ?? ""; } export function workerEndpointEnabled(): boolean { return getWorkerToken().length > 0; } export type WorkerAuth = { ok: true } | { ok: false; status: number; error: string }; // Validate an Authorization header against WORKER_TOKEN with a constant-time // compare. 503 when the endpoint is disabled (no token configured), 401 on a // missing/malformed/mismatched token. export function authorizeWorkerRequest(authHeader: string | null): WorkerAuth { const token = getWorkerToken(); if (!token) { return { ok: false, status: 503, error: "worker endpoint disabled (set WORKER_TOKEN to enable)", }; } const match = /^Bearer\s+(.+)$/i.exec(authHeader ?? ""); if (!match) return { ok: false, status: 401, error: "missing bearer token" }; const provided = Buffer.from(match[1]); const expected = Buffer.from(token); if ( provided.length !== expected.length || !crypto.timingSafeEqual(provided, expected) ) { return { ok: false, status: 401, error: "invalid worker token" }; } return { ok: true }; }