Skip to content

CLI reference

norviq is the command-line client for the Norviq API. Use it for the first login, password recovery, and day-to-day operations (policies, audit, agent trust, the red-team suite, fleet).

Everything the CLI does, it does over the HTTP API — except norviq login and norviq admin reset-password, which shell out to kubectl exec because they exist for the case where you have no token yet.

The CLI ships in the norviq Python package on PyPI. Python 3.11 or newer.

Terminal window
pip install norviq
norviq --help

Pin it to the platform you are talking to — the same package carries the SDK, and the API and CLI are released together:

Terminal window
pip install norviq==0.2.5

The framework extras (norviq[langchain], norviq[crewai], …) are for the in-process SDK, not the CLI. The CLI needs no extras.

These are options on the top-level group, so they go before the subcommand. Each also reads an environment variable.

Option Env var Default Purpose
--api-url NRVQ_API_URL http://127.0.0.1:8080 Base URL of the Norviq API
--token NRVQ_API_TOKEN (empty) Bearer credential
-o, --output table table or json
Terminal window
norviq --api-url https://norviq.internal --token "$NRVQ_API_TOKEN" policy list

--token accepts either a session JWT (from norviq login, or the console) or a Norviq API key — the nrvq_-prefixed string minted at Settings → API keys in the console (POST /api/v1/keys, admin-only, secret shown once). The API dispatches on the nrvq_ prefix, so both work in the same field.

-o json prints the raw API payload. It is honoured by policy list, policy get, policy versions, audit list, audit stats, audit top-blocked, agent list and agent get. Every other command prints a fixed human-readable summary and ignores -ostatus, policy create/delete/dry-run/rollback/apply, agent reset-trust, agent freeze, fleet *, config *, login, admin reset-password. Table output prints No results. on an empty set and a (n results) footer otherwise.

If a file named .env exists in the current working directory, the CLI loads it at startup. It only fills in variables that are not already set — a real environment variable always wins over the file.

Terminal window
cat > .env <<'EOF'
NRVQ_API_URL=https://norviq.internal
NRVQ_API_TOKEN=nrvq_...
EOF
norviq policy list # no flags needed

Every API failure exits 1 with a single ERROR: line on stderr. 401 is reported as Authentication failed - check NRVQ_API_TOKEN., 404 as Resource not found., and everything else as API error (<status>): <detail from the API>. The HTTP timeout is 10 seconds.

Roles are admin > service > viewer. A non-admin principal is scoped to its own namespace, so a read that “works” may return a narrower set than an admin sees.

Command Credential
status none/healthz and /readyz are unauthenticated
redteam catalog none — the catalog is bundled in the package, no HTTP call
policy list / get / versions any authenticated principal (namespace-scoped)
audit *, agent list / get, fleet status any authenticated principal (namespace-scoped)
redteam run / single any authenticated principal — the CLI replays through /evaluate
policy create, policy delete, policy dry-run admin or service (admin to move a cluster-band policy) — dry-run compiles and executes submitted rego against OPA, a write-class capability, not a passive read
policy rollback, policy apply admin
agent freeze, agent reset-trust admin
fleet join, fleet leave admin
login, admin reset-password no API token — kubectl exec rights on the release namespace
config show / set local only, no API call

norviq login mints a short-lived admin token inside the cluster via kubectl exec — the signing key never leaves the API pod and is never printed. It returns a ready-to-use console sign-in link, so you can reach the console on a fresh install with no identity provider configured.

Terminal window
norviq login -n norviq --console-url http://localhost:8080
Option Default Purpose
-n, --namespace norviq Kubernetes namespace of the Norviq release
--context current kubectl context to use
--ttl 3600 Token lifetime, in seconds
--console-url http://localhost:8080 Where you reach the console (port-forward / ingress)
sequenceDiagram
    participant You as norviq login
    participant K as kubectl
    participant P as norviq-api pod (container "api")
    You->>K: exec deploy/norviq-api -c api
    K->>P: python -m norviq.api.token_mint --ttl 3600
    P-->>K: signed admin JWT (signing key stays in the pod)
    K-->>You: token on stdout
    You->>You: print <console-url>/login#access_token=...

It prints a …/login#access_token=… deep link (click to sign in, no password) and the raw token (paste it into the console login screen, or export it as NRVQ_API_TOKEN for the CLI).

Requires kubectl on PATH and RBAC that permits exec into the release namespace — that kubectl access is the authorization. The command fails with a named error if kubectl is missing, if the exec times out (30 s), or if the pod returns an empty token.

norviq admin reset-password resets a local user’s password from inside the cluster — a no-egress recovery path with no email or SMTP. It sets a fresh one-time password and forces a change on the next login. Nothing leaves the cluster.

Terminal window
norviq admin reset-password -n norviq --username admin
Option Default Purpose
-n, --namespace norviq Kubernetes namespace of the release
--context current kubectl context
-u, --username admin Local user to reset
--console-url http://localhost:8080 Console URL for the sign-in hint

It prints a one-time username / password — sign in with them and the console immediately forces you to set a new password. Same kubectl exec mechanism and same 30-second timeout as norviq login.

Terminal window
norviq status
API: Online
Redis: Connected
DB: Connected

It reads GET /healthz (liveness) and GET /readyz (dependency probe).

Terminal window
norviq policy list # every policy you can read
norviq policy get <namespace> <agent-class> # one policy + its rego source
norviq policy create -f guard.rego -n prod -c chatbot --mode block # block | audit | escalate
norviq policy dry-run -f guard.rego -n prod -c chatbot # replay against recent traffic, no write
norviq policy versions <namespace> <agent-class> # version history
norviq policy rollback <namespace> <agent-class> 3 # restore version 3
norviq policy apply <namespace> <agent-class> --target-ns prod # bind it to a target scope
norviq policy delete <namespace> <agent-class> # prompts for confirmation

create and dry-run both require -f/--file, -n/--namespace and -c/--class. --mode sets the enforcement mode recorded with the policy and defaults to block, and is accepted only by create and apply.

dry-run writes nothing. It compiles the candidate rego and replays it against the scope’s last 24 hours of real audit records, then prints four fields of the response:

Records checked: 1284
Would block: 37
Would allow: 1247
Recommendation: No currently-allowed traffic would be newly blocked — safe to deploy.

Read Recommendation rather than Would block: it is computed from newly blocked calls — the decision flips this candidate would cause — while Would block includes traffic the live policy already blocks. An empty scope is reported honestly (No recent real traffic for this scope — cannot simulate impact) rather than as a clean run, and a partial replay says so. The full response carries more than the CLI prints (newly_blocked, newly_blocked_samples, block_rate_pct, eval_errors). policy dry-run prints the fixed summary above and ignores -o json; call POST /api/v1/policies/dry-run or use the console’s Policy Tester to see the whole payload.

Terminal window
norviq policy apply prod chatbot --target-type workload --target-ns prod --target-name checkout-agent
Option Default Notes
--target-type agent_class agent_class, workload, or namespace
--target-ns Required
--target-name (empty) Required when --target-type workload — the CLI refuses the call otherwise
--target-kind deployment deployment is the only accepted value; the evaluator resolves no other kind
--mode block block, audit, or escalate

The workload guard is deliberate: the server keys a workload policy at deployment:<name>, which is what the engine looks up at evaluation time. With no name there is no key, and an earlier build silently fell back to a class policy while reporting success.

See Writing policies.

Terminal window
norviq audit list -n prod -d block -t execute_sql --range 24h --limit 50
norviq audit stats --range 7d -n prod # totals by decision
norviq audit top-blocked --range 24h # most-blocked tools
Flag Applies to Default Notes
-n, --namespace all three all readable Your role may already pin this
-d, --decision list all allow, block, escalate, audit
-t, --tool list all Case-insensitive substring match, applied server-side across the whole range
--range all three 24h Exactly 1h, 6h, 24h, 7d, 30d
-l, --limit list 20 Server caps at 500

--range and --limit are not validated client-side. --range 90d or --limit 1000 reaches the API and comes back as ERROR: API error (422): ….

The HTTP endpoint supports filters the CLI does not expose (agent, framework, rule_id, exclude_synthetic, offset) — reach for GET /api/v1/audit/records or the console’s Audit Log when you need those. GET /api/v1/audit/export is the authenticated bulk export.

Terminal window
norviq agent list # agents with trust score, category, violation count
norviq agent get <spiffe-id> # one agent's trust detail
norviq agent freeze <spiffe-id> # kill switch — sets the score to 0.0
norviq agent reset-trust <spiffe-id> --score 1.0 # clear the freeze and the cap

freeze and reset-trust are the same call — PUT /api/v1/agents/{spiffe_id}/trust, admin only — with a different score. The SPIFFE ID is a path argument, so quote it:

Terminal window
norviq agent freeze 'spiffe://norviq/ns/prod/sa/checkout-agent'

The freeze/cap is persisted in the database as well as Redis, so a cache flush or Redis restart cannot silently lift the kill switch.

See Concepts → Trust score.

Prove a policy actually blocks — run the built-in adversarial attacks against a target.

Terminal window
norviq redteam catalog # print the bundled catalog (no HTTP call)
norviq redteam run --namespace prod --agent chatbot # run the suite
norviq redteam run --category prompt_injection # one category
norviq redteam run --agent chatbot -o markdown # markdown report
norviq redteam single <attack-id> # run a single attack
Option run single Default
--api-url yes yes inherited from the global option
--token yes yes inherited from the global option
--agent yes test-agent
--namespace yes default
--category yes all categories
-o, --output yes table (also json, markdown)

redteam run is the one place -o markdown exists; the global --output only offers table/json.

--category takes one of: prompt_injection, data_leakage, supply_chain, excessive_agency, unbounded_consumption, cross_tenant, sql_injection, shell_injection, trust_manipulation, chain_exploit, policy_bypass, sector_policy, mcp_identity, policy_composition. An unknown value raises a ValueError from the enum rather than a friendly message.

run and single declare their own --api-url / --token and fall back to the global ones, so both of these work and mean the same thing:

Terminal window
norviq --api-url https://norviq.internal --token "$NRVQ_API_TOKEN" redteam run --namespace prod
norviq redteam run --api-url https://norviq.internal --token "$NRVQ_API_TOKEN" --namespace prod

The per-command flag wins when both are given. NRVQ_API_URL / NRVQ_API_TOKEN feed the global options and therefore reach both paths.

The console and CI can call these HTTP endpoints directly to drive the red team. All are under the /api/v1 prefix and require an admin bearer token.

Method & path Purpose
POST /api/v1/redteam/run?attack_id=<id>&target_agent=<class>&target_namespace=<ns> Run one attack against the chosen target identity. Returns the result row (decision, rule_id, latency) + trust score. attack_id is required; 404 if unknown.
POST /api/v1/redteam/suite?target_agent=<class>&target_namespace=<ns> Run the full suite against each seeded class in the namespace (target_agent optional). Returns a run_id; 409 (with the in-flight run_id) if a suite is already running for that namespace.
GET /api/v1/redteam/catalog The attack catalog — each entry mapped to its MITRE ATLAS technique + OWASP LLM control.
GET /api/v1/redteam/targets?namespace=<ns> The agent classes seeded in a namespace, for target selection.
GET /api/v1/redteam/results/latest?namespace=<ns> The most recent durable run + efficacy roll-up ({"has_run": false} when none exist). namespace=all skips the filter.
GET /api/v1/redteam/results?namespace=<ns>&limit=<n>&offset=<n> Paginated run summaries, newest first. Page size is bounded by config.retention.redteamSummaryKeepRuns.
GET /api/v1/redteam/results/{run_id} One run’s full result set.
GET /api/v1/redteam/report/{run_id} In-process report cache, kept for backwards compatibility. Durable reads should use results/{run_id}.
Terminal window
# run the suite against a namespace's seeded classes
curl -s -X POST "$NRVQ_API_URL/api/v1/redteam/suite?target_namespace=chatbot-prod" \
-H "Authorization: Bearer $NRVQ_API_TOKEN"
# fetch the latest run + efficacy roll-up
curl -s "$NRVQ_API_URL/api/v1/redteam/results/latest?namespace=chatbot-prod" \
-H "Authorization: Bearer $NRVQ_API_TOKEN"

POST /redteam/suite and POST /redteam/run are rate-limited to 15 requests per 60-second window by default — starting a suite fans out to every agent class x every attack in the catalog, a genuine DoS surface. The read endpoints (catalog, targets, results*, report) use the API’s general default limit (300/60s), so polling the console’s Red Team or Overview pages won’t trip the tighter ceiling.

Terminal window
norviq fleet status # single-cluster or enrolled in a fleet?
norviq fleet join <hub-minted-token> # enroll this cluster into a fleet
norviq fleet leave # leave + shed pushed policy

join and leave require admin; status is readable by any authenticated principal. leave reports how many hub-pushed policies it shed.

See Deployment → Multi-cluster fleet.

Terminal window
norviq config show # active api-url / token (masked to the last 4) / output
norviq config set output json