Source: https://wraith-daemon-production.up.railway.app/docs/pilot/
Last updated: 2026-10-29

Docs · Pilot guide

# Run an observe-only pilot.

What Wraith observes, what persists, and how to measure it honestly.

## Observe-only by default

Wraith classifies sessions as `human`, `synthetic_browser`, `declared_api`, or `uncertain`, and reports those results in the dashboard. Detection does not block visitors. Routing is optional; it remains inactive unless you configure Actions/intent rules and connect server-side middleware.

## What the browser tracker collects

- Pointer movement summarized in 200 ms buckets, including position and movement statistics.
- Click position and pointer-down-to-pointer-up dwell time.
- Touch taps and drags: start position, dwell, path distance, sample count, and maximum deviation from the touch start.
- Inter-key timing intervals only. The tracker does not read or store the key values.
- The page hostname, so batches replayed from another domain can be rejected — your site key is public in the page source, and this keeps outside noise out of your dashboard.
- Browser signals: the `webdriver` flag, bot-UA and headless-UA flags, plugin count, language count, touch-support flag, and viewport width and height.
- A session identifier. The tracker creates it as `s_` + a random base-36 fragment + the current time in base 36, then stores it in `sessionStorage` as `wraith_sid`. If storage is unavailable, it uses a random identifier without storing it.

The tracker does not read key values, form contents, or cookies. Its regular telemetry fetch requests use `credentials: omit` and its payloads do not contain cookies. The generated session identifier is pseudonymous, not a personal identity.

Deployment through Google Tag Manager is supported — see the [quickstart](https://wraith-daemon-production.up.railway.app/docs/quickstart) for the Custom HTML recipe. Each verdict also pushes a `wraith_classified` event to `window.dataLayer` for your own analytics.

## Server-side classification and routing

For `/api/v1/classify` and `/api/v1/route`, your backend supplies the user agent and a header map. Wraith uses those values in memory to classify the request and derives the crawler session ID from the site, user agent, and request IP using a truncated SHA-1 digest; the IP itself is not written into that ID as plain text. Persisted classify and route payloads retain only `user-agent`, `accept`, `sec-fetch-mode`, and `sec-fetch-site`. Cookies and authorization headers are never stored.

Browser-like requests that headers alone cannot settle come back as `uncertain` with a signed `challenge_url`. Redirecting the browser there loads the tracker bound to the same session, so the first telemetry batch upgrades the verdict in place.

Known search crawlers (Googlebot, Bingbot, and similar) are tagged separately from AI crawlers: intent rules can target the `ai_agents` audience, which excludes them. Serving search crawlers content that differs from what humans see can be treated as cloaking, so prefer `ai_agents` unless a rule specifically intends to answer a search bot.

Pointer motion, key timing, and tap dwell are behavioral signals — treat them under your own privacy policy the way you would analytics data. Wraith stores per-session kinematic summaries, never keystroke content, and the tracker can be self-hosted instead of loaded from our domain if your supply chain requires it.

Wraith also sees the request IP on every batch. It is used in memory for rate limiting, hashed into crawler session IDs, and kept in a short in-memory log of rejected ingests (50 per site) so key problems are visible; it is not written to the telemetry tables.

## Consent

On live visitor traffic, gate the tracker behind your consent manager. With `data-wraith-consent="required"` (or `window.__wraith = {consent: 'required'}` for Tag Manager), the tracker attaches no listeners, mints no session ID, and sends nothing until your banner calls `Wraith.consent(true)`. `Wraith.consent(false)` withdraws: listeners detach, unsent data is dropped, and the stored session ID is removed. Browsers that send Global Privacy Control never collect, whatever the consent call says.

```
<script src="/wraith.js" data-wraith-site="yoursite"
        data-wraith-key="wk_your-site-key" data-wraith-consent="required" async></script>
<script>
  // inside your consent manager's "analytics accepted" callback
  window.Wraith && Wraith.consent(true);
</script>
```

Visitors who decline produce no telemetry, so routing for them falls back to the header-only `/api/v1/classify` verdict.

## Retention and backups

Raw telemetry batches for registered sites remain in the database until deleted. On startup, Wraith restores sessions from the preceding seven days by default; those restored sessions, replay data, and session-derived metrics survive redeploys. The default daily database snapshot retention is seven backups. Snapshots include partner and site keys, so keep downloads private. Pilot data can be deleted at the end of the pilot on request.

## Known limits

- There is no published accuracy figure yet. Calibration → Live verdict accuracy reports the verdicts your site receives against your verified labels, with 95% intervals; quote it only once the intervals are narrow.
- An agent that clicks without moving the pointer may be classified as `uncertain`.
- A crawler that presents a Chrome user agent can evade checks that rely only on request headers.
- Dashboard labels provide the ground truth needed to measure accuracy.

## Measuring accuracy in a sandbox

Run the pilot on a staging copy or a few internal pages before live traffic. Collect sessions whose source is known from how they were produced, never from Wraith's verdict:

- **Humans:** several different people, each on their own device — mouse, trackpad and phone — doing ordinary tasks. One person's visits are one source, however many sessions they produce.
- **Agents:** separate runs of the agents you expect, such as Claude computer use, ChatGPT agent, Playwright, Puppeteer and browser-use, including headed and stealth configurations.
- Put who or what produced each session in the label's `source` (for example `participant-07/trackpad` or `playwright-stealth/run-3`). The evaluation reports results per source.

Wraith's thresholds were tuned on the developer's own visits and test agents, so keep those sessions out of any quoted figure. Interval width depends on sample size: with 30 human sessions and no false "agent" verdicts, the true rate could still be about 10%; showing it is under 1% takes about 300.

## Pilot checklist

1. Complete [Quickstart steps 1–4](https://wraith-daemon-production.up.railway.app/docs/quickstart) on a sandbox or internal pages.
2. Agree on targets before starting: maximum share of humans called agent, minimum share of agents caught, and minimum coverage.
3. Collect and label sessions as above, then read Calibration → Live verdict accuracy.
4. Before live traffic: consent gate on, privacy policy names Wraith, tracker self-hosted if required, data agreement signed.

[quickstart →](https://wraith-daemon-production.up.railway.app/docs/quickstart) [← docs home](https://wraith-daemon-production.up.railway.app/docs)
