Problem
trail.js is built, tested and switched off (RFC 0055). It posts to an endpoint that no
service answers. The trail service takes a recording only with a person's sign-in, which
is right for the extension and impossible for the script: the people whose trails it
records are a site's visitors, not our users.
The script needs to say which account a trail belongs to without a secret, because anything in a page is public. Whatever it carries can be copied by anyone who views the source.
Proposal
A diagram is drawn here in the RFCs product; this page does not show diagrams yet.
A site key
- An account makes a site key in the console's Trails page, for one or more of its
verified domains (RFC 0027). It is public by design:
data-keyon the script tag. - A key can do one thing: add script recordings to its account. It cannot read, list or
delete anything. Reading stays with the account's people and its API keys with
trail:read. - A key can be paused, rotated and deleted; a deleted key's uploads are refused at once.
The endpoint
POST /v1/trails/events, the address the script already uses, open at the edge without a sign-in, like the other public writes.- A batch is accepted only when the key is active and the request's
Originis one of the key's domains. A browser setsOriginitself; a script outside a browser can forge it, which the budgets below are for. - Batches from one tab carry the tab's random trail id and become one recording with
client: "script", closed after a quiet period. The service re-checks every event as it does today and drops what the recorder may not send. - No address, cookie or identifier of the visitor is stored. The trail id is the only link between batches and it dies with the tab.
Budgets
Each key has a daily budget of recordings and events, separate from the account's extension uploads, and the account sees what it used. Past the budget the endpoint answers 429 and the script stops sending for the page load. A copied key can fill its own account's budget with noise, which is visible, bounded and fixed by rotating the key; it can never read anything.
Consent is unchanged
The key decides where a trail goes, never whether one is recorded. The script records only
after trail.consent(true), on our sites after the analytics answer, on a customer's site
after their consent tool says yes.
Ours first
Our own account gets the first key, for our site. trails.enabled on the site turns on in
the same change as the legal page's section on trails and a new consent version, as the
site's check already requires.
Alternatives considered
Per-visitor tokens from the customer's backend. Stronger, and a burden for every customer before the first trail. A later option for sites that want it.
Uploads through the customer's own server. Moves the key out of the page and puts our endpoint behind every customer's proxy. Optional later, not the default.
No key, the domain alone. Anyone can claim a domain in a request. The key plus the verified domain at least ties an upload to an account that proved it owns the site.
Decision
Open.
Status log
- 2026-10-07: Written. The script is built and switched off; its endpoint is not routed and no site key exists yet.