Fingerprint Pro Internals
The standalone collectors

fp-collect

The agent's 140 stage collectors, its envelope builder and its wire codec, cut out of the deobfuscated bundle and emitted as one plain ES module. No CRC32 property lookups, no encrypted string tables,

The agent's 140 stage collectors, its envelope builder and its wire codec, cut out of the deobfuscated bundle and emitted as one plain ES module. No CRC32 property lookups, no encrypted string tables, no operator wrappers. Every function is the shipped code with those layers folded away, so it measures exactly what the agent measures.

Generated by npm run collector from agent/agent.clean.js. Do not edit by hand: regenerate after a version bump and the diff shows what the new build changed.

filewhat it is
fp-collect.jsES module, exports collect, buildPayload, frame, send
fp-collect.console.jsthe same code as one statement for a DevTools paste, assigns window.fpCollect
index.htmlruns collect() and prints one row per signal, sends nothing

Run it#

Serve the repository and open the page:

python3 -m http.server 8123
open http://127.0.0.1:8123/collector/index.html

The page prints the wire id, the name, the status code, the collection time and the value for each of the 140 collectors, and leaves the whole result on window.fp.

For any other page, paste fp-collect.console.js into the DevTools console:

const { named } = await fpCollect.collect();
console.table(Object.entries(named).map(([signal, entry]) => ({ id: entry.id, signal, s: entry.value?.s })));

API#

import { collect, buildPayload, frame, send } from "./fp-collect.js";

const { named, components } = await collect();
const payload = await buildPayload({ apiKey: "<tenant public key>" });
const sealed = await frame(payload);
const result = await send({ apiKey: "<tenant public key>", endpoint: "https://<tenant ingress>" });

A frame built here reopens with node tools/codec.mjs --open <file>, which is the check that the extraction did not drift from the bundle.

What it is not#

collect() runs the 140 collectors registered in the cm module's three stages. The other three wire ids (s69, s55, s48) come from the module's toRequest and appear only in buildPayload(), which is why the payload carries 143 signal ids and the table shows 140 rows.

Two collectors need the tenant's TURN credentials to report anything (s94, s219, the WebRTC pair). Without a real tenant session they return a connection status, which is what the status column shows.

Against the Chrome capture in captures/, 132 of the 140 collectors report the same status code here. The eight that differ are environmental: the WebRTC pair, the storage estimate, and five signals whose result depends on page origin and browser state rather than on the code.