Skip to content
On this page

A spec invariant is a constraint the web platform specification itself places on a value, which makes any violation provable from the standard alone. Where coherence checks compare two surfaces against each other and oracles read values derived from the engine build, an invariant check needs neither a second surface nor a reference corpus. The specification says navigator.deviceMemory must be one of a short list of approved numbers, so a browser reporting 6 has produced a value no conforming implementation can produce. No population data is required to know it is wrong.

Quick facts

DefinitionA value constraint written into the web standard itself
What a violation provesThe value did not come from a conforming implementation
Reference data neededNone - the spec is the reference
Classic exampledeviceMemory must be one of 0.25, 0.5, 1, 2, 4, 8
False-positive rateVery low, because the legal set is defined rather than observed

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.

FamilyWhat it comparesWhat it needs
CoherenceOne surface against another surfaceTwo independent code paths to the same fact
OraclesA declared value against a derived oneKnowledge of what each engine and OS produces
Spec invariantsA value against the standard that defines itNothing 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.

Code example

javascript
// Spec invariants: violations provable from the standard, no corpus needed.
const violations = [];

// 1. Enumerated set - the spec defines exactly which values may be reported
const LEGAL_MEMORY = [0.25, 0.5, 1, 2, 4, 8];
const dm = navigator.deviceMemory;
if (dm !== undefined && !LEGAL_MEMORY.includes(dm)) violations.push('deviceMemory:' + dm);

// 2. Enumerated return - canPlayType may only return '', 'maybe', or 'probably'
const answer = document.createElement('video').canPlayType('video/mp4');
if (!['', 'maybe', 'probably'].includes(answer)) violations.push('canPlayType:' + answer);

// 3. Quantisation - rtt is specified to round to the nearest 25ms
const rtt = navigator.connection?.rtt;
if (rtt !== undefined && rtt % 25 !== 0) violations.push('rtt:' + rtt);

// 4. Internal arithmetic - available area cannot exceed the screen
if (screen.availWidth > screen.width || screen.availHeight > screen.height) {
  violations.push('avail-exceeds-screen');
}

// 5. Derived serialisation - origin is computed from its own components
const rebuilt = location.protocol + '//' + location.host;
if (location.origin !== rebuilt) violations.push('origin-mismatch');

// 6. Mutual exclusion - a charging battery is not also discharging
navigator.getBattery?.().then(b => {
  if (b.charging && b.dischargingTime !== Infinity) violations.push('battery-exclusion');
});

console.log({ conforming: violations.length === 0, violations });

Next in Coherence and lie detection · 7 of 8

Time is its own coherence problem, with several clocks that must agree.

What Is Clock Coherence Detection?

Related terms

Concept map

Concept map

How What Are Spec Invariants in Fingerprinting connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

Why does the spec limit deviceMemory to a short list of values?

To cap the entropy the API can contribute. Reporting exact installed RAM would let sites distinguish machines far more finely than the feature needs, so the specification defines a coarse approximation drawn from a fixed set and caps it at 8 regardless of how much memory is present. The side effect is that any value outside the set identifies itself as non-conforming.

How are spec invariants different from coherence checks?

A coherence check needs two surfaces and concludes that they disagree, without saying which one is wrong. An invariant check needs only one value and the specification, and concludes that the value itself is illegal. That makes invariants cheaper to run and easier to act on, but they cover far less ground because most fingerprinting surfaces are not constrained by the standard at all.

Can a real browser ever violate a spec invariant?

It happens, but rarely, and usually as a genuine implementation bug that gets fixed. Privacy tools that normalise values are the more common source, since some deliberately return constants that may not respect a quantisation rule. This is why invariant violations are best read as a signal about how an environment was assembled rather than as proof of automation on their own.

Last updated: 2026-07-30 · Facts last verified: 2026-07-30