The fp_-prefixed fingerprint id. Falls back to a fresh,
non-cached crypto.randomUUID() for this call only if reading navigator
somehow throws.
getFingerprintDistinctId(); // => "fp_3f9c2a1b..." — stable for this device
export function getFingerprintDistinctId(): string {
if (cachedFingerprint) return cachedFingerprint;
try {
const nav = navigator as NavigatorWithFingerprintSignals;
const signals = {
fpVersion: FINGERPRINT_VERSION,
platform: nav.userAgentData?.platform ?? nav.platform ?? '',
mobile: nav.userAgentData?.mobile ?? false,
cores: nav.hardwareConcurrency ?? 0,
memory: nav.deviceMemory ?? 0,
lang: nav.language ?? '',
};
cachedFingerprint = `fp_${md5(JSON.stringify(signals))}`;
return cachedFingerprint;
} catch {
return crypto.randomUUID();
}
}
Computes the device-fingerprint
distinct_id: a hash of a handful of highly stablenavigatorsignals, readable identically in the popup, options page, and the service worker (noscreen/DOM dependency). Because it's a pure function of the environment rather than something stored, the same physical install produces the same id before and after a reinstall —chrome.storage.local(where the old random id lived) can be wiped with no effect on it.This is a coarse cohort identifier, not a strong unique-device id — deliberately low entropy. Signals were chosen to exclude anything volatile (the UA version string, which churns roughly every 4 weeks on auto-update;
Intltimezone ornavigator.languages, which drift without any user action) since either would fragment the id and defeat "stable across reinstall." That leaves common, stable traits — many unrelated installs sharing typical hardware (e.g. "8 cores, Win32, en-US") will hash to the same id, so PostHog will under-count unique installs, not over-count. Higher-entropy techniques (canvas/WebGL/audio fingerprinting) would fix that but are the specific pattern Chrome Web Store review and privacy tooling scrutinize, and were deliberately ruled out, not just downweighted.