The third kind of check
Detection techniques that examine a single browser fall into three families, and it is worth separating them because they have very different costs and very different failure modes.
| Family | What it compares | What it needs |
|---|---|---|
| Coherence | One surface against another surface | Two independent code paths to the same fact |
| Oracles | A declared value against a derived one | Knowledge of what each engine and OS produces |
| Spec invariants | A value against the standard that defines it | Nothing but the specification |
That last row is why invariants are attractive to implementers. Coherence checks can misfire when a legitimate privacy tool normalises one surface. Oracle checks need a maintained table of what real engines produce, which drifts as browsers ship. An invariant check is a closed question: the standard enumerates the legal values, so anything outside the set is non-conforming by definition, and the check needs no maintenance beyond tracking spec changes.
The trade-off is coverage. Invariants only constrain values the specification bothered to constrain, which is a minority of the fingerprinting surface. They are a precise instrument with a narrow reach, best used to catch careless edits rather than to characterise a browser.
The invariant families worth knowing
Most published invariants fall into four groups.
Enumerated value sets. The specification lists every legal value. navigator.deviceMemory is defined as an approximation reported from the set 0.25, 0.5, 1, 2, 4, and 8 - deliberately coarse to limit entropy, and capped at 8 regardless of installed RAM. A value of 6, 16, or 12 is outside the set. Similarly canPlayType() is specified to return only the empty string, maybe, or probably; any other string is non-conforming.
Quantisation rules. Some values must be rounded before exposure. navigator.connection.rtt is specified to be rounded to the nearest 25 milliseconds specifically so it cannot carry fine-grained timing information. A value of 137 ms did not come through the specified rounding. High-resolution timers carry a similar constraint, covered in timing fingerprinting.
Mutual exclusion and internal arithmetic. Some objects have fields that constrain each other. A BatteryManager reporting charging: true must report dischargingTime: Infinity, because a charging battery is not discharging. screen.availWidth can never exceed screen.width. navigator.appVersion is specified as the User-Agent string minus its leading token, so the two are arithmetically related rather than independent.
Derived serialisations. Some values are defined as a function of others. location.origin is serialised from protocol, host, and port by a specified algorithm, so it cannot disagree with its components. A browser where these drift apart is reporting a value that was assembled rather than computed.
Why invariants are the cheapest layer to get wrong
Invariant violations are almost always accidents rather than trade-offs. Nobody deliberately sets deviceMemory to 6 in order to gain something; it happens because a value was picked to look plausible without checking what the specification permits. The same applies to randomised hardware profiles that draw from a realistic-looking range rather than the enumerated set, and to values copied from a device summary written by a human rather than read from a browser.
That makes invariants an unusually good early warning. A configuration that violates one has not been validated against the platform it claims to run on, which usually means the other values were not validated either. Detection systems weight them accordingly: a single enumerated-set violation says something specific about how the environment was assembled, in a way a rare canvas hash never does.
The defence is simply to read the specification for any value being set, and to prefer values a real browser actually produced over values that merely look reasonable. This is the same conclusion as the rest of the coherence family: an identity harvested from one real machine satisfies constraints nobody remembered to check, because the machine satisfied them first. Managed web data infrastructure gets this for free by running real browsers rather than assembling profiles, so enumerated sets, quantisation, and derived serialisations all hold without anyone auditing them.