import { createHmac } from "node:crypto"; /** * Derives a daily-rotating, non-reversible pseudonymous visitor identifier * for session grouping - the same approach used by privacy-friendly * analytics tools (Plausible/Fathom-style): hash(ip + userAgent + host) * salted with a value that changes every calendar day. Raw IP/UA are never * persisted anywhere, only this hash - and since the salt rotates daily, * the same visitor hashes differently tomorrow, so there is no cross-day * linkage and no long-term individual profile. * * ANALYTICS_SALT_SECRET is a dedicated secret (not PAYLOAD_SECRET) - this * codebase keeps one secret per purpose on purpose (see * MEETING_SESSION_SECRET's comment in .env.example). */ function secret(): string { const value = process.env.ANALYTICS_SALT_SECRET; if (!value) throw new Error("ANALYTICS_SALT_SECRET is not set"); return value; } function dateKey(date: Date): string { return date.toISOString().slice(0, 10); } function dailySalt(date: Date): string { return createHmac("sha256", secret()).update(dateKey(date)).digest("hex"); } export function visitorHash(args: { ip: string; userAgent: string; host: string; date?: Date }): string { const salt = dailySalt(args.date ?? new Date()); return createHmac("sha256", salt).update(`${args.ip}|${args.userAgent}|${args.host}`).digest("hex"); }