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.
| file | what it is |
|---|---|
fp-collect.js | ES module, exports collect, buildPayload, frame, send |
fp-collect.console.js | the same code as one statement for a DevTools paste, assigns window.fpCollect |
index.html | runs 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>" });
collect({ region, urlHashing })runs every stage source and returnsnamed(one entry per signal, keyed by readable name) andcomponents(the raw{s, v}map the envelope builder takes).buildPayload({ apiKey })runscollectand returns the wire object: 143sNkeys plus the envelope keys documented inreference/envelope.md.frame(payload)encodes with the agent's own JSON-to-bytes codec, deflate-raw compresses over 1024 bytes, and seals with tag3,14. Returns the bytes as sent.send({ apiKey, endpoint })posts that frame and returns the response body.
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.