# Vigilbase CLI command reference

The CLI uses public scanner APIs on `vigilbase.com` and authenticated APIs on
`one.vigilbase.com`. Use `vigilbase <command> --help` or
`vigilbase help <command>` for built-in help (`vigilbase help cf zones` shows
the same help as `vigilbase cf zones --help`), and `vigilbase help <tool>` (for
example `vigilbase help website-security`) for how to run a tool. Examples
below omit `--json`, which every command supports. Commands are matched in any
case.

## References

| Area | Complete command and API reference |
| --- | --- |
| Cloudflare | [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.10/files/CLOUDFLARE.md) |
| Security | [SECURITY.md](https://app.unpkg.com/@vigilbase/cli@0.2.10/files/SECURITY.md) |
| Customers, organizations, monitoring, support | [ONE.md](https://app.unpkg.com/@vigilbase/cli@0.2.10/files/ONE.md) |
| Local Cloudflare Checkup (cf on your machine) | [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.10/files/LOCAL.md) |
| Sign-in, credentials, configuration, troubleshooting | [README.md](https://app.unpkg.com/@vigilbase/cli@0.2.10/files/README.md) |

The references list the supported operations, filters, response and pagination
behaviour, API endpoints and access requirements of the CLI.

## Global options

| Flag | Meaning |
| --- | --- |
| `--json`, `-j` | JSON on stdout; progress and browser instructions on stderr |
| `--help`, `-h` | Help for the named command; makes no API request. In a terminal the main help starts with the banner; pipes and files get plain usage |
| `--version`, `-V` | The installed version: the banner in a terminal, only the version number when stdout is a pipe or a file, `{"name":"@vigilbase/cli","version":"…"}` with `--json` |
| `--one-url <origin>` | One origin; default `https://one.vigilbase.com` |
| `--auth-url <origin>` | The Identity issuer to trust; default `https://auth.vigilbase.com`. A loopback issuer is trusted only when set here or in `VIGILBASE_AUTH_URL`, with a loopback `--one-url` |
| `--base-url <origin>` | Public origin; default `https://vigilbase.com` |
| `--timeout <ms>` | Per-request timeout, integer 1–2147483647; default 60000 |
| `--client-id <id>` | Device authorization client; default `vigilbase-cli` |

Origins must be HTTP(S) URLs without credentials, paths, query strings or
fragments, and use HTTPS except on loopback (`localhost`, `127.0.0.0/8`,
`[::1]`). An empty flag value is an error; an empty environment variable counts
as unset. A command checks only the origins it uses.
`--customer-id` is a command-specific One customer UUID; it does not change the
session's permissions. API reads may still return 403 when the signed-in role
lacks access. Unknown flags, flags a command doesn't take (including scope
flags), missing arguments and extra arguments fail before any API request. A
missing argument is named with the command's usage, for example
`Missing <url>. Usage: vigilbase headers <url>` or
`Missing <customer-id>. Usage: vigilbase customers summary <customer-id>`.
Invalid values fail the same way, with a message that names the flag and the
allowed values, for example `--severity must be one of: critical, high, medium, low.`,
`--limit must be between 1 and 200.` or
`--start must be an ISO 8601 date-time with an offset, for example 2026-10-12T00:00:00Z.`;
with `--json` these errors have `"code": "INVALID_ARGUMENT"`. An empty or
whitespace-only argument counts as missing. An unknown subcommand gives
``Unknown <group> command "<name>". Run `vigilbase <group> --help` for usage.``
(also `INVALID_ARGUMENT`). An unknown option gives code `UNKNOWN_OPTION`, with
`Did you mean --x?` when one is close; a missing or unexpected option value
gives `INVALID_ARGUMENT` (`--timeout needs a value.`,
`--json does not take a value.`).
`cf dns`, `cf waf` and `cf speed-settings` require `--customer-id` before
they send a request, because One refuses these reads without it. `--style`
applies only to `badge`.

## Authentication

Paths on One are relative to `--one-url`; Identity endpoints come from the
issuer's `/.well-known/openid-configuration` (for production,
`https://auth.vigilbase.com/api/auth/…`).

| Command | Options and behaviour | Requests |
| --- | --- | --- |
| `login` | Device sign-in; `--no-open` for remote terminals. Signing in again with Identity first revokes and removes the sign-in it replaces (before the new device code), unless that one's last refresh had an uncertain result | One `GET /.well-known/oauth-protected-resource`. Identity: discovery, `POST /api/auth/device/code`, polling the token endpoint, then userinfo; you approve at `https://auth.vigilbase.com/device`. Legacy: One `POST /api/auth/device/code`, `POST /api/auth/device/token`, `GET /api/auth/get-session`; you approve at One's `/device` |
| `login --token-stdin` | Check and save a token piped from a secret manager; the input is bounded and never echoed. A saved Identity sign-in is revoked and removed before the token is saved | One `GET /api/auth/get-session`; Identity revocation when a sign-in is saved |
| `login --token <bearer>` | Older input, discouraged because arguments can appear in process lists and history. `VIGILBASE_ONE_TOKEN` also works; neither combines with `--token-stdin` | Same as `--token-stdin` |
| `whoami` | Shows the saved session; signed out is a successful answer. Notes a CLI 0.2.0 `credentials.json` that is being ignored, and names a sign-in saved for another configuration (``A saved sign-in exists for One <origin>, Identity <issuer>, client ID <id>; pass the matching options or run `vigilbase login`.``). When Identity has ended the saved sign-in, it prints `Your saved sign-in was ended by Vigilbase Identity and has been removed.` on stderr and reports you as signed out | Identity: discovery and userinfo (a refresh first if due). Legacy: One `GET /api/auth/get-session` |
| `logout` | Revokes every saved Identity sign-in, signs a legacy session out, then deletes the credential files. A sign-in Identity has already ended counts as revoked; any other failed Identity revocation keeps every file. JSON: `{success, cleared, warnings}`; `warnings` is `[]` when there are none | Identity: discovery, then `POST /api/auth/oauth2/revoke` per saved sign-in. Legacy: One `POST /api/auth/sign-out` (best effort) |
| `doctor` | Checks the public API, your session and your saved sign-in independently; exits 1 if any check fails. `signIn` also fails for a sign-in saved for another One origin, Identity issuer or client ID | `GET {base-url}/api/v1/agent/tools`. Signed out or legacy: One `GET /api/auth/get-session`. Identity: `GET {issuer}/.well-known/openid-configuration`, then the userinfo endpoint it names (One is not contacted); a refresh at the token endpoint first if the access token expires within 60 seconds |

Credentials are saved atomically with owner-only permissions in
`identity-credentials.json` (Identity) or `credentials.json` (legacy One
sessions, shared with CLI 0.1.x), and are tied to the One origin and, for
Identity, the issuer, client and One resource they were issued for. Rotating
refresh tokens are coordinated across CLI processes by `credentials.json.lock`.
Login polling handles pending approval, denial, expiry and short network
failures. Access tokens never appear in CLI output. Signing in isn't supported
on Windows yet. The README covers the files, CLI 0.1.x/0.2.0 compatibility,
logout, directory checks and the config directory.

`doctor` JSON has `checks.publicApi`, `checks.one` and `checks.signIn`, each
`{ok, url?, error?, status?, code?}`. `checks.one.url` is the endpoint it
checked: One's `get-session`, the discovered userinfo endpoint, or the
issuer's discovery URL when discovery itself failed. `checks.signIn` has no
`url`. When a check fails, the top level has the first failing check's
`error`, `status` and `code`.

`whoami` JSON has `authenticated`, `oneBaseUrl`, `user`, `issuer`,
`clientId`, `resource`, `organizationId`, `savedFor`, `signInEnded`,
`expiresAt`, `scope`, `obtainedAt`, `authRequiredTools` and
`legacyIdentityFileIgnored`. `expiresAt` and `scope` are those of the access
token in use, after any refresh `whoami` just made. `obtainedAt` is when you
signed in; a refresh doesn't change it. Signed out, `issuer`, `clientId`,
`resource`, `organizationId`, `expiresAt`, `scope` and `obtainedAt` are `null`.
`savedFor` is `{oneBaseUrl, issuer, clientId}` when a sign-in is saved for
another One origin, Identity issuer or client ID (`issuer` and `clientId` are
`null` for a legacy session), otherwise `null`. `signInEnded` is `true` when
Identity ended the saved sign-in during this command and its file was removed.

`authRequiredTools` means something different per command. The names stay as
they are so scripts keep working:

| Command | `authRequiredTools` in JSON |
| --- | --- |
| `list`, `doctor` | Every tool that needs a One sign-in, whether you are signed in or not. In `list`, `oneExecutableTools` is what you can run through One now, and `[]` when `authenticated` is false |
| `whoami` | The tools your sign-in unlocks: the same tools when signed in, `[]` when signed out |
| `login` | Every tool that needs a One sign-in; all of them are unlocked once login succeeds |

## Public scanners and authenticated scans

| Command | Options | API |
| --- | --- | --- |
| `list` (alias `tools`) | Lists the public catalog and the One-only scanners | GET `/api/v1/agent/tools` |
| `run <tool> <target>` | `--via public\|one`; `--public` (or `--via public`) forces the public API for the public tools | POST `/api/v1/agent/tools/<tool>/execute` (anonymous) or One POST `/api/tools/<tool>/execute` |
| `headers <url>` | Shortcut for `run security-headers`; same routing options | Same as `run` |
| `email <domain>` | Shortcut for `run email-security`; same routing options | Same as `run` |
| `describe <tool>` | Prints the tool's full description and a `Target: <target format>` line; `--no-skill` skips fetching skill Markdown | Catalog plus the tool's published skill URL |
| `skill <tool>` | Prints the complete published skill; JSON includes URL and content | Catalog plus the tool's skill URL |
| `badge <domain>` | `--style default\|compact\|mini` | None; builds the public badge URL locally |

`badge <domain>` takes a domain. A http(s) URL is reduced to its hostname;
case, a trailing dot and a leading `www.` are ignored. Anything else exits 1
with `Enter a domain such as vigilbase.com. Usage: vigilbase badge <domain>`
before any output. JSON for `badge` is
`{success: true, domain: <normalised hostname>, style, url}`; `describe` and
`skill` JSON also start with `success: true`.

Public tools: `security-headers`, `email-security`. One-only tools:
`website-security`, `react2shell`, `fortibleed-check`. Tools aren't commands of
their own: run them with `run <tool> <target>` (`vigilbase help <tool>` prints
that usage offline). Public scans use the
public API unless you add `--via one`; being signed in doesn't change that.
`--public` and `--via one` cannot be combined. A One-only tool with `--public`
or `--via public` stops before any request with
``website-security runs only through Vigilbase One: drop --public and run `vigilbase login`.``
(or `drop --via public`). `run` refuses a tool name it doesn't know locally:
``Unknown tool "<name>". Run `vigilbase list` for the allowlist.``
(code `INVALID_ARGUMENT`), as `describe` does. The public API itself answers a One-only tool with 404
`not_found` and an unknown name with the allowlist URL.

`email <domain>` accepts a fully qualified name with a trailing dot and
internationalised names (converted to punycode). Names under a public domain,
such as `test.vigilbase.com`, are accepted; reserved suffixes (`.test`,
`.example`, `.local`, `.internal`, `.invalid`, `.localhost` and similar) and IP
addresses are refused by vigilbase.com.

Public scans share an hourly quota of 20 scans per client IP address and 20 per
target (1,000 an hour overall); the catalog's
`rateLimit.scanQuota` describes it. When it is used up, the API answers 429
`rate_limit_exceeded` with `Retry-After` set to the seconds until the quota
resets (up to an hour), and the CLI exits 2 without retrying. A 503
`service_unavailable` means only that scan storage is down.

Public paths are relative to `--base-url`. CLI 0.2.1 and earlier call the
unversioned `/api/agent/tools` instead, which vigilbase.com plans to retire on
1 September 2027.

The public route is anonymous and never reads your saved sign-in, the
credentials lock or Identity, so sign-in problems cannot make it fail. Runs
that go through One (`--via one` and the One-only tools) need a sign-in.
Without one they stop with this error:

```
Error: This command needs a Vigilbase One sign-in: run `vigilbase login`.
```

With `--json` the error has code `NOT_SIGNED_IN`; every One command uses the
same sentence and code. Other sign-in errors keep their own message, with that
sentence as the hint unless the message already says to run `vigilbase login`.
An unsafe or relative config path and Windows get no such hint: `login` would
fail the same way, so they keep their own fix.

`list` reads your saved sign-in from disk only (no refresh, no network, no wait
for the lock). In JSON, `authenticated` says whether it looks usable and
`oneExecutableTools` follows it: the tools you can run through One now, `[]`
when signed out. If it can't be used, `list` still prints the
catalog, shows you as signed out and prints one stderr line per run:
``Couldn't use your saved sign-in (<reason>); running anonymously. Run `vigilbase login` to fix.``
For an unsafe config path or credentials file, which `login` would refuse too,
the line ends with the fix instead, for example
``Couldn't use your saved sign-in (/tmp is a symbolic link); running anonymously. Set VIGILBASE_CONFIG_DIR to a private directory without symbolic links in its path.``
`list` and `doctor` ignore a config path that fails the checks when no
credentials file is there.

When a public scan fails and vigilbase.com explains why with a known error
code, the CLI prints `Error: <message> (<code>)` (JSON: `error`, `code`,
`status`), with the message cleaned to one line of at most 300 characters.
Otherwise it prints a message of its own, never the server's text.

When a scan run through Vigilbase One fails (One answers 422), the CLI prints
`Error: The scan couldn't be completed. Try again later.` and exits 1. JSON
has `success: false`, that `error`, `status: 422` and the `toolRunId` One
recorded, for `vigilbase runs get <toolRunId>`.

When the scanner ran but couldn't assess the target, for example because the
site answered with a bot verification challenge or an error page, the CLI
prints `✗ <tool> · <target> · n/a` and the scanner's reason on the next line,
cleaned to one line of at most 300 characters, and exits 1. This looks the
same on the public API and through One. JSON has `success: false`, that reason
as `error`, `toolName`, `target`, `grade: null`, `gradeScore: null`, the
scanner's `result`, `status: 422`, `code: "scan_unassessed"` and the
`toolRunId` when one was recorded. Other public failures (for example a 400
for an invalid target, or a 422 without a result) still print
`Error: <message> (<code>)`.

## Account and billing

| Command | Aliases/options | One API |
| --- | --- | --- |
| `account` | `show`, `me`, `overview` | GET `/api/account` |
| `account services` | `service` | GET `/api/account/services` |
| `account org` | `organization`, `members` | GET `/api/account/organization` |
| `account endpoints` | `endpoint` | GET `/api/account/endpoints` |
| `account sessions` | `session` | GET `/api/account/sessions` |
| `account invites` | `invitations`; optional `list` or `ls` | GET `/api/auth/orgs/invitations` |
| `account invites create` | `add` alias; the email as `<email>` or `--email <email>` (not both), `--role <role>` (default member), required `--confirm`. Usage: `vigilbase account invites create <email>\|--email <email> [--role <role>] --confirm` | POST `/api/auth/orgs/invite` |
| `account billing` | Same as the billing commands | Same as billing |
| `billing` | `summary`, `show`, `status` | GET `/api/account/billing` |
| `billing portal` | `open`; `--no-open` prints the portal URL without opening a browser | POST `/api/billing/portal` |

Invitations need a One browser session: Identity's management API doesn't accept CLI sign-ins, so `account invites` and `account invites create` answer 403 (`browser_session_required`): the CLI prints `You don't have access to this.` and points you to https://one.vigilbase.com.

Creating an invitation sends it; `billing portal` starts a Stripe portal
session. Both are explicit actions. Billing data comes from One's Stripe
integration; the CLI does not store invoices or payment information.

Invitations check the email and role locally and send a fresh UUID
`operationId` each time. Accepted roles: `admin`, `member`, `readonly`,
`partner_owner`, `partner_sales`, `partner_operations`, `partner_finance`,
`partner_readonly`, `billing_only` and `sales_only`. One still checks whether
you may assign the role. `--confirm` is required. Portal URLs must be HTTPS on
Stripe's billing host before they are printed, opened or put in JSON.

## Run history and account Watchdog

| Command | Options/aliases | One API |
| --- | --- | --- |
| `runs` | `list`, `ls`; `--tool <name>`, `--limit <n>` (1–200, default 50) | GET `/api/tool-runs` |
| `runs get <id>` | `show <id>` or `runs <UUID>` | GET `/api/tool-runs/<id>` |
| `runhistory` | Alias of the whole `runs` group | Same as runs |
| `watchdog` | `list`, `targets`, `status` | GET `/api/account/watchdog/targets` |
| `watchdog scans` | `scan`; `--target-id <UUID>`, `--limit <n>` (1–100, default 20) | GET `/api/account/watchdog/scans` |

Text output prints full IDs: the first column of `runs list` works with
`runs get`, `watchdog scans` and `account sessions` show complete IDs, and
`watchdog list` rows are `<target id>  <domain>  <uptime status>  24h=<uptime>`,
whose target ID is what `watchdog scans --target-id` takes.

Customer-specific Watchdog and monitoring views are in ONE.md.

## Local Cloudflare Checkup (no sign-in)

All vigilbase.com paths below are under
`https://vigilbase.com/api/v1/tools/cloudflare-checkup/local-session`.

| Command | Options | Requests |
| --- | --- | --- |
| `cloudflare-checkup` (≡ `local cloudflare-checkup [run]`) | `--email`, `--account-id`, `--out`, `--overwrite`, `--yes` (install cf without asking), `--confirm` (accepted, no longer needed), `--no-open`, `--cf-auth auto\|profile\|env\|default` (default `auto`), `--profile`, `--cf-bin`, `--max-requests-per-5min`, `--base-url` (vigilbase.com or loopback), `--timeout` | In order: GET `…/availability` (before the email is asked for), POST `…/request` and `…/verify` (email verification), GET `…/local-session` (before cf runs), allowlisted read-only cf commands, GET `…/local-session` again and POST `…/local-session/snapshot` once the snapshot is saved and shown (automatic; `collect` never uploads). npm only to install the pinned cf when none is usable (asks first) |
| `cloudflare-checkup login` | `--profile`, `--no-open`, `--dry-run`, `--cf-bin` | Runs `cf auth create <profile> [--no-browser] --scopes …` interactively (`--no-browser` comes from `--no-open`) |
| `cloudflare-checkup doctor` | `--cf-auth`, `--profile`, `--cf-bin` | Offline cf mapping check, one `cf auth whoami` |
| `cloudflare-checkup accounts` | `--cf-auth`, `--profile`, `--cf-bin` | `cf accounts list` (all pages) |
| `cloudflare-checkup collect` | `--out` (required), `--account-id`, `--overwrite`, `--dry-run`, `--max-requests-per-5min`, cf options | Allowlisted read-only cf commands; writes the snapshot |
| `cloudflare-checkup inspect <file>` | — | None |
| `cloudflare-checkup submit <file>` | `--email`, `--no-open`, `--base-url`, `--timeout` (`--yes`/`--confirm` accepted, not needed) | After checking the file: GET `…/availability`, POST `…/request` and `…/verify`, GET `…/local-session`, POST `…/local-session/snapshot` |
| `cloudflare-checkup logout` | `--profile`, `--cf-bin` | Runs `cf auth delete <profile>` interactively, then removes the cf Vigilbase installed |

Each subcommand also works as `local cloudflare-checkup <subcommand>`.

The one command and `submit` first ask vigilbase.com whether local checkups are
available (no email, no credentials). When they are switched off, the command
stops before asking for your email with
`Local checkups aren't available right now.` and the hint
`Run the Cloudflare Checkup in your browser instead: https://vigilbase.com/tools/cloudflare-checkup`
(`LOCAL_MODE_DISABLED`). When vigilbase.com needs a newer CLI, it stops with
`CLI_UPGRADE_REQUIRED`, also before any email. If vigilbase.com doesn't answer
the check, the command goes on.

Then they verify your work email: the CLI asks for it (or takes `--email`),
vigilbase.com emails a 6-digit code, and the CLI asks for the code (without a
terminal, both are read from stdin, one line each; add `--yes`). In a terminal
both show an interactive timeline on stderr (inputs, arrow-key questions,
spinners, a collection progress bar); pipes, CI, `TERM=dumb` and `--json` keep
plain lines and prompts. Ctrl-C exits 130 (SIGTERM 143, SIGHUP 129), and a
cancelled run never uploads. The address, the code and the session code are
never echoed, logged, stored or passed to cf or npm. cf output and stderr are
never echoed; failures carry a stable `code` (in plain text after the message,
for example `Error: Local checkups aren't available right now. (LOCAL_MODE_DISABLED)`)
and a `hint` that usually says to run the same command again. See LOCAL.md for the email
verification, the managed cf install, the allowlist, the environment policy,
the uploaded data and the error codes.

## Output and effects

Exit codes: **0** success, **1** validation, API, network or sign-in failure,
**2** HTTP 429 rate limiting; `cloudflare-checkup` also exits **130**, **143**
or **129** when cancelled by SIGINT (Ctrl-C), SIGTERM or SIGHUP (128 + the
signal number). A graded scan exits 0 whatever the grade. Its first line starts
with `✓` only when the result's `status` is `"secure"` and the grade isn't F;
otherwise it starts with `•`. Scripts should gate on `.grade` or
`.result.status`. When vigilbase.com returns a shareable report link, text
output ends with `  Report: <url>`. A scan that failed exits 1. So does a scan
the scanner could not grade (result `status` `"error"`, no grade): it prints
`✗ <tool> · <target> · n/a` and the scanner's message, cleaned to one line of
at most 300 characters, and JSON has `success: false` with that message as
`error`. A signed-out `whoami` is not a failure, and a healthy `doctor` does
not need a sign-in. API error JSON keeps the status, code and retry delay when
available.

Vigilbase One's error messages are never shown, because they can describe
One's configuration. The CLI prints its own message for the status instead:
``Authentication required. Run `vigilbase login`.`` (401),
`You don't have access to this.` (403), `Rate limit exceeded.` (429) and
`Request failed (HTTP <status>).` otherwise. When it recognises the failure
from the route, the status and One's error code, it adds a hint of its own
(`Hint: …` in text, `hint` in JSON):

| One answers | Hint |
| --- | --- |
| 400 without `--customer-id` to `support tickets`, or to a `cf` command that reads the Cloudflare workspace or checkups (`cf accounts`, `cf account`, `cf workspace`, `cf zones` with filters, `cf checkup`, `cf checkup start`) | `Vigilbase staff: add --customer-id <uuid>.` |
| 403 to `security settings …` or `security notifications preferences` with `--customer-id` | `Personal settings belong to each user, so View-as (--customer-id) cannot read them.` |

A security command that needs a customer (`FLAREHAWK_VIEW_AS_REQUIRED`) says
`Select a customer with --customer-id <customer UUID> for security commands.`

Security and customer operations keep JSON response envelopes and pagination
fields. Cloudflare inventory commands may apply the account or zone filter you
asked for locally as well as sending it to One; zone detail returns the
selected object. Text output is for people; use JSON for automation and
evidence export.

Provider credentials, signed delegation claims, internal bearer tokens and
organization overrides are never CLI inputs. Commands use named One routes,
with two exceptions. Local provider reads: `vigilbase cloudflare-checkup` runs
Cloudflare's own `cf` CLI on your machine with a fixed allowlist of read-only
commands; cf owns your Cloudflare login, and the Vigilbase CLI never reads,
prints, stores or uploads it. And the vigilbase.com local-session routes, which
are the checkup's only Vigilbase requests: GET `…/availability`, POST
`…/request` and POST `…/verify` without a credential, then GET `…/local-session`
and, once the snapshot is saved and shown, POST `…/local-session/snapshot` with the exact snapshot
file, both authorized by the session code the email verification returned (see
LOCAL.md). Webhooks, cron and admin repair endpoints, interactive OAuth
handoffs, streaming or binary downloads, and endpoints that create records on
GET are not exposed. Remediation that needs durable approval and receipts stays
in its existing One and Vigil flows.
