CLI Reference
Command reference for the usertrust CLI — the six everyday commands in full (init, inspect, health, verify, snapshot, tb) and the eleven others it registers.
All CLI commands are available via npx usertrust.
Installation
npm install usertrustThe CLI is available as npx usertrust.
usertrust init
Interactive wizard that creates the .usertrust/ vault. It prompts for one or more provider API keys, a total budget in dollars for the project, which does not reset (Enter takes $5; stored as usertokens, dollars × 10,000), whether to use the built-in rate table or custom per-model rates, and optionally a local inference endpoint (Ollama, LM Studio or vLLM). Anthropic, OpenAI and Google keys are checked against the provider unless --skip-verify is passed; a key that fails the check is still saved, with a warning, and a key for any other provider is not checked. It writes the config and the default policy file; the audit chain is not created until the first governed call. If a vault already exists, init exits 0 and keeps its config (--json reports created: false) unless --reconfigure is passed. The key is typed masked, never echoed, and an existing .env is left as it is. When stdin is not a terminal, init asks nothing and writes the same defaults as --json.
npx usertrust init # interactive wizard
npx usertrust init --json # non-interactive: budget 50,000 usertokens, no providers, an .env template
npx usertrust init --skip-verify # accept keys without calling the provider
npx usertrust init --reconfigure # run the wizard again over an existing vaultCreates:
.usertrust/
├── usertrust.config.json
├── .env # the keys the wizard was given, else a template of commented lines; mode 0600
├── .gitignore # tigerbeetle/, *.tigerbeetle, dlq/, .env, .env.*.tmp
├── audit/ # empty until the first governed call writes events.jsonl
├── policies/
│ └── default.yml
├── patterns/
├── snapshots/
├── board/
└── dlq/The vault directory is chmod 700 and every file init writes is mode 600. audit/events.jsonl, audit/index.json, patterns/memory.json, leases.json and dlq/dead-letters.jsonl are created lazily by the runtime components that own them, and init never creates a tigerbeetle/ directory.
usertrust.config.json written by init: budget, tier "mini", policies "./policies/default.yml", pii "warn", board, circuitBreaker, patterns, audit, providers, pricing (plus customRates, endpoints, local when chosen)usertrust inspect
Shows a vault bank statement — budget, spend, remaining balance, recent transactions.
npx usertrust inspect
npx usertrust inspect --json # machine-readable outputusertrust health
Entropy diagnostics with 6 signals, scoring 0-100:
npx usertrust health
npx usertrust health --json0 is healthy and 100 is maximum entropy. The level is low below 30, elevated from 30 and critical from 60; a failed chain, an ambiguous settlement or a fully spent budget floors the score at 60. The six signals are policy violations, budget utilization, chain integrity, PII detections, anomaly aborts and injection pattern matches, and the policy file is validated and reported on its own line first.
usertrust verify
Verifies the integrity of the SHA-256 hash-chained audit trail:
npx usertrust verify
npx usertrust verify --json
npx usertrust verify <project-root> # at most one path: the directory that holds .usertrust/, not .usertrust itselfChecks:
- Every event's hash covers the previous event's hash
- Chain starts from GENESIS_HASH (64 zeros)
- Sequence numbers start at 1 and have no gaps
- The head recorded in
audit/events.jsonl.metamatches the chain tail. This catches a cut made while the sidecar was left alone, and only until the next governed call from a freshly started process; it does not catch a cut made with the sidecar rewritten to match
What this does and does not catch is in Verification. Verification spans rotated *.jsonl segments as one chain. On a FAILED verdict, or when no vault exists, the process exit code is 1, so the command works as a CI gate. The plain form reports Anchor state: UNANCHORED because it checks internal consistency only; for independent verification pass externally fetched anchor material: --anchor <file> or --anchors <file> (repeatable, - reads stdin), --pubkey <pem>, --bundle <file>, --anchor-url <url>, --successor-pin <pem>, --require-anchor, --require-external-anchor, --max-anchor-age <dur>, --max-unanchored-events <n>, --vault-id <id>. --receipts <file> cross-checks receipts you kept against the chain and exits 1 (INCOMPLETE) when one names an event the log no longer holds. Unknown flags, more than one path, or an empty path exit 2 with a message; a path that is not a directory, or that holds no .usertrust/ vault, exits 1.
usertrust snapshot
Create and restore vault state checkpoints:
npx usertrust snapshot create <name> # create a snapshot
npx usertrust snapshot list # list available snapshots
npx usertrust snapshot restore <name> # restore from snapshot
npx usertrust snapshot restore <name> --force # acknowledge that a live TigerBeetle ledger is not rolled backThe subcommand is positional and required: bare usertrust snapshot, --list or --restore only print Usage: usertrust snapshot <create|restore|list> [name]. A snapshot captures audit/, policies/, patterns/, usertrust.config.json, leases.json and spend-ledger.json; tigerbeetle/, snapshots/, dlq/ and audit/anchors are never captured. --force bypasses only the refusal that a live tigerbeetle/ ledger cannot be rolled back. snapshot create exits 1 when it fails; a missing name or vault, and every restore outcome, exit 0.
snapshot restore writes the snapshot's files over the live ones and deletes nothing, so events appended after the snapshot was taken are dropped, with no warning and no backup, and it exits 0 even when it refuses. snapshot create takes no writer lock and overwrites a snapshot that already has that name. Read the Snapshot restored: line, or parse the --json output and test .success === true, not the exit code. Stop every usertrust process that writes the vault before you create or restore a snapshot. See LIMITATIONS.md section 15.
usertrust tb
TigerBeetle process management:
npx usertrust tb start # NOT YET IMPLEMENTED — prints the manual command below
npx usertrust tb stop # NOT YET IMPLEMENTED — prints the manual command below
npx usertrust tb status # works: reports whether a TigerBeetle process is runningtb start and tb stop are stubs today. They exit without managing anything and
print the manual command instead — process lifecycle management is a future
version. tb status is real: it checks for a running tigerbeetle process.
Start and stop TigerBeetle yourself:
mkdir -p ./data # format writes the file, not the directory above it
tigerbeetle format --cluster=0 --replica=0 --replica-count=1 --development ./data/0_0.tigerbeetle
tigerbeetle start --addresses=3001 --cache-grid=256MiB ./data/0_0.tigerbeetle
kill $(pgrep tigerbeetle)Two things this block gets right that are easy to get wrong:
mkdir -p ./datafirst.usertrust initcreates the.usertrust/vault, not./data, andtigerbeetle formatexitserror: FileNotFoundwhen the parent directory is missing.- Port 3001, not 3000. With the generated config, the SDK dials
127.0.0.1:3001—tigerbeetle.addressesis not written byinit, so the schema default inpackages/coreapplies. Thetb startstub prints that same address (--addresses=127.0.0.1:3001); bind it as above, or settigerbeetle.addressesin.usertrust/usertrust.config.jsonto whatever you did bind.
See the TigerBeetle quick start for installation and for running it in Docker.
usertrust completions
Generate shell completion scripts:
# Bash
npx usertrust completions bash > ~/.local/share/bash-completion/completions/usertrust
# Zsh
npx usertrust completions zsh > "${fpath[1]}/_usertrust"
# Fish
npx usertrust completions fish > ~/.config/fish/completions/usertrust.fishOther commands
The CLI registers seventeen commands. The ten not covered above:
npx usertrust policy validate [path] # check the policy file loads and would enforce; exit 2 on bad arguments
npx usertrust export --markdown <dir> # write receipts as markdown (Obsidian-ready)
npx usertrust anchor <init|now|status|export|export-bundle|doctor|rotate|resume>
# external audit anchoring; sinks: --sink-file, --sink-url, --sink-s3
npx usertrust pricing # show the rate configuration in effect
npx usertrust budget --cost-center <name> --allocated <int> [--parent <id>] [--period-start <iso>] [--period-end <iso>]
# one cost center's balance and runway
npx usertrust secret <add|rm|ls|get|rotate> # credentials in the encrypted vault store
npx usertrust skill verify <manifest.json> # validate a skill manifest against the supply-chain policy
npx usertrust ui # launch usertrust-ui if installed, else print the npx one-liner
npx usertrust audit quarantine-tail [--dry-run]
# move a torn final line of the audit log aside, recorded on the chain
npx usertrust report --week --end <iso> [--format md|html|json] [--out <path>] [--agent-key <key>] [--segment <n>] [--top <n>]
# weekly spend report from the vault; --end (with a time zone) is requiredFlags and exit codes:
| Command | Flags | Exit codes |
|---|---|---|
policy validate [path] | --json; with no path it reads the file named by policies in the config | 0 valid (or no policy file and no path given); 1 invalid rules, a missing explicit file or an unreadable config; 2 bad arguments |
export --markdown <dir> | --markdown is required; --json | 0 written; 1 no vault, no directory given or a failure (the output also reports whether the chain verified) |
anchor <action> | init and rotate take --key-file <path>; now and doctor take the sink flags --sink-file <path>, --sink-url <url> and --sink-s3 bucket=B,region=R[,prefix=P][,endpoint=E]; export and export-bundle take --since <anchorSeq>; resume takes --latest <record|-> | 0 done; 1 on a failure or an unknown action |
pricing | --json | 0 |
budget | --cost-center <name> and --allocated <int> are required; --parent <id> (else $USERTRUST_USER_ID, else local), --period-start <iso>, --period-end <iso>. Connects to TigerBeetle at tigerbeetle.addresses | 0 printed; 1 on missing or invalid flags, no vault or a ledger failure |
secret <sub> | add <name> <value>, rm <name>, ls, get <name>, rotate <name> <value> | 0, except 1 when the command or the vault cleanup after it fails |
skill verify <manifest.json> [entry] | optional second path: the skill's entry file, hashed against the manifest | 0 whatever the verdict; read success in the --json output |
ui [args] | forwards its arguments to usertrust-ui when installed; ignores --json. --allow-host <name> (repeatable) also answers requests whose Host is that DNS name, for a private network proxy such as tailscale serve that forwards to the loopback bind (the server still binds 127.0.0.1 only; a wildcard, IP address or invalid name exits 2). Use it only behind a private network; never behind a public tunnel | 0 |
audit quarantine-tail | --dry-run | 0 done; 1 refused or failed; 2 bad arguments |
report --week --end <iso> | --format md|html|json (default md, or json with --json), --out <path>, --agent-key <key>, --segment <n>, --top <n> (default 10), --budget <int> or --tenant-config <file> (one of the two) | 0 reported; 1 no vault or an operational failure; 2 bad arguments, or the chain did not verify (the report is stamped UNVERIFIED) |
Global options
--json— Machine-readable JSON output. Every command takes it exceptui.--skip-verify,--reconfigure— Read only byinit;reportrefuses them- An unknown command within three edits of a real one prints
Did you mean "…"?; anything else prints the command list