> For an index of all Botscent documentation, see https://botscent.nibnalin.me/llms.txt.

# How it works

What each half of Botscent reads, when each half sees evidence, and how the halves share a verdict.

Botscent looks for agents in two places: in the request that loads a page, and in the page itself. The server half reads the request. The page half watches the page. Each half gives a verdict of the same shape.

```text title="Two halves"
request ──> server half ──> the request's own verdict (inspect)
                 │
                 │  Server-Timing entry, for agents only
                 ▼
page ─────> page half ────> the page verdict (verdict, useBotscent)
                 │
                 │  reportHeaders or data-botscent-field, when your code opts in
                 ▼
your server ──────────────> a page report (readReport)
```

## The server half

The server half reads what one request declares. It finds 3 kinds of evidence:

* a [Web Bot Auth](https://datatracker.ietf.org/doc/draft-ietf-webbotauth-httpsig-protocol/) signature, checked against the signers' keys that each release bundles
* a user-agent token that an agent, a crawler or an HTTP client publishes for itself
* the verified-bot field of the hosting platform, such as Cloudflare's `request.cf`

The server half reads headers only, never the body. It calls no service. It runs on Node.js, Cloudflare Workers, Vercel, Deno and Bun, and in Python. You call [`inspect`](/docs/server-api#inspect), or add an adapter that calls `inspect` for each request.

## The page half

The page half watches the document for evidence that an agent operates the page. It finds 3 kinds of evidence:

* the automation flag, `navigator.webdriver`
* the shapes and markers that agent browsers and extensions leave, such as the Claude for Chrome marker
* input that arrives while the document is hidden

The page half is about 5 KB gzipped and makes no network request. When it sees agent evidence, the page verdict stays `agent` for the rest of the document's life.

## When evidence arrives

Each kind of agent shows its evidence at a different time:

| Agent                                        | Examples                                                                    | Half                                         | When                                                            |
| -------------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------- |
| Agents that sign or declare their requests   | ChatGPT, Devin, Manus, Grok Bot, the Cursor in-app browser                  | server, and the page for Cursor's user agent | At the request                                                  |
| Cloud browsers and browsers under automation | Muse, Instinct, Cloudflare Browser Run, any browser with the webdriver flag | page                                         | At load, before any input                                       |
| Extension and desktop agents                 | ChatGPT for Chrome, Claude for Chrome, the Codex in-app browser             | page                                         | When the agent acts, which can be seconds or minutes after load |
| Agents that send input to a hidden document  | No name from this evidence                                                  | page                                         | At input                                                        |

So the page verdict can change during a visit. Code that reads the page verdict must follow its changes. `useBotscent`, `subscribe` and the `botscent` event on `window` give each change. See [Page API](/docs/page-api).

## One page, both halves

The server half can send the request's own verdict to the page in a `Server-Timing` entry. Then the page verdict holds the evidence of both halves. An adapter writes the entry only for an agent's navigation. By default, it writes the entry only where it runs in front of the cache. See [From the server to the page](/docs/server-to-page).

The page half sends nothing on its own. Your code can send the page verdict to your server as a page report. The server keeps the page report apart from the request's own verdict. See [From the page to the server](/docs/page-to-server).

## What it does not do

* It does not detect automation built to look like a person.
* It does not read a request body, or what a person types into the page.
* It does not say who performed a particular action on the page.
* It does not send anything to the maintainers. There is no telemetry.
* It has not been measured with Comet or with Windows assistive tools.

## Related

* [The verdict](/docs/verdict): what `type`, `agent_name` and `reasons` mean.
* [The trust model](/docs/trust-model): which verdict you can use to decide access.
* [Privacy](/docs/privacy): every probe of the page half and every header that the server half reads.
