What it measures
The signal resolves the device to a single coarse OS-family token — one of a small fixed set (Windows, macOS, Linux, Android, iOS, ChromeOS, or unknown). That one token is the entire hash input. The raw navigator.platform string (e.g., 'MacIntel', 'Win32', 'Linux x86_64'), navigator.maxTouchPoints, and additional navigator properties (hardwareConcurrency, deviceMemory, userAgentData.platform, userAgentData.architecture, and languages) are all collected and stored in value.engine for analytics and debug use, but none of them enter the hash.
Collapsing to an OS family — rather than hashing the raw platform string — keeps the hardware hash stable for one device: raw values vary cosmetically for the same OS (different Linux or macOS build strings, ARM vs x86 markers) and are overridden by some privacy modes, whereas the OS family does not.
Collection is synchronous and sub-millisecond.
How it's collected
If globalThis.navigator is absent, the result is absent. Otherwise the collector classifies the device into one OS-family token using the most reliable platform indicators the browser exposes, preferring structured low-entropy hints where available and falling back to the user-agent string and legacy navigator fields. The resulting single token is hashed with xxHash64. The exact ordering of indicators is an internal detail and may change between releases without changing this contract.
The collector also reads navigator.userAgentData (Chrome-only) and navigator.languages in separate try/catch blocks, silently catching failures. Those values — along with the raw platform string, maxTouchPoints, hardwareConcurrency, and deviceMemory — are placed in value.engine for debug only and never influence the hash.
Confidence rules
| Confidence | Trigger |
|---|---|
| normal | globalThis.navigator is present |
| absent | globalThis.navigator is undefined, or any unhandled exception |
Why hardware-bound
The OS family of a device does not change when the user switches browser engines. All major engines (Blink, Gecko, and WebKit) agree on whether the machine is running Windows, macOS, Linux, Android, iOS, or ChromeOS, so the coarse token is identical across Chrome, Safari, Firefox, and Brave on the same device. It is also lower-entropy and therefore far more stable than the raw platform string, which carries cosmetic OS-build and CPU-marker differences the hash would otherwise churn on.
hardwareConcurrency and deviceMemory were considered for the hash but excluded because Brave Standard mode farbles both per eTLD+1 session, Firefox resistFingerprinting returns fixed dummy values, and Tor Browser returns hard-coded values. Including them would break the cross-browser hardware-identity guarantee.
Things worth knowing
- iPadOS 13+ reports a desktop-Mac-like platform; the classifier accounts for this so iPads still resolve to the iOS family rather than macOS.
- The raw maxTouchPoints, platform string, hardwareConcurrency, and deviceMemory remain available in value.engine for callers that want the fine-grained values; they are debug-only and never affect the hash.
- When navigator.deviceMemory is not defined (Firefox, Safari), the value.engine field stores an unsupported result placeholder instead of a numeric value.
- userAgentData is Chrome-only; its raw fields are stored in value.engine for callers that run only in Chromium, but they are not hashed directly.
Last reviewed 2026-06-07

