Skip to content

The three verbs

Everything Veridor does is one of three questions, asked in this order.

Verb The question What you get
map What don't I cover, and what would a compromise reach? An access matrix, a blast radius per identity, boundary coverage, ranked anomaly candidates, and a diff against an earlier matrix
run Does the boundary hold, and did it change? Findings, evidence grades, a baseline diff, SARIF, and the CI exit code
mutate Would my suite actually catch a regression? A mutation score and every mutant your checks failed to kill

Discover, declare, gate, then verify the gate. map proposes; you decide what to declare; run holds you to it; mutate tells you whether holding you to it means anything.

Only run gates on what it discovered

Nothing map finds moves its exit code. An inconsistency it noticed is a hypothesis, not a finding, and discovery must not fail somebody's build. map still exits 3 when its grid measured nothing, or, with --require-complete, when part of it went unmeasured; see exit codes. A candidate produces no SARIF result, never enters .veridor/runs or latest.json, and never moves an exit code.

There is one opt-in gate, and it is not about a finding at all. --fail-on-drift fails when this run's matrix differs from a baseline you recorded earlier. That is a fact about two runs rather than a judgement about one.

mutate has an exit code of its own, and it is never a run's. A surviving mutant says your declaration is weak. It says nothing about your target.

Verb 0 1 2 3
map it produced a map you asked --fail-on-drift and the matrix moved refused, or the inputs would not load nothing in the grid was measured; or --require-complete and part of it was not; or, under a drift gate, no cell was measured in both runs
run nothing the gate fails on was found the gate failed configuration, specification, path-safety or undefined-coverage error the gate passed but something else did not
mutate every mutant was killed at least one survived nothing was measured, or a mutant was unresolved --require-evaluable and a mutant could not be evaluated

The full breakdown, including 70, 130 and 143, which any command can end with, is in exit codes.

What each verb needs

run, mutate, capsule and verify take a hand-written veridor.json and nothing else. That is the entire gate: detection, evidence grades, the regression diff, SARIF, and a portable proof.

map, init and coverage additionally require an OpenAPI 3 or Swagger 2 JSON specification, because they read a route inventory and Veridor never discovers routes on its own. If you have no specification, a route-list stub with empty operation objects is enough:

{"openapi": "3.0.0", "info": {"title": "mine", "version": "1"},
 "paths": {"/api/invoices": {"get": {}}, "/api/invoices/{id}": {"get": {}},
           "/api/admin/audit": {"get": {}}}}

Note

Specifications must be JSON. Veridor has no YAML parser by design: a hand-written one could silently drop routes, and a dropped route inflates the coverage number. Convert once with npx @redocly/cli bundle openapi.yaml -o openapi.json.

What each verb writes to your target

Verb Methods sent by default Rate Plan refused above
map GET and HEAD only 2 req/s, sequential, capped at 10 --max-requests, default 500, absolute ceiling 20,000
run whatever your configuration declares unthrottled —
mutate whatever your configuration declares, re-issued --min-interval, default 0.5s --max-requests, default 5,000

run is unthrottled because it issues the requests you wrote, once each, plus its own marker probes and liveness controls. map fans a specification out across every identity, which is a scanning-shaped workload and is treated as one — see safety posture. --read-only refuses, before any request, any configuration that can issue a method outside GET, HEAD and OPTIONS; it is honoured by run and by mutate at both tiers.

The utilities

These are commands you reach for while using the three verbs, not headline commands. veridor --help shows the same split.

Command Does
init writes a starter configuration from a specification. Every check it writes is a guess, not a measurement
coverage measures boundary coverage alone, issuing no request
new-marker prints a marker that passes validation
report renders an existing run JSON as SARIF, JUnit, GitLab SAST, Code Quality or Markdown, or a drift JSON as Markdown, without repeating the probes
capsule writes a portable, runnable proof of one finding, carrying no credential its rule recognises
serve opens a local viewer over .veridor/, on 127.0.0.1:8787

verify is an alias for run, and additionally takes a capsule file, which it treats as untrusted input (safety posture).

Where the verbs belong

run is the verb that belongs in CI. mutate is something you do deliberately at a desk, because its output is written for a human. map is too — except for drift, which is built for CI and is the one way to get a gate without declaring anything.

One CI step is one verb. If you want both a declared gate and drift, use two steps: they exit non-zero for different reasons, and a shared exit code would hide which one failed. See the GitHub Action.

What none of them establish

Read claims and limits before you read a report. In short:

  • Boundary coverage says a cross-identity denial is asserted. It never says the boundary is enforced.
  • A candidate is an inconsistency between two observations — a hypothesis to declare and test. It is never a finding.
  • Blast radius counts operations an identity was observed to reach, as a lower bound. It never counts objects.
  • A mutation score says perturbing your declaration changes the verdict. It says nothing about operations your suite does not cover.
  • A finding says observed behaviour differed from your declaration. It does not say the declaration was right.

Your configuration is the policy. A wrong declaration and a real vulnerability produce the same finding, and Veridor cannot tell them apart.