platform unless
it says umami. Replace <tenant id>, <site id> and the dates.
Web traffic
-
Outcome counters per day. Every event the gateways saw is counted by outcome
(
accepted,quarantined,bot,dlq,rejected_<reason>):Signs of forging: a rise inquarantined(many visitor tokens from one address prefix within an hour), inreplay(a captured proof sent again), inbad_prooforbad_headers(naive server-side posts), orrate_limited. A rise inacceptedwith none of these is either real traffic or a capable forger (real browsers, residential proxies). -
Hourly shape. Real traffic follows the time zones of its audience. Flat lines, exact
round counts, or traffic that starts and stops on the minute are suspicious:
-
Primary and quarantine bands side by side (the operator aggregates read from Umami):
A sudden quarantine band next to a flat primary band is the same-IP check working. Pageviews per visit near 1 with bounces near visits and
totaltimenear 0 is a common pattern of scripted pageviews. -
Concentration. Forged traffic tends to come from few browsers, operating systems, devices,
countries or referrers:
-
Raw events, when the aggregates are not enough. Database
umami, both of the site’s websites (the primary has the site id; the quarantine id iscp.sites.quarantine_website_id):Many sessions with one identical client shape and one path per session within an hour is scripted traffic. No address is stored anywhere, so the investigation cannot and does not identify who sent it.
API traffic
API numbers come from the tenant’s own OTLP export with its own token; a tenant can inflate them by sending made-up metrics. There is no proof-of-browser equivalent.-
Volume per service and day:
-
Warnings the writer recorded (cardinality overflow, lossy histograms, span-derived data):
-
Plausibility. Compare requests per day with the status mix and the latency histogram:
made-up metrics often have one status code, one bucket, or perfectly even minutes. Minute rows
are kept for
API_MINUTE_RETENTION_DAYS(30 by default) inapi.metrics_minutewith the same columns. -
Tokens.
select id, name, status, created_at, last_seen_at from cp.otlp_tokens where tenant_id = '<tenant id>';Stored metric rows do not carry the token, so a token cannot be tied to particular rows.
Acting on a finding
- Ask the project. Most jumps are launches, campaigns or a crawler they run themselves.
- Suspend while the question is open (tenant-removal.md): ingestion stops, data stays.
- Raise the cost for everyone when one site is targeted by naive forging: raise
PROOF_BITS(12 today) or lowerIPCLUSTER_THRESHOLD(20 today) inedge_gateway_env(infra/ansible/roles/edge/defaults/main.yml, or an override of the whole map ininfra/ansible/inventories/<env>/group_vars/all.yml), record the reason in ../reference/gateway-limits.md, and roll out withmake <env>-deploy ROLLING=1 .... A higherPROOF_BITScosts every visitor’s browser CPU; measure first (pnpm --filter tracker bench:proof). A lower threshold quarantines more shared addresses (carrier NAT, offices). - Record the finding with dates, the queries’ output and the decision, where the operators keep their review notes, and mention it when the project’s numbers are compared.
Exercised
Not yet exercised as an investigation. No suspected case exists: there is no production traffic. Phase 19 ran every query above against a scratch PostgreSQL 18.6 with all platform migrations applied (theplatform queries) and against the development stack’s Umami 3.5.0
database (the umami query), with placeholders replaced by a random uuid and a date range,
to prove they are valid against the real schemas. Results are in the table below.