File DA-026 View as Markdown
Security
This page explains how Double Agent keeps your numbers clean when someone sends fake traffic, and what protects your account and keys. Read it if you are reviewing Double Agent for your security team, or if a session in the portal is marked Unverified traffic and you want to know why.
What protects what
| Threat | What stops it |
|---|---|
| Floods of fake session reports | Rate limits per IP, per key and per hostname; proof of work during a spike; quarantine |
| Forged or replayed reports | The server sets each session's weight (always 1) and checks it for plausibility. A session id is stored once however often it is replayed. Optional attested tickets from your edge |
| A visitor faking their own verdict | The server scores every session again from evidence the browser can't forge, and never trusts the browser's verdict. Verify tokens on your server, not in the page |
| Account farming | POST /v1/accounts needs proof of work or a Turnstile token |
| Sign-in link abuse | Links work once, last 15 minutes and are hashed at rest. The answer is the same whether or not the email has an account |
| Stolen secret key | Rotate it in Sites › your site › API keys (the old key keeps working for 24 hours) or revoke it at once. Keys are hashed at rest |
| Reading another customer's data | Every data query is scoped to sites your account owns, and data routes need a verified domain. The agent directory is a separate public listing |
Trust and quarantine
Every session report gets a trust score from 0 to 1. It starts at 1 and loses points for each problem: the page's origin doesn't match a verified domain, the browser's headers contradict what the page reported, the request comes from a datacenter, proof of work was missing or wrong, a burst from one network, and so on. A valid attested ticket adds points back.
Below 0.4, or with a forged ticket, wrong proof of work or tampered evidence, the session is stored as unverified traffic. You can still see it in the portal, on the Unverified traffic tab of Sessions, but it is kept out of your stats, out of the site's learned priors, and out of relabel and webhooks. The integrity field on a server verdict shows the score and the reasons. See Verdict.
Proof of work
Proof of work is off by default. When a site's reports suddenly spike, the API asks every browser on that site to solve a small puzzle for the next 10 minutes (14 bits of SHA-256 over sid|host|nonce|solution). It sends the puzzle as a response header:
DA-PoW: v=1;d=14;nonce=<nonce>
The script solves it in a background worker, so real visitors don't notice. A report without the answer is not dropped. It loses trust, and without a valid ticket that puts it in quarantine.
Attested tickets
An edge you run in front of your site (a Cloudflare Worker or a Next.js proxy) can vouch that it served each page. It adds a ticket to every HTML response, the script sends the ticket back once, and a valid ticket raises the session's trust.
Server-Timing: da;desc="t=<ticket>"
ticket = b64url("<nonce>.<host>.<exp>") + "." + b64url(HMAC-SHA256(ticketKey, <the first part>))
ticketKey = HMAC-SHA256(key = your secret key, message = "da-ticket")
<nonce>isn_plus 22 random base64url characters, used once.<host>is the page host as the browser sees it, port included.<exp>is a Unix time at most 600 seconds ahead. 540 leaves room for clock drift.- Only the derived
ticketKeyis used. Your secret key never reaches the browser.
A ticket that is expired or was already used by another session counts as absent: no bonus, no penalty. Only a forged one (bad signature, wrong host, expiry too far ahead) counts against the session. For edge reporting and the edge hint, see Intent › Edge adapter.
Rate limits
Every limit returns 429 with Retry-After. Limits keyed by IP use a hash of the IP salted daily, never the raw address.
| Route | Limit |
|---|---|
Session reports (/v1/collect) | 60 per IP a minute, 10,000 per key a minute, 1,000 per keyless hostname a minute |
Live presence (/v1/ping) | 120 per IP a minute |
Visitor config (/v1/visitor-config) | 120 per IP a minute |
Server-checked verdicts (/v1/check) | 30 per IP a minute |
Server evaluation (/v1/evaluate) | 1,000 per key a minute |
Edge hits (/v1/hits) | 100 batches per key a minute, 1,000 per account a minute |
| Data routes (portal and API reads) | 120 per session a minute |
| Install check | 30 per IP a minute |
| Domain verification | 10 per site a minute |
Sign-in (/v1/auth/login) | 3 per email per 15 minutes, 10 per IP an hour |
Sign-in link (/v1/auth/verify) | 20 per IP a minute |
| CLI device sign-in | 10 per IP a minute; 10 approvals per user per 15 minutes |
New accounts (/v1/accounts) | 5 per IP an hour, 3 per email a day |
Limits for the agent directory are on REST API, and for AI agents using Double Agent on Agent access.
Raw data
Raw IPs, full user agents, request headers and device details are kept apart from ordinary session views, for 30 days. Only owners and admins can read them, through GET /v1/sessions/:sid/raw (or with your secret key), and every read is audited. Raw provider data can include IP-based coordinates more precise than the rounded ones in the ordinary session view. Retention is set in the privacy policy. See also Privacy and consent.
Next: Verified agents for how agent identity is proven, or Privacy and consent.