Skip to main content
Command-line interface for QWED verification.

Installation

The qwed CLI is automatically available after installation.

Commands

qwed init - onboarding and bootstrap

Onboarding wizard that sets up verification engines, configures your LLM provider, and bootstraps a local API key — all in a single command. Run this once after installing QWED.
The wizard runs three steps:
  1. Engine readiness — checks that core verification engines (SymPy, Z3, AST, SQLGlot) are installed, then runs deterministic smoke tests to confirm they work correctly
  2. LLM provider setup — select a provider, enter credentials with masked input, validate the key format, and test the connection with retry support
  3. API key bootstrap — asks for the organization name and owner, starts a local QWED server, verifies the server’s identity, creates an organization, and generates a one-time API key you can use immediately
Supported providers: Example session:
The key above is shown in QWED’s documented masked form — a real qwed init run prints the full v2 key (qwed_live_ + 36 characters) once, at that moment.

Organization owner

qwed init signs up the organization it creates with an owner email and password. Use them to sign in to the organization later, for example to read its records in qwed-dashboard.
  • Email. Pass --owner-email, or set QWED_OWNER_EMAIL. qwed init reads QWED_OWNER_EMAIL only when --owner-email is not given. An explicit empty or blank --owner-email is refused (or asked again interactively), never replaced by the environment variable. In interactive mode without either, qwed init asks for the email right after the organization name.
  • Password. qwed init prompts for the password with hidden input and asks you to confirm it. There is no password flag, so the password stays out of your shell history. qwed init never prints the password or saves it to .env.
qwed init checks both values before it starts the server. The server runs the full email validation. If the email is already registered, signup fails with Email already registered. qwed init then tells you how to continue: sign in with that owner (for example to qwed-dashboard) and create a key there, or re-run with another email.

Non-interactive mode (CI/CD)

Use --non-interactive to run qwed init without prompts. This is useful in CI pipelines, Docker builds, or automated provisioning scripts.
In non-interactive mode, qwed init reads the owner password from QWED_OWNER_PASSWORD. Set it from your CI secret store before you run the command:
  • Owner email without a password. If you pass an owner email (flag or QWED_OWNER_EMAIL) and QWED_OWNER_PASSWORD is unset or empty, qwed init prints --owner-email with --non-interactive needs QWED_OWNER_PASSWORD set., exits with status 1, and stops before the server starts.
  • Password isolation. qwed init removes QWED_OWNER_PASSWORD from the environment of the local server it starts. The server never sees it outside the signup request.
  • No owner email. qwed init creates the organization with a generated owner whose credentials are never shown or saved, and prints a warning that nobody can sign in to the organization. Its records cannot be read in qwed-dashboard.
An owner who can sign in is named only when the organization is created. Running qwed init again with --owner-email creates a new organization. It does not recover the records of the earlier one.
All flags:
API keys are never logged or displayed in full. The .env file is written atomically with owner-only permissions (0600 on Unix) and symlink protection. qwed init auto-generates three server secrets on first run. QWED_JWT_SECRET_KEY signs JWTs. QWED_API_KEY_LOOKUP_SECRET derives the HMAC-SHA256 digests the server uses to look up API keys. QWED_AUDIT_SECRET_KEY signs the audit hash chain, and the server refuses to start without it. All three are passed into the local server it starts. If the project-root .env already sets QWED_AUDIT_SECRET_KEY, qwed init reuses that value. The fail-closed import check at server boot no longer trips on a fresh install.
Local server runtime directory: During Step 3, qwed init starts a local API server and needs a writable location for the SQLite bootstrap database (qwed.db). The CLI resolves the runtime directory as follows:
  1. Current working directory — used if it is writable
  2. ~/qwed-demo/ — created and used as a fallback when the current directory is read-only (e.g., inside a container, a mounted volume, or a system-managed path)
The server process starts with its working directory set to this runtime directory, and DATABASE_URL is explicitly set to sqlite:///<runtime_dir>/qwed.db. This ensures bootstrap writes never target a read-only path.
If you previously encountered sqlite3.OperationalError: attempt to write a readonly database during qwed init, upgrade to v4.0.0 or later. The fix automatically resolves a writable directory so Step 3 succeeds regardless of where you run the command.
Server identity handshake: Before bootstrapping an API key, qwed init proves that the server answering on --server-url holds this project’s secrets. A healthy GET /health only shows that something answers on the port. A foreign process, or a stale QWED server started with older secret values, would otherwise receive the bootstrap request and issue a key that the intended server cannot resolve after a restart. The handshake runs on both paths: when qwed init reuses an already-running server, and when it spawns a fresh one.
  1. qwed init generates a fresh random nonce for each run (24 random bytes, URL-safe Base64).
  2. It calls GET /health/identity with that nonce.
  3. The server returns HMAC-SHA256 proofs over the nonce under QWED_JWT_SECRET_KEY, QWED_API_KEY_LOOKUP_SECRET, and QWED_AUDIT_SECRET_KEY (audit_hmac). A server on a different audit key would write audit rows this project’s server can’t verify after a restart.
  4. qwed init computes the expected proofs from its local secrets and compares them in constant time.
The secrets themselves never leave the server. Because each proof is bound to a one-time nonce, a captured response cannot be replayed in a later handshake. A narrow residual race is accepted by design: a local process that binds the port after it is released between the proof and the bootstrap could receive the key without holding either secret. Defending against that active local attacker is out of scope — the handshake addresses accidental staleness — so bootstrap only on hosts you control. The handshake fails closed. qwed init prints [x] Server identity check failed: <reason>, exits with status 1, and does not bootstrap a key when:
Never bootstrap API keys against a server you don’t recognize. If the identity check fails, stop whatever process holds the port before you re-run qwed init.
Security guarantees:
  • Keys are entered with masked input and never displayed in full
  • Keys are never written to logs
  • .env is written with 0600 permissions (owner-read/write only on Unix)
  • .gitignore protection is enforced before writing secrets — the command aborts if .env cannot be gitignored
  • Atomic file writes prevent partial credential exposure
  • Symlink targets are refused to prevent path traversal
  • .gitignore protection is enforced before writing secrets
Placeholder API key detection: During onboarding, qwed init automatically detects and ignores common placeholder or dummy API key values. If your environment variable or --api-key argument contains a placeholder value, QWED treats it as empty and prompts you for a real key. Recognized placeholders include your-api-key, changeme, placeholder, xxx, and short NVIDIA keys such as nvapi-xxxx. This prevents silent failures caused by example values left in .env files or CI templates. The following patterns are rejected:
  • Generic placeholders: test, dummy, sample, example, placeholder, changeme, null, none
  • Wildcard-style values: strings made entirely of x, *, or . characters (e.g. xxx, ****)
  • Template values: strings starting with your- or replace- that contain key
  • Short NVIDIA keys: nvapi- prefixed values shorter than 20 characters
In non-interactive mode, the CLI checks three key sources: CLI argument, environment variable, and NVIDIA fallback. If all three resolve to a placeholder, the command reports that the key is required and exits. You get a clear error instead of a cryptic provider failure downstream. Re-running qwed init: Running the command again merges new values into your existing .env file. Existing variables that you don’t reconfigure are preserved. All three server secrets (QWED_JWT_SECRET_KEY, QWED_API_KEY_LOOKUP_SECRET, and QWED_AUDIT_SECRET_KEY) are retained across re-runs and never rotated automatically. Rotating the lookup secret invalidates every previously issued API key’s lookup digest. Rotating the audit key invalidates the signature on every audit row already written. Projects onboarded before the audit key was required have no QWED_AUDIT_SECRET_KEY in .env. Re-run qwed init to add one; your existing secrets are kept. If your .env already sets QWED_API_KEY_LOOKUP_SECRET equal to QWED_JWT_SECRET_KEY, qwed init refuses to continue: reusing the JWT secret is a value the server will not boot with. Remove the offending line (a fresh, distinct value is generated) or set a distinct value manually. See API key storage and migration for why the lookup secret must stay stable.

qwed doctor - system health check

Run a local health check that reports the status of verification engines, LLM provider connectivity, the QWED server, and the database. Use this after installation or when verification calls fail unexpectedly.
What it checks: The command exits with code 0 when all required checks pass (status OPERATIONAL) and code 1 when any required check fails (status DEGRADED). Optional engines that are missing do not cause a degraded status.

.env override behavior

The doctor command loads your .env file with override mode enabled. This means values in your .env file take precedence over variables already set in the current shell environment. For example, if your shell has ACTIVE_PROVIDER=ollama but your .env file contains ACTIVE_PROVIDER=openai_compat, the doctor report reflects the .env value (openai_compat). This ensures the health report matches the configuration your application actually uses at runtime.

Database URL resolution

When checking database health, doctor prefers the DATABASE_URL environment variable (including values loaded from .env) over the application settings default. The resolution order is:
  1. DATABASE_URL from environment / .env — used if present and non-empty
  2. Application settings — the DATABASE_URL from your QWED configuration
  3. Default — falls back to sqlite:///./qwed.db
This ensures the doctor report checks the database your deployment actually connects to, even when DATABASE_URL is set via environment variables rather than application config. Example output:

JSON output (CI/CD)

Use --json for machine-readable output. Useful in CI pipelines or monitoring scripts.
Options:
Run qwed doctor --json in CI to gate deployments on system health. A non-zero exit code means at least one required component is degraded.

qwed test - deterministic verification tests

Run the built-in deterministic test suite across the Math, Logic, SQL, and Code engines. Every test case has a known expected result and does not depend on an LLM, so outcomes are fully reproducible.
Use this command to confirm that verification engines are working correctly after installation, upgrades, or environment changes. What it tests: The command exits with code 0 when all tests pass and code 1 when any test fails. Example output:
Each line prints the verdict together with its admission decision, so VERIFIED never appears alone. VERIFIED means the engine decided the claim, not that the claim is safe to use. A claim proven false, such as 2+2=5, is VERIFIED (false) | DENY. A test case whose self-check fails prints FAILED instead of a verdict, and an engine that raises prints ERROR.

Verbose mode

Use --verbose to show additional detail per test case, such as computed values and internal status codes.
Options:

qwed verify - one-shot verification

Verify a query and exit.
Options:
  • --provider, -p - LLM provider (openai, anthropic, gemini)
  • --model, -m - Model name (e.g., gpt-4o-mini, llama3). Without it, the CLI reads the selected provider’s model variable (see Provider-specific variables). With an explicit --base-url, it reads no model variable, so pass --model
  • --base-url - Custom API endpoint (for Ollama: http://localhost:11434/v1)
  • --api-key - API key (or use QWED_API_KEY env var). The provider qwed init saved gets a key only from --api-key on the command line or from its own key variable, never from QWED_API_KEY
  • --no-cache - Disable caching
  • --quiet, -q - Minimal output (for scripts)
  • --mask-pii - Mask PII (emails, phone numbers, etc.) before sending queries to the LLM
QWED names no default model. When neither --model nor the provider’s model variable is set, qwed verify stops with an error that names the variable to set. With an explicit --base-url, qwed verify reads no model variable, not even CUSTOM_MODEL, and the error asks for --model. Examples:

qwed interactive - interactive mode

Start an interactive REPL session.
Without --provider, qwed interactive runs the provider qwed init saved in ACTIVE_PROVIDER, with its endpoint and key, as qwed verify does. It takes the model from --model, else from that provider’s model variable, and stops with an error naming the variable when neither is set. Usage:
Special commands:
  • stats - Show cache statistics
  • exit, quit, q - Exit interactive mode

qwed cache - cache management

Manage verification result cache.

qwed cache stats

Show cache statistics.

qwed cache clear

Clear all cached results.

qwed pii - PII detection

Test PII detection on arbitrary text. Requires the qwed[pii] extra.
Example output:
Install PII support with pip install 'qwed[pii]' and download the spaCy model: python -m spacy download en_core_web_lg.

qwed context - Verification Context utilities

New in v7.1.0
Work with Verification Context documents from the command line. All three commands print JSON. validate and from-diagnostic exit with code 1 on any failure. resolve exits with code 1 when it can’t read the file or the attestation check fails, but exits with code 0 when the commitment doesn’t resolve. In CI, gate on the resolved field, not only the exit code. validate and resolve check a document’s attestation by default: it must verify with the issuing server’s keys and sign this document. Pass the keys with one of these options: Pass --server or --jwks-file, not both. The attestation errors are:

qwed context validate

Validate a Verification Context document file against the JSON Schema of its spec_version (v1.0 or v1.1), including the verdict/proof_ref invariants, after the attestation check.
Example output:
An invalid or unreadable file prints {"valid": false, "error": "..."} and exits with code 1. Error values are the attestation errors above, invalid_document_file (file could not be read or parsed as JSON) and validation_failed (schema or invariant violation).

qwed context resolve

Resolve the proof_ref evidence commitment of a document file. The command re-derives the SHA-256 commitment over the canonical bound payload and compares it to the stored value, after the attestation check.
Example output:
resolved is true only for a schema-valid VERIFIED document whose attestation verifies and whose stored proof_ref matches the re-derived commitment. An attestation failure prints {"resolved": false, "error": "..."} and exits with code 1. Any other outcome prints {"resolved": false} and exits with code 0, so check the resolved field: a proof_ref that doesn’t resolve confers no authority.

qwed context from-diagnostic

Create a Verification Context document from a DiagnosticResult JSON file and print it. The command runs locally and has no signing key, so it prints an unsigned v1.0 document. Validate it with --no-require-attestation. For a signed document, use the server’s POST /verification-context/from-diagnostic.
A malformed diagnostic file produces a BLOCKED document rather than a crash. A VERIFIED diagnostic without a valid attestation token fails closed to BLOCKED (fail-closed).

Environment variables

ACTIVE_PROVIDER

Set by qwed init. Tells the CLI which provider to use when no --provider or --base-url flag is given.
Values the CLI runs: ollama, openai-compatible (or openai_compat), openai, openai_direct (run as openai), anthropic, and gemini. The CLI reads the value as the server does: in any case, with -, _, or a space. When ACTIVE_PROVIDER is unset, the CLI runs Ollama. A provider only the server runs, such as azure_openai or auto, stops qwed verify and qwed interactive with an error naming ACTIVE_PROVIDER. So does anthropic with ANTHROPIC_ENDPOINT set, because the CLI calls the Anthropic API directly.

Provider-specific variables

These are written to .env by qwed init and loaded automatically via python-dotenv: The recommended way to set these is via qwed init, which writes them to a .env file automatically. You can also export them manually.

DATABASE_URL

Override the default database connection used by QWED. When set, qwed doctor uses this value for its health check instead of the application settings default.
Supported schemes include sqlite, postgresql, mysql, and mariadb. For SQLite, use the sqlite:/// prefix (e.g., sqlite:///./qwed.db for a relative path or sqlite:////absolute/path/qwed.db for an absolute path).

QWED_API_KEY

API key that qwed verify --api-key reads when the flag is not given. It is used with an explicit --provider or --base-url. It is never sent to the provider qwed init saved in ACTIVE_PROVIDER, because it may be your QWED server’s key: set that provider’s own key variable (such as OPENAI_API_KEY) instead.

QWED_QUIET

Disable colorful branding output.

Configuration

Provider priority

When you run qwed verify without explicit flags, the CLI resolves the provider in this order:
  1. Command-line flags — --provider or --base-url take highest precedence
  2. ACTIVE_PROVIDER env var — set by qwed init or manually in .env
  3. Ollama: when ACTIVE_PROVIDER is unset, the CLI runs Ollama at OLLAMA_BASE_URL (default http://localhost:11434/v1)
If you have run qwed init, the ACTIVE_PROVIDER value in your .env determines which provider and credentials are used automatically.

Models

QWED names no default model. Each provider runs the model you configure, from --model or the provider’s model variable (OPENAI_MODEL, ANTHROPIC_MODEL, GEMINI_MODEL, OLLAMA_MODEL, or CUSTOM_MODEL). The value is sent to the provider unchanged, so any model ID it serves works. When no model is set, qwed verify and qwed interactive stop with an error that names the variable to set. Commands that call no model, such as qwed test, need none.

Output formats

Colorful output (default)

Quiet output (--quiet)

Error output


qwed provider - custom provider management

Manage custom LLM providers defined in ~/.qwed/providers.yaml. See Custom providers for the full guide.

qwed provider import <url>

Import a community provider definition from a URL. The YAML file is downloaded, validated, and saved locally.
Example output:
After importing, the new provider appears as a selectable option when you run qwed init.
Only https URLs are accepted, including across redirects. http:// URLs are rejected before fetching. The imported YAML must contain base_url and api_key_env fields. Downloads time out after 10 seconds.

Use cases

1. Quick verification

2. Scripting

3. Local development

4. Cache performance testing


Troubleshooting

”Ollama not running"

"API key required"

"Module not found”


Advanced features

Caching behavior

  • Default: Enabled (24h TTL)
  • Disable: --no-cache flag
  • Clear: qwed cache clear
  • Stats: qwed cache stats

Multiple providers