Getting started

CLI

The Anvil CLI is the anvil command-line client in apps/cli. Use it to inspect package decisions before install, scan lockfiles, warm the registry cache, request analysis, review reports, manage overrides, and check gateway health.

The CLI is a client. It does not run Anvil Registry by itself. Package decision commands require a running gateway, and protected operator commands require an Admin service plus an admin token. Tiny detail, large difference; otherwise you have installed a very confident phone with nobody on the other end.

Prerequisites

  • Node.js 22.
  • An Anvil Registry gateway, local or remote.
  • An admin token for commands that read or mutate protected operator state.

Install from npm

Install the published CLI globally:

npm install --global @anvilstack/cli

Or run it without a global install:

npx @anvilstack/cli doctor

Then point it at a gateway:

export ANVIL_REGISTRY_URL=http://localhost:4873
anvil doctor

If you do not have a gateway yet, start one with Docker Compose from the repository:

git clone https://github.com/anthonyhumphreys/anvil-registry.git
cd anvil-registry
docker compose -f infra/docker/docker-compose.yml up -d --build gateway worker admin

The local gateway listens on http://localhost:4873; Admin listens on http://localhost:3000; the local admin token defaults to local-dev-token.

For the full local setup, see Quickstart. For hosted infrastructure, see Deployment.

Run from the repository

Use this when developing the CLI itself.

Install workspace dependencies with lifecycle scripts disabled:

pnpm install --ignore-scripts

Run the CLI through the workspace package:

ANVIL_REGISTRY_URL=http://localhost:4873 pnpm --filter @anvilstack/cli dev -- doctor
ANVIL_REGISTRY_URL=http://localhost:4873 pnpm --filter @anvilstack/cli dev -- explain react@latest

Build the CLI when you want the compiled entrypoint:

pnpm --filter @anvilstack/cli build
node apps/cli/dist/index.js doctor

Link the anvil command locally

For a local shell command, build the package and link it:

pnpm --filter @anvilstack/cli build
pnpm --dir apps/cli link --global

Then run:

ANVIL_REGISTRY_URL=http://localhost:4873 anvil doctor

To remove the local development link later:

pnpm --global remove @anvilstack/cli

Configure endpoints

Most commands talk to the gateway:

export ANVIL_REGISTRY_URL=http://localhost:4873

If ANVIL_REGISTRY_URL is not set, the CLI falls back to PUBLIC_BASE_URL, then http://localhost:4873.

Admin-facing commands use the Next.js Admin service URL:

export ANVIL_ADMIN_URL=http://localhost:3000

If ANVIL_ADMIN_URL is not set, the CLI uses http://localhost:3000.

Admin-gated commands read ANVIL_ADMIN_TOKEN, falling back to ADMIN_TOKEN:

export ANVIL_ADMIN_TOKEN=local-dev-token

Keep this token out of committed shell scripts and CI logs. It is small, sharp, and entirely uninterested in your excuses.

Check the gateway

Use doctor before routing installs through the gateway:

anvil doctor

It checks:

  • GET /-/health
  • GET /-/ready
  • GET /-/anvil/policy

The command exits with 0 only when health and readiness pass.

Explain one package

anvil explain react@latest
anvil explain @tanstack/react-query@latest

explain posts to /-/anvil/explain, prints the policy decision, reasons, analysis summary, LLM review summary when present, and any active override.

Exit behaviour:

Decision Exit code
allow 0
warn 0
quarantine 0
block 1

Use scan when quarantine should fail a lockfile gate.

Scan lockfiles

Scan exact dependency versions from lockfiles:

anvil scan package-lock.json
anvil scan pnpm-lock.yaml
anvil scan yarn.lock

Supported inputs:

File Behaviour
package-lock.json Reads resolved package versions from npm lockfile data.
pnpm-lock.yaml Reads package keys from the pnpm lockfile package section.
yarn.lock Reads package entries and resolved versions where available.

Queue static analysis for risky or not-yet-reviewed versions:

anvil scan pnpm-lock.yaml --queue-analysis

scan exits with 1 when any scanned package is blocked or quarantined. Warnings are printed but do not fail the command.

Warm cache and analysis

Use warm before a team or CI fleet switches registry traffic:

ANVIL_REGISTRY_URL=http://localhost:4873 \
ANVIL_ADMIN_TOKEN=local-dev-token \
  anvil warm ./seed-lockfiles/package-lock.web.json

warm fetches package metadata through the gateway and queues analysis for every resolved package version in the lockfile. It does not approve packages or create a bypass.

Watch the queue:

anvil queue status

Smoke test the gateway

Run a basic gateway smoke check:

anvil smoke
anvil smoke is-number

The smoke command checks gateway health/readiness, fetches metadata, verifies tarball URL rewriting through Anvil, fetches the tarball, and optionally checks Admin health when ANVIL_ADMIN_URL is set.

Manage overrides

Create explicit audited overrides:

anvil approve suspicious-pkg@1.2.3 \
  --reason "reviewed package source and install script" \
  --approved-by security-review \
  --expires-at 2026-06-20T00:00:00Z

By default, approve creates an allow override. To create a different action:

anvil approve suspicious-pkg@1.2.3 \
  --action quarantine \
  --reason "allow local review but keep CI blocked" \
  --approved-by security-review

Revoke an override:

anvil revoke suspicious-pkg@1.2.3 --revoked-by security-review

List overrides:

anvil overrides --limit 20
anvil overrides --target suspicious-pkg@1.2.3
anvil overrides --package suspicious-pkg --version 1.2.3

Review audit events:

anvil audit-events --limit 20
anvil audit-events --target suspicious-pkg@1.2.3

Override commands require ANVIL_ADMIN_TOKEN or ADMIN_TOKEN.

Request LLM review

When LLM review is enabled, request reviewer context for a package:

anvil llm-review package@1.2.3 --requested-by security-review --priority high

This queues review work. It does not allow a package, and deterministic policy remains the enforcement authority.

Inspect analysis reports

Fetch the latest matching analysis report:

anvil reports package@1.2.3

Narrow by immutable identity:

anvil reports package@1.2.3 \
  --integrity sha512-example \
  --shasum abc123 \
  --analyser static-v1

Compare two reports for the same package version:

anvil reports compare package@1.2.3 \
  --left-integrity sha512-old \
  --right-integrity sha512-new

Report commands use ANVIL_ADMIN_URL and require an admin token. They read the Admin service JSON route handlers; the browser console shows the same evidence with friendlier tables and package detail pages.

Review Node Base reports

List submitted Anvil Node Base reports:

anvil node-base reports --limit 20
anvil node-base reports --type lifecycle
anvil node-base reports --type ioc --risk high

Fetch one report by id:

anvil node-base report <id>

The command exits with 1 when a listed or fetched Node Base report contains high-risk findings.

Manage the popular package index

Inspect the active popular package index:

anvil popular-index show

Upload a generated index:

anvil popular-index upload popular-index.json \
  --generated-at 2026-05-20T00:00:00Z \
  --uploaded-by security-review

The popular package index helps name-squatting checks compare low-adoption package names against known popular packages. You can inspect and upload the same index through the Admin console at /popular-package-index.

Test policy against package.json

Use policy test for a quick dependency-name check:

anvil policy test package.json

This reads dependency names from package.json and asks the gateway about the latest resolvable versions. For exact installed versions, use anvil scan <lockfile> instead.

CI examples

Scan an npm lockfile in CI:

export ANVIL_REGISTRY_URL=https://npm.example.com
export ANVIL_ADMIN_TOKEN="${ANVIL_ADMIN_TOKEN}"

npm install --global @anvilstack/cli
anvil doctor
anvil scan package-lock.json --queue-analysis

If you prefer not to install globally:

npx @anvilstack/cli doctor
npx @anvilstack/cli scan package-lock.json --queue-analysis

Use npm ci --ignore-scripts or Anvil Node Base safe mode for the actual install. The CLI reviews and warms dependency decisions; it is not a replacement package manager.

Publish the CLI

The npm package is @anvilstack/cli and exposes the anvil binary. Publishing requires access to the @anvilstack npm scope.

From the repository:

npm whoami
pnpm --filter @anvilstack/cli build
pnpm --filter @anvilstack/cli test
pnpm --filter @anvilstack/cli publish --access public

Before publishing a release, verify that the npm README still points users at the gateway setup docs and that anvil doctor works against a local Compose gateway.

Command reference

anvil doctor
anvil explain package@version
anvil scan package-lock.json [--queue-analysis]
anvil scan pnpm-lock.yaml [--queue-analysis]
anvil scan yarn.lock [--queue-analysis]
anvil warm package-lock.json
anvil warm yarn.lock
anvil smoke [package]
anvil approve package@version --reason "intentional dependency" [--approved-by reviewer] [--expires-at 2026-06-20T00:00:00Z]
anvil revoke package@version [--revoked-by reviewer]
anvil llm-review package@version [--requested-by reviewer] [--priority high]
anvil queue status
anvil overrides [--target package@version] [--package package] [--version version] [--limit 20]
anvil audit-events [--target package@version] [--limit 20]
anvil popular-index show
anvil popular-index upload popular-index.json [--generated-at 2026-05-20T00:00:00Z]
anvil reports package@version [--integrity sha512-...] [--shasum ...] [--analyser static-v1]
anvil reports compare package@version [--left-integrity sha512-old] [--right-integrity sha512-new]
anvil node-base reports [--type dependency|lifecycle|ioc|network] [--risk risky|high|medium] [--limit 20]
anvil node-base report <id>
anvil policy test package.json