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

# npx botscent check

Every flag, check, outcome, JSON field and exit code of the install check.

`npx botscent check` tests a Botscent install from outside. It requests the page, opens the page in a local Chrome, and scans the project on disk. Each check prints one line with an outcome. For the procedure that uses this command, see [Verify your install](/docs/verify).

## Synopsis

```sh title="Terminal"
npx botscent check <url> [--origin <url>] [--project <dir>] [--chrome <path>] [--no-browser] [--json] [--report]
```

`<url>` is the page to test. It must be an `http` or `https` URL, and only one URL is allowed.

## Options

| Flag           | Value       | Description                                                                                                          |
| -------------- | ----------- | -------------------------------------------------------------------------------------------------------------------- |
| `--origin`     | a URL       | Also requests the origin directly. The check then names a hop that removes `Server-Timing`.                          |
| `--project`    | a directory | Scans this project. The default is the current directory when it has a `package.json`.                               |
| `--chrome`     | a path      | Drives this Chrome or Chromium. The default is `CHROME_PATH`, then an installed browser, then Playwright's Chromium. |
| `--no-browser` | (none)      | Skips the checks that need a browser. Their outcome is `skipped`.                                                    |
| `--json`       | (none)      | Prints the checks as JSON. See [JSON output](#json-output).                                                          |
| `--report`     | (none)      | Adds a block to paste into an issue. See [The report block](#the-report-block).                                      |

`npx botscent --version` prints the version and exits with code 0.

## What it does

The command does three things, in this order:

1. It requests the page twice as a document navigation. The first request uses the check's own user agent. The second request follows at once with an empty user agent, so a cache that stored the first response serves it to the second.
2. It opens the page in a headless Chrome over the DevTools protocol. Two seconds after the load event, it reads the page half's [`diagnostics`](/docs/page-api#diagnostics) and verdict. It also reads the `botscent` entries that the document received, the console errors and the Content Security Policy violations.
3. It reads the project's declared frameworks and every `botscent` import. A configuration that names an entry as a string, such as Nuxt's `modules: ['botscent/nuxt']`, counts as an import.

With `--origin`, the command also requests the origin with its own user agent. It names a hop that removes `Server-Timing` only when it saw both sides.

## The user agent

The first request sends this `User-Agent`:

```txt title="User-Agent"
botscent-check/<version> (+https://botscent.nibnalin.me/check)
```

`botscent-check` is a registered token. The server half therefore gives the request the reason [`ua.declared-agent-token`](/docs/reasons) and the agent name `botscent-check`. A working server half with its transport on sends back an entry that names `botscent-check`.

Chrome under the DevTools protocol sets the webdriver flag, as any automation does. The page half therefore reports the browser as an agent with `browser.webdriver-flag`.

## Checks

The checks run in this order. The ids are stable.

| Id             | What it checks                                                                                              |
| -------------- | ----------------------------------------------------------------------------------------------------------- |
| `reachable`    | The page answers with a status below 400. A 403, 429 or 503 from the site's bot protection gives `unknown`. |
| `server-half`  | The response to the check's own request carries a `botscent` entry that names `botscent-check`.             |
| `entry`        | The response carries one entry, written between 10 s before and 120 s after the check's request.            |
| `no-store`     | The decorated response has `Cache-Control: no-store`.                                                       |
| `person`       | The request with an empty user agent gets no `botscent` entry.                                              |
| `cache`        | No shared cache serves people HTML that carries a `botscent` entry.                                         |
| `page-script`  | The page half is on the page and started.                                                                   |
| `page-verdict` | The page verdict under automation is an agent with `browser.webdriver-flag`.                                |
| `transport`    | The page received the server's entry, as `diagnostics().transport` shows.                                   |
| `probes`       | No probe failed. A `pending` probe gives `unknown`, and an `unsupported` probe passes.                      |
| `csp`          | The Content Security Policy does not block Botscent's own script.                                           |
| `script`       | `botscent.js`, when the page loads it, comes from the site's own origin.                                    |
| `stack`        | The headers, the HTML or the project show a framework or a platform.                                        |
| `adapters`     | The project imports `botscent`, and an existing proxy or middleware was wrapped, not replaced.              |

### `csp`

The `csp` check fails only when a violation names Botscent's own script. The line reports other blocked resources as context, by kind and directive, without their URL and without a fix.

### `adapters`

The `adapters` check compares a Next.js proxy or Vercel middleware that uses `botscent/next` or `botscent/vercel` with its version at git `HEAD`:

* If the file had code of its own at `HEAD` and now has only Botscent's lines, the proxy was replaced. The check fails.
* A `withBotscent(existing)` call is a wrap, also under an imported alias. The check passes.
* Any other edit gives `unknown`, because a scan of the text cannot tell whether the old code still runs.

## Outcomes

| Outcome   | Meaning                                                                              |
| --------- | ------------------------------------------------------------------------------------ |
| `pass`    | The check saw what a correct install gives.                                          |
| `fail`    | The check saw a fault. The line gives the likely cause and the fix.                  |
| `unknown` | The check cannot tell a correct setup from a fault. The line gives the likely cause. |
| `skipped` | The check had nothing to test, for example without a browser or outside a project.   |

Each line gives the outcome, the check id and the observation. For anything but `pass`, the line also gives the likely cause and the fix. The text of a line is for people and can change in any release.

> **Note:** At an origin with the transport off, `server-half` is `unknown`. The verdict is still in the application's code.

## JSON output

With `--json`, the command prints one object:

```json title="Output"
{
  "check": "<version>",
  "url": "<url>",
  "checks": [{ "id": "", "outcome": "", "observed": "", "detail": "", "cause": "", "fix": "" }],
  "exit": 0
}
```

| Field               | Description                                                |
| ------------------- | ---------------------------------------------------------- |
| `check`             | The version of the command.                                |
| `url`               | The tested URL, without its query or fragment.             |
| `checks`            | One object per check, in the order of [Checks](#checks).   |
| `checks[].id`       | The check id.                                              |
| `checks[].outcome`  | `pass`, `fail`, `unknown` or `skipped`.                    |
| `checks[].observed` | What the check saw.                                        |
| `checks[].detail`   | Optional: the page's own text, such as its error messages. |
| `checks[].cause`    | Optional: the likely cause.                                |
| `checks[].fix`      | Optional: the fix.                                         |
| `exit`              | The exit code.                                             |

The field names are stable. The text of `observed`, `detail`, `cause` and `fix` can change in any release.

## The report block

`--report` adds a block to paste into an issue. The block has only what Botscent owns or bounds:

* The versions, the stack and the adapters found.
* Botscent's own entry at each hop. Other `Server-Timing` entries are counted, not copied.
* The diagnostics and the `observed` text of each check.

The block never has `detail`, a query string, a cookie or an address. Its text can change in any release.

## Exit codes

| Code | Meaning                                                                          |
| ---- | -------------------------------------------------------------------------------- |
| `0`  | Installed. No check failed, and `server-half` or `page-script` passed.           |
| `1`  | Broken. A check failed.                                                          |
| `2`  | Unverified. No check failed, and neither `server-half` nor `page-script` passed. |
| `64` | A usage error, such as a missing URL or an unknown option.                       |

An error that stops the command also gives code 64.

## Stability

These parts are stable across minor versions: the flags, the `--json` field names, the check ids, the outcome values and the exit codes. A change to one of them is a major version. See [Versions and stability](/docs/versions).
