The verdict
What the three keys of a verdict mean, and how Botscent chooses the reasons and the agent name.
A verdict is the object that both halves of Botscent return. It says whether an agent is visiting, and names the agent when the evidence allows.
type Verdict = {
type: 'agent' | 'human'
agent_name?: string
reasons: Reason[]
}What type means
type is agent when reasons has at least one reason. type is human when reasons is empty.
human means that Botscent observed no agent evidence. It is never evidence of a person. A probe that the browser does not support, a probe that failed and a negative observation all count as no evidence. Nothing makes a verdict more human.
A person's verdict is always exactly this object:
{ type: 'human', reasons: [] }What reasons lists
Each reason is the id of one piece of evidence that held, such as signer.web-bot-auth.verified or claude.marker.active. The list has no duplicates. It follows the order of the catalogue, strongest first. Reasons lists every reason.
A newer server half can send reasons that an older page half does not know. Those reasons follow the known ones, in the order received.
Some evidence counts only together with other evidence:
instinct.credentials.wrappersandinstinct.geetest.accessor-paircount only together. Then both are listed.- The 3
codex.*reasons count only when at least 2 of them hold. Then every one that holds is listed.
How agent_name is chosen
agent_name is a lower-case id, such as chatgpt or claude-chrome. Agents lists every name. When there is no name, the key is absent. It is never null or an empty string, and a human verdict never has it.
Only 2 kinds of evidence can give a name:
- Declarations: a Web Bot Auth signer host, verified or not. A user-agent token. What the page's own
navigatordeclares. In the page, the name in the server'sServer-Timingentry. - Product shapes: the Muse accessor family, the Instinct wrappers with the GeeTest pair, the Codex shell, the ChatGPT badge and the Claude marker.
Botscent chooses the name in this order:
- If any declaration gives a name, the verdict gets that name only when every declaration gives the same name.
- If no declaration gives a name, the verdict gets a product shape's name only when every product shape gives the same name.
- In every other case, the verdict has no name.
So a request that declares 2 different products in its user agent gets no name. Its type is still agent.
Some evidence never gives a name: the webdriver flag, a HeadlessChrome user agent, input to a hidden document, and the platform's verified-bot field. Verification of a signature never changes the name. Verification changes only the reason, from signer.web-bot-auth.declared to signer.web-bot-auth.verified.
Warning: Do not use
agent_nameto allow access. Anyone can send the headers that produce a name. UseisVerified(verdict, name)on the request's own verdict.
Three verdicts with one shape
Botscent gives a verdict in 3 places. Each one describes a different thing:
| Verdict | Where you get it | What it describes |
|---|---|---|
| The request's own verdict | inspect, or the adapter, such as req.botscent | What one request declared. |
| The page verdict | verdict, useBotscent, subscribe | Whether the page half observed agent evidence at any time in this document. |
| A page report | readReport on your server | The page verdict, as your code sent it. Each reason has the page. prefix. |
combine joins the request's own verdict and a page report. The joined verdict is an agent when either verdict is an agent. See From the page to the server.
How the page verdict changes
The page verdict starts as { type: 'human', reasons: [] }. When the page half observes agent evidence, type becomes agent and stays agent for the rest of the document's life.
The name and the reasons depend only on the set of observations. The order in which evidence arrives does not change them, and neither do duplicates. The name can change when stronger evidence arrives, such as a declaration after a product shape.
During server rendering, framework values show { type: 'human', reasons: [] }. After hydration, they follow the page verdict. So the first render in the browser always matches the server's HTML.
Plain data
A verdict has these 3 keys only. The TypeScript and Python halves give the same JSON for the same evidence. In TypeScript, a verdict is a frozen object. In Python, a verdict is a dict.
What the verdict does not tell you
- It does not prove that a person is present.
- It does not say who controls the browser now, or who performed a particular action.
- It has no confidence value.
- It reports a person as an agent when the person works inside an agent's surface. Examples are the Codex in-app browser, Grok Bot's cloud computer, and a Muse or ChatGPT agent session that the person took over.
- It does not prove which agent is visiting. Only
isVerifiedwith a name proves that. See The trust model.
Related
- The trust model: which verdict you can use to decide access.
- Reasons: every reason, which half gives it, and whether it names an agent.
- Agents: every agent name and the evidence for each.
- Server API (TypeScript): the options of
inspect.