Back to the BotGuard study
Study document · October 2026

Background check, Nov. 2025 to Oct. 2026

444 captures: August’s new tests, WebGPU removal, local storage module.

Powered by RESONEO

QyE background check: November 2025 to October 17, 2026

Analysis of the program Google runs in the background on its results pages (sg_ck=QyE channel), based on collected responses. Status as of October 19, 2026.

In brief

  1. Four new test families appear between August 20 at 12:06 UTC and August 22 at 20:58 UTC. Our corpus contains no capture between these two points in time. All four arrive together: settings declared by the browser, text rendering and hyphenation, pointer precision, WebGPU.
  2. The decoded program grows by about 13% (+2,648 bytes) between August 12 and 28, while the JavaScript engine that runs it shrinks slightly.
  3. The WebGPU test is removed on October 5, between 06:53 and 14:07 UTC. The other three families remain. Our live session of October 17 confirms this.
  4. A variant with a local storage module has coexisted with the normal program since July. 134 captures from July 8 to October 17; it is served at the same time as the normal version, for the same request URL.
  5. Stored data is only accepted by the program copy that wrote it.

Corpus and method

ItemValue
QyE entries identified (responses over 20,000 bytes)4,780, from November 13, 2025 to October 17, 2026
Selected captures444: all large responses, their nearest normal neighbour, all of September, three per week since November 2025, date bisections
Bodies retrieved and verified (hash and capture date)444 of 444
Bodies run offline441; 3 responses related to Google Flights, in a different format, are excluded
Runs with empty storage441, without errors
Detailed traces69

The original engine is run with its program in an isolated browser. The https://www.google.com/search page is served locally; no request is sent to Google and no token is submitted.

The August increase

MeasureAugust 12August 28Difference
Encoded program, characters26,27529,807+3,532, i.e. +13.44%
Decoded program, bytes19,70322,351+2,648, i.e. +13.44%
JavaScript engine, bytes63,73463,231−503
Full response, bytes120,495123,421+2,926, i.e. +2.43%

The auxiliary configuration is identical. The increase comes neither from the encoding nor from the packaging.

Size of the normal program, month by month

Decoded program, in bytes, excluding the variant with storage.

PeriodCapturesMedianMinimumMaximum
November 2025318,87218,83819,523
December 2025818,91518,57520,158
January 20261319,13618,78820,150
February1119,09918,83020,138
March1319,28719,14820,110
April1519,40319,11420,102
May1119,86319,75923,213
June920,14719,99720,341
July8820,09719,86121,196
August 1 to 205319,73418,63819,950
August 22 to October 5, 06:539822,14821,25322,811
October 5, 14:07 to October 172721,82921,43922,663

About +1 KB over nine months, then a jump of about +2.4 KB on August 22, then −319 bytes after WebGPU was removed. The spread between captures from the same period reaches 1.5 KB: size alone is not enough to attribute the October decrease.

The new tests

TestWhat the program does
Declared settingsReads navigator.platform, navigator.doNotTrack and window.doNotTrack
Text renderingCreates hidden blocks in a monospaced font (16 px, 20 px line height), compares hyphens: auto and none, reads offsetHeight
PointerCalls matchMedia with any-pointer: fine and any-pointer: coarse
Graphics card (until October 5)Calls navigator.gpu.requestAdapter with powerPreference: low-power, then reads info.vendor and info.architecture
Period (UTC)TracesSettings, rendering, pointerWebGPU
November 2025 to August 20, 12:0610absentabsent
August 22, 20:58 to October 5, 06:5343presentpresent
October 5, 14:07 to October 1716presentabsent

No exceptions in the 69 traces. The removal of WebGPU coincides with the switch to a new version of the JavaScript engine, between 06:53 and 14:07 UTC on October 5. Engine versions change several times a week: the coincidence dates the change, without proving that the engine is its cause.

Constants related to the new tests were located in the program (about 618 bytes of literal reconstruction). The exact split of the additional 2,648 bytes between code, data and padding is not established.

The variant with local storage

It can be identified in two consistent ways: its size, and the fact that it leaves state in localStorage after running. Out of 441 runs, the 134 large programs all leave state, the other 307 none.

MonthIndex entriesVariantsShare
November 2025 to June 20261,64100%
July1,106116 (July 8 to 16)10.5%
August721 (August 18)1.4%
September861 (September 28)1.2%
October 1 to 171,875160.9%

It is the normal program of the time, extended with a module of 4.7 to 7.4 KB (median 6 KB). It follows the normal program's changes: without the new tests in July, with WebGPU on September 28, without WebGPU in October. In October, the nearest normal capture with the same engine is a median of 1.2 minutes away; on October 15, a variant and a normal program are captured 16 seconds apart. Nothing in the request URL distinguishes them: the assignment is made server-side. An A/B test, sampling or targeting all remain compatible with this data.

The module also schedules a 2,000 ms interval.

Stored data is tied to its program

Reinjected stateProgram reading itResult
Its own statethe same programaccepted (6 July programs out of 6, and the August 18 variant)
State from another July 8 program, same engineJuly programrejected (4 cases out of 4)
State from another July 8 program, different engineJuly programrejected (2 cases out of 2)
State from a July programAugust 18 variantrejected
State from the August 18 variantJuly programrejected

"Accepted": same result as with the program's own state. "Rejected": same result as with empty storage. These cross-program tests do not establish how state is validated or used server-side.

Storage structure and content

Decoding six programs (July 8 and 11, August 18, September 28, October 6 and 15) shows a common structure. The original engine and associated program are replayed offline, without sending tokens to Google. The decoded array is captured during storage readback; lengths and the final count are verified.

ElementSize and observation
Outer envelope8 bytes
Decoded-content marker3 bytes: 01 be ef
Program-associated block12 bytes, unknown role, different across the six samples
Record4-byte big-endian timestamp, 1-byte field observed as 1, 2-byte big-endian length, variable payload
Final count1 byte

Without interaction, the payload is 114 bytes: 121 bytes per record and a binary total of 24 + 121 × n. One record therefore represents 145 bytes; twenty, 2,444 bytes, before base64 encoding. Interactions change the length: 146 payload bytes for a movement or keystroke, 151 for an isolated click in the tested scenarios.

The timestamp matches program capture time exactly to the second in five samples and differs by one second in the sixth. It does not measure the simulated visit date in these replays. The role of the field equal to 1 remains unknown.

History of the last twenty runs

On the August 18 program, 21 fresh contexts successively receive the state written by the previous context. Each run contains a distinct click, from (101,201) to (121,221). The final state contains exactly the twenty records from (102,202) to (121,221), in order: the first is removed. This FIFO policy concerns program runs, not twenty clicks within one page. Format similarity alone does not establish it for the other five programs.

The studied coordinates represent the last click: a click at (123,234), followed by movement to (500,400), leaves (123,234). Movement alone leaves the (65535,65535) sentinel.

A block marked c5 26 01 20 contains a flag, followed by 32 one-byte counters when active in the tested scenarios. One keystroke sets counters at indices 27, 28 and 29 to 1; three keystrokes raise them to 3. The a and b keys produce identical payloads. These are event counters, without distinguishing typed characters in this block and these scenarios; this does not establish whether collection occurs elsewhere.

A synthetic JavaScript click increments a counter without populating coordinates like the CDP click. Since sequences and targets may differ, this contrast does not establish an effect of isTrusted alone. Exact mappings between all counters and DOM events, and their saturation rules, remain undetermined.

Keys and encoding: 134 states

PeriodStatesObserved keysEncoding
July116Common base with suffix 0 to 9Padded base64
August1Base with suffix 4Padded base64
September1Unsuffixed baseUnpadded URL-safe base64
October16Unsuffixed baseUnpadded URL-safe base64

The change is bounded by our August 18 and September 28 captures, without an exact deployment date. The purpose and selection rule of suffixes are not established; cumulative volume across multiple keys is unmeasured. A persistent history establishes neither a stable personal identifier, cross-site tracking, nor use in the server score.

Limits

  • Dates are capture dates, bracketed by our neighbouring captures. The context in which each request was issued is unknown.
  • An absent test means that its strings were not reconstructed in this offline scenario. For WebGPU, the October absence is cross-checked live.
  • The variant counts for the index rely on size: a single false positive among the 441 verified bodies.
  • No reliable conclusion on execution cost or detection effectiveness.
Back to the BotGuard study