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.
Install
Section titled “Install”The CLI ships in the norviq Python package on PyPI. Python 3.11 or newer.
pip install norviqnorviq --helpPin it to the platform you are talking to — the same package carries the SDK, and the API and CLI are released together:
pip install norviq==0.2.5The framework extras (norviq[langchain], norviq[crewai], …) are for the in-process SDK, not the
CLI. The CLI needs no extras.
Global options
Section titled “Global options”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 |
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 -o — status, 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.
cat > .env <<'EOF'NRVQ_API_URL=https://norviq.internalNRVQ_API_TOKEN=nrvq_...EOFnorviq policy list # no flags neededErrors and exit codes
Section titled “Errors and exit codes”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.
What each command needs
Section titled “What each command needs”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 |
First login
Section titled “First login”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.
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.
Recover a lost password
Section titled “Recover a lost password”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.
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.
Status
Section titled “Status”norviq statusAPI: OnlineRedis: ConnectedDB: ConnectedIt reads GET /healthz (liveness) and GET /readyz (dependency probe).
Policies
Section titled “Policies”norviq policy list # every policy you can readnorviq policy get <namespace> <agent-class> # one policy + its rego sourcenorviq policy create -f guard.rego -n prod -c chatbot --mode block # block | audit | escalatenorviq policy dry-run -f guard.rego -n prod -c chatbot # replay against recent traffic, no writenorviq policy versions <namespace> <agent-class> # version historynorviq policy rollback <namespace> <agent-class> 3 # restore version 3norviq policy apply <namespace> <agent-class> --target-ns prod # bind it to a target scopenorviq policy delete <namespace> <agent-class> # prompts for confirmationcreate 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: 1284Would block: 37Would allow: 1247Recommendation: 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.
policy apply
Section titled “policy apply”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.
norviq audit list -n prod -d block -t execute_sql --range 24h --limit 50norviq audit stats --range 7d -n prod # totals by decisionnorviq 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.
Agents & trust
Section titled “Agents & trust”norviq agent list # agents with trust score, category, violation countnorviq agent get <spiffe-id> # one agent's trust detailnorviq agent freeze <spiffe-id> # kill switch — sets the score to 0.0norviq agent reset-trust <spiffe-id> --score 1.0 # clear the freeze and the capfreeze 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:
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.
Red-team suite
Section titled “Red-team suite”Prove a policy actually blocks — run the built-in adversarial attacks against a target.
norviq redteam catalog # print the bundled catalog (no HTTP call)norviq redteam run --namespace prod --agent chatbot # run the suitenorviq redteam run --category prompt_injection # one categorynorviq redteam run --agent chatbot -o markdown # markdown reportnorviq 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:
norviq --api-url https://norviq.internal --token "$NRVQ_API_TOKEN" redteam run --namespace prodnorviq redteam run --api-url https://norviq.internal --token "$NRVQ_API_TOKEN" --namespace prodThe per-command flag wins when both are given. NRVQ_API_URL / NRVQ_API_TOKEN feed the global options
and therefore reach both paths.
Red-team HTTP API
Section titled “Red-team HTTP API”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}. |
# run the suite against a namespace's seeded classescurl -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-upcurl -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.
Fleet (multi-cluster, opt-in)
Section titled “Fleet (multi-cluster, opt-in)”norviq fleet status # single-cluster or enrolled in a fleet?norviq fleet join <hub-minted-token> # enroll this cluster into a fleetnorviq fleet leave # leave + shed pushed policyjoin 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.
CLI settings
Section titled “CLI settings”norviq config show # active api-url / token (masked to the last 4) / outputnorviq config set output json