IPHey's "inconsistent browser fingerprint (whale)" is, as far as we can tell, its matcher's nearest guess for a Chromium build it cannot place. Naver Whale is a Chromium browser, so an unknown Chromium lands nearest to it. We followed that label through the Chrome version, through branding and through the whole JavaScript surface. It ended at the Widevine DRM module sending a device certificate in the clear, which any build Google has not signed does.

Every desktop profile we made got the signal. It cost us a long time, and most of it went on theories that were true and beside the point.

First theory: the version is too new

The BROWSER card had been red for a long time, and the excuse in our detector runner said Google's own build of the same Chrome major scored the same. We re-measured that properly, with the stock browser relaying through the profile's own proxy, so both browsers left from the same US residential exit in the same minute.

card        Tesseract Chrome 153        Chrome for Testing 153
headline    Unreliable                  Unreliable
BROWSER     inconsistent (whale)        inconsistent (whale)
LOCATION    green, United States        red, masked or inconsistent
MX SCORE    90                          80

The (whale) string was byte-identical, from Google's own unmodified binary. And the Chrome 152 installed on the same Mac scored Trustworthy 100. That read like a database: IPHey had not catalogued Chromium 153 yet, so it reached for the nearest thing it had. We pinned the excuse to the version so it would expire by itself when the engine moved on.

That control did prove something real. Chrome for Testing carries none of our patches, so the label was not caused by any of them. What it did not prove was that the version was the cause.

Every profile is drawn from one real computer, not from separate listsIn Mosaic: Machines that exist. GPU, screen, CPU and fonts from one real device. Free holds 3 profiles. Pro is $49 a month for any number.

How it works: Machines that exist

Second theory: branding

Chrome for Testing is unbranded, like every build that is not Google Chrome itself. So the next question was what a branded Chrome has that an unbranded Chromium does not. We found two things, and both were real.

The first was the speech voice list. speechSynthesis.getVoices() returns 199 voices in Google Chrome on our Mac and 180 in any unbranded build, because nineteen Google network voices come from a component that ships only in branded Chrome. A page reads that in one call. Patch 0145 restores them, and the story is in 199 voices.

The second was field trials. An unbranded build compiles in a testing config that gives it twenty API-surface entries Chrome does not have and takes away ones it does. --disable-field-trial-config moves all twenty, written up in twenty APIs Chrome doesn't have.

Fixing both made our engine closer to Chrome. Neither moved IPHey.

Third theory: something in JavaScript

So we compared everything a page script can see, and then the order it sees it in. Sorted property lists cannot show enumeration order, and enumeration order is readable. Unsorted, with the field-trial switch on, our engine was byte-identical to branded Chrome across window, Navigator, Screen, Document, Element, Performance, the canvas and WebGL contexts, MediaDevices, window.chrome and more.

Below JavaScript it matched too: all fourteen request headers in order and case, zero differing fields in the TLS ClientHello, zero in the HTTP/2 settings. About fifty surfaces in all. And IPHey still scored 90 where branded Chrome scored 100.

That ruled a lot out. The unpatched Chrome for Testing had already ruled out our patches, and now the surfaces they touch were ruled out as well, so a patch bisect would have hunted for something that was not there.

What IPHey's inconsistent browser fingerprint (whale) was: Widevine

What found it was reading what IPHey collects rather than what we assumed it collects. Its detector is a wasm-bindgen module, and wasm-bindgen names every import after the thing it reads. We wrapped the module's import table and logged every call, which gave us the whole collection in plain text. One field differed: a media key session message of 1704 bytes from our engine, against 2 bytes from Chrome.

Chrome's two bytes ask for a service certificate so the Widevine module can encrypt its client ID before sending anything. That privacy mode is only for a browser that passes Verified Media Path, which needs a Google signature over the browser binary. Our engine cannot carry one, so its first message was a licence request with the per-install device certificate inside it, readable.

ours, unmodified          Unreliable    MX  90
ours, EME made to reject  Trustworthy   MX 100
branded Chrome 153        Trustworthy   MX 100

That certificate is the same for every profile on the machine and survives every value a fingerprint generator redraws. The full measurement, and the three fixes we tried that changed nothing, are in one key for every profile.

What we changed, and what we kept

Widevine is off in Mosaic unless a profile asks for it, under Privacy › Leak protection › Allow Widevine DRM playback. A page asking for it is told it is not supported, which is readable too, but it is not a key that joins your profiles together.

We kept the voice and field-trial fixes, because a Chromium that says it is Chrome should not fail a one-line check. And we kept the habit the score taught us, written up in IPHey's score moved with the address: a control on the same exit in the same minute, and several rounds, before believing any verdict.