Guides

Persistence engine

A visitor in Veriprint is not a single hash. It is a cluster of signal vectors that follows a device as its browser, hardware, and network change. This page explains how matching, weighting, and drift tracking work, and where the limits are.

A visitor is a cluster, not a hash

Naive fingerprinting hashes all signals into one string; a single changed value (a browser update shifting the canvas render, a new monitor changing the screen resolution) produces a different hash and a false new visitor. Veriprint instead stores each visitor as a weighted signal vector. On every identify call, the incoming vector is compared against known visitors using entropy-weighted similarity:

SimilarityDecision
≥ 0.82Link to the existing visitor at high confidence.
≥ 0.62Link to the existing visitor with a lowered confidence score: enough signals moved that the match is plausible but not certain.
< 0.62Mint a new visitorId. The response reports matchedVia: ["new"].

Weighting by distinctiveness

A signal's weight is how much it distinguishes one device from another, which is its entropy, not merely how stable it is. This distinction matters. Every MacBook reports platform MacIntel, the same core count, the same timezone within a city, and the same stock system fonts. Those signals are perfectly stable and almost useless for telling two machines apart. Weighting them highly makes two similar laptops look identical and merges them into one visitor, which is the worst error the engine can make.

So the highest weights go to signals that are both reasonably durable and high entropy: the GPU renderer string, the installed-font set, and the canvas, WebGL, and audio render hashes. Ubiquitous signals get low weight and are used to bucket candidates, not to decide identity.

TierWeightSignalsWhy
Distinguishing4canvas, WebGL, GPU renderer, fontsDevice-specific. The GPU renderer separates an M4 from an M5; the render hashes and font set separate two otherwise identical laptops. These decide identity.
Distinctive, noisier3audio, screen resolutionReal entropy, but a little noisier or coarser than the top tier.
Population-narrowing2CPU cores, languages, timezoneStable, but a whole product line or a whole city shares one value. Helpful context, weak on their own.
Bucketing only1platform, vendor, memory, colorDepth, touch, userAgentNear-ubiquitous or fast-changing. Useful for grouping candidates cheaply, almost no identity power. IP and ASN are a weak network hint on top.

A Chrome update that rewrites the canvas and WebGL hashes still leaves the GPU renderer, fonts, screen, and audio in agreement, so the same device re-links. Two different laptops that share every common signal but differ on the render hashes and GPU renderer stay separate.

The entropy gate

Reweighting is not enough on its own, because a device with many matching common signals can still score high. A fingerprint-only match (one with no surviving anchor) is therefore accepted only if the distinguishing signals also agree past a floor. If two devices line up on timezone, platform, core count, and language but their canvas, WebGL, audio, and GPU renderer disagree, the match is rejected and a new visitorId is minted.

Similar devices without anchors
Two identical-model laptops in a privacy browser that blocks canvas, WebGL, and audio may offer nothing that distinguishes them. Veriprint declines to guess: it gives them separate IDs and relies on anchors to tell them apart. A false split is safer than a false merge.

The chip hard rule

One signal is treated as absolute, and only in the negative direction. The gpuChip value is the CPU/GPU model parsed from the GPU renderer (for example apple-m4 versus apple-m5). A matching chip adds no weight, because a whole office can share one M1 Pro, so a match proves nothing. But you cannot swap the chip in a laptop, so two observations that report two different specific chips cannot be the same machine. When that happens the candidate is dropped before scoring, and the conflict even overrides a stored anchor, since a shared id across two different chips is a leftover from a bad merge, not a real device. This is what separates two Chrome-on-Mac laptops whose canvas and audio render identically. Generic values like apple-silicon (Safari masks the chip) are never specific enough to split on.

Low-trust platforms: iOS and Safari

Some platforms make every device of a model look the same on purpose. iOS and iPadOS route every browser through WebKit, normalize canvas, WebGL, and audio, hide CPU and memory, expose no Client Hints, and mask the GPU. An iPhone 13 and an iPhone 14 produce near-identical fingerprints, down to the same screen resolution. Desktop Safari and hardened privacy browsers do much the same on the render side.

On these low-trust platforms a fingerprint match is not evidence of the same device, so Veriprint does not fingerprint-merge them at all. A request with no surviving anchor mints a fresh visitorId and leans entirely on the durable anchors for recognition. Two different iPhones therefore get two different ids, because each phone has its own storage.

The honest iOS limit
This costs fingerprint-based recall on iOS: an iPhone that clears cookies, localStorage, and the cache at once looks brand new, because nothing in an iOS fingerprint reliably identifies a single device. Every web vendor hits this wall. High-accuracy iOS identification requires a native app and Apple's IDFV, which a web SDK cannot access. We would rather return a new id than merge two people.

Anchors short-circuit matching

The SDK writes the returned visitorId into three independent stores (a cookie, localStorage, and the Cache API) and sends whatever survives back as anchors on the next call. Any one valid anchor resolves the visitor directly, and the SDK rewrites all three stores, so clearing cookies alone does not break the chain:

anchor-cycle
visit 1  → all 3 anchors written (cookie, localStorage, Cache API)
user clears cookies
visit 2  → localStorage + cache anchors still match → same visitorId
         → cookie is re-written        (matchedVia: ["localStorage", "cache"])
user clears ALL site data
visit 3  → no anchors → fingerprint similarity match at 0.87 → same visitorId
         → all 3 anchors re-written    (matchedVia: ["fingerprint"])

Drift tracking with EMA decay

Devices drift: fonts get installed, browsers update, displays change. On each linked identify, the stored vector moves toward the new observation with an exponential moving average at alpha = 0.25, so the stored value becomes 75% history and 25% new observation. Identity follows the device through gradual change instead of snapping to a stale snapshot, while a one-off anomaly (a spoofed signal, a temporary VM) cannot drag the cluster far.

When a returning device is re-linked (via anchor or similarity), its original firstSeenAt is preserved and visitCount keeps accumulating, so account-age and velocity checks stay meaningful across storage wipes.

Persistence curve and limitations

Honest expectations
Fingerprint-based identification is probabilistic. Veriprint reports a confidence score on every match precisely because no fingerprint is a permanent, guaranteed identifier. Do not build flows that assume it is.
  • Weeks: strong. Anchors usually survive, and signal drift is minimal; most returns match at high confidence.
  • A few months: good. EMA tracking absorbs browser updates and normal drift; matches remain reliable, with occasional lowered-confidence links.
  • Beyond that: graceful degradation. Accumulated drift lowers similarity; some visitors resolve as new.
  • Multi-year: realistic only for devices that keep at least one anchor and do not change hardware. Pure fingerprint recognition across years of drift is not something you should plan around.

Hard resets (a new device, a fresh OS install, or aggressive anti-fingerprinting browsers that randomize signals) produce a new visitor. That is by design: guessing would create false merges, which are worse for fraud decisions than false splits. Use confidence and confidenceLevel to decide how much weight each match should carry in your own logic.