Skip to content

Command-line reference

Every command's --help, exactly as veridor 0.1.0 prints it at commit d215e1a. This page is generated from the tool itself, so a flag listed here is a flag the code accepts. For what each command is for and how to use it, start with the guides linked in each section.

verify is an alias for run, and also accepts a capsule file (see capsule and verify).

veridor

usage: veridor [-h] [--version] <command> ...

Check that your API still agrees with the authorization you declared.

THE THREE VERBS

  map      What don't I cover, and what would a compromise reach?
           Projects an OpenAPI inventory into an access matrix, a blast radius
           per identity, boundary coverage, and ranked anomaly candidates.
           Read-only by default, rate limited, and it refuses its own plan
           before the first request. Candidates are hypotheses, never findings.

  run      Does the boundary hold, and did it change?
           Issues every declared expectation as every declared identity, grades
           marker evidence, runs multi-step sequences, diffs against a baseline
           and sets the CI exit code. This is the gate.

  mutate   Would my suite actually catch a regression?
           Rewrites your own expectations and reports the mutants your checks
           failed to kill. A surviving mutant is a check that cannot fail.

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

UTILITIES

  capsule      write a portable, runnable proof of ONE finding, to attach to
               a disclosure. Carries no credential; the reader verifies it
               against their own instance with `veridor verify <file>`
  init         write a starter configuration from an OpenAPI specification
  coverage     measure boundary coverage alone, issuing no request
  new-marker   print a fresh identity marker
  report       render a run as SARIF, JUnit, GitLab SAST or Code Quality,
               or Markdown
  serve        open the local run history UI

`verify` remains an alias for `run`, and additionally takes a capsule:
`veridor verify proof.json` prints CONFIRMED / NOT_REPRODUCED / INCONCLUSIVE.

options:
  -h, --help  show this help message and exit
  --version   show program's version number and exit

commands:
  <command>   one of the verbs or utilities above; each takes its own --help

map

Guide: map and drift.

usage: veridor map [-h] --config CONFIG [--output-dir OUTPUT_DIR]
                   [--base-url BASE_URL] [--wait-for-target SECONDS] [--matrix MATRIX]
                   [--blast-radius BLAST_RADIUS] [--candidates CANDIDATES]
                   [--promote PROMOTE] [--require-complete] [--dry-run]
                   [--max-requests MAX_REQUESTS] [--rate REQUESTS_PER_SECOND]
                   [--max-run-seconds MAX_RUN_SECONDS]
                   [--on-retry-after {continue,stop}] [--timeout MATRIX_TIMEOUT]
                   [--include [METHOD ]/path/template]
                   [--ignore [METHOD ]/path/template] [--allow-mutating-method METHOD]
                   [--acknowledge-mutating TEXT] [--acknowledge-target TEXT]
                   [--baseline BASELINE] [--drift DRIFT_PATH]
                   [--fail-on-drift {none,opened,any}] --openapi OPENAPI
                   [--coverage COVERAGE_PATH] [--badge BADGE] [--base-path BASE_PATH]
                   [--allow-no-matches]

Project an OpenAPI inventory into an access matrix, then report every identity's
observed reach, the boundary coverage of your declaration, and the inconsistencies
worth declaring a check for. Nothing here is a finding: `map` produces no SARIF,
writes nothing into the run history, and nothing it discovers can fail a build. It
exits 0 when it produced a map -- unless you asked --fail-on-drift to gate on how this
matrix differs from an earlier one, which is a fact about two runs rather than a
verdict on one.

options:
  -h, --help            show this help message and exit
  --config CONFIG       path to veridor JSON config
  --output-dir OUTPUT_DIR
                        artifact directory (default: .veridor)
  --base-url BASE_URL   override target.base_url
  --wait-for-target SECONDS
                        before the first request, try a plain TCP connection to the
                        target's host and port once a second until one is accepted or
                        SECONDS pass (default: 0, no wait). Either way the run then
                        goes ahead and one stderr line says which it was; a target
                        that never accepted shows up as the probes' own transport
                        errors. For a target that is still starting; an accepted
                        connection does not mean the new version is serving (a
                        previous version's pod accepts at once). The check connects
                        directly even when a proxy carries the requests, and its line
                        then says so. Never changes a verdict
  --matrix MATRIX       also write the access matrix JSON here
  --blast-radius BLAST_RADIUS
                        also write the blast radius JSON here
  --candidates CANDIDATES
                        also write the candidates JSON here
  --promote PROMOTE     write a paste-ready, deliberately incomplete check per
                        candidate to this path. They are stubs: what the boundary IS
                        is your decision

safety envelope (read-only by default):
  --require-complete    additionally exit 3 when any probed cell went unmeasured -- a
                        rate limit, a 5xx, a timeout. Those operations are unverified
                        rather than verified. A grid where NOTHING was measured --
                        every probed cell unmeasured or a redirect, which Veridor does
                        not follow -- exits 3 with or without this flag. An --ignore,
                        matrix.ignore or --include that leaves nothing to probe -- no
                        operation, or only writes the read-only posture skips and
                        routes with no specimen -- is refused before the first request
                        (exit 2), so it never produces that empty grid
  --dry-run             print the plan and the safety posture, then stop. Nothing is
                        sent. Run this first against any target you did not provision
                        yourself
  --max-requests MAX_REQUESTS
                        refuse to start when the plan is larger than this (default:
                        500). The refusal happens before the first request, and
                        reports the number it refused
  --rate REQUESTS_PER_SECOND
                        requests per second, sequential (default: 2)
  --max-run-seconds MAX_RUN_SECONDS
                        wall clock the whole matrix may take before it stops and
                        reports a partial grid (default: 1800)
  --on-retry-after {continue,stop}
                        what a 429 carrying Retry-After means. continue (default)
                        counts it toward the ordinary rate-limit budget -- three
                        consecutive or five cumulative -- so a target that sends the
                        header gets the same tolerance as one that does not. stop
                        abandons the grid on the first one, which was the only
                        behaviour and cost a spec-compliant limiter 3.5x the coverage
                        of a rude one
  --timeout MATRIX_TIMEOUT
                        per-request timeout (default: target.timeout_seconds)
  --include [METHOD ]/path/template
                        probe only matching operations; repeatable. Matches by PATH
                        PREFIX, not as a glob or a regex: a match selects the route
                        and everything beneath it, '*' is an ordinary character, and
                        '{id}' must be spelled as the specification spells it
  --ignore [METHOD ]/path/template
                        never probe matching operations; repeatable, adds to
                        matrix.ignore
  --allow-mutating-method METHOD
                        name one mutating verb the matrix may send, e.g. DELETE.
                        Repeatable, never a wildcard, and inert without --acknowledge-
                        mutating
  --acknowledge-mutating TEXT
                        the exact acknowledgement string mutating verbs require. Two
                        independent gates, so one stray flag cannot open both
  --acknowledge-target TEXT
                        required verbatim when the target is not loopback or a private
                        address: 'I am authorized to test HOST'. The refusal prints
                        the exact string. This is friction, not a security boundary

drift -- authorization regression with nothing declared:
  --baseline BASELINE   a previous matrix JSON, or the directory `map` writes them to
                        (e.g. .veridor/matrix). Reports what moved since. Nobody
                        declares anything: the first run records, later runs diff. A
                        directory is for discovery only -- --fail-on-drift refuses it,
                        because this run writes into the same directory
  --drift DRIFT_PATH    write the machine-readable drift report JSON here. Needs
                        --baseline; without one it is refused, before anything is sent
  --fail-on-drift {none,opened,any}
                        exit non-zero on drift (default: none). `opened` fails only on
                        changes that mean MORE access than the baseline -- a refusal
                        that became a success, or a response that now carries another
                        identity's marker. A redirect in the baseline that is served
                        now is not a refusal Veridor saw, so `opened` reports it and
                        `any` fails on it. `any` fails on any change. Either policy
                        exits 3 instead of 0 when not one cell was measured in both
                        this run and the baseline (a baseline recorded while the
                        target was down, say): no change could have been reported, so
                        that is not a pass. Discovery does not gate a build unless you
                        ask it to

boundary coverage (offline, static):
  --openapi OPENAPI     OpenAPI 3 or Swagger 2 JSON specification
  --coverage COVERAGE_PATH
                        write the machine-readable coverage report JSON to this path
  --badge BADGE         write shields.io endpoint JSON for the coverage badge to this
                        path
  --base-path BASE_PATH
                        spec path prefix to apply (default: auto-detect and show the
                        scoring)
  --allow-no-matches    report coverage as 'not measured' instead of failing when no
                        check path matches any operation

run

Guide: run and Write your access policy.

usage: veridor run [-h] [--config CONFIG] [--output-dir OUTPUT_DIR]
                   [--baseline BASELINE] [--base-url BASE_URL]
                   [--wait-for-target SECONDS] [--json JSON_PATH] [--sarif SARIF]
                   [--fail-on {new+confirmed,new,any,confirmed,none}] [--read-only]
                   [--require-clean-teardown] [--no-prove-liveness]
                   [--require-complete] [--report FORMAT=PATH] [--openapi OPENAPI]
                   [--coverage COVERAGE_PATH] [--badge BADGE] [--base-path BASE_PATH]
                   [--allow-no-matches]
                   [capsule]

Issue every declared expectation as every declared identity, grade the marker
evidence, execute any declared multi-step sequences, diff against a baseline, and set
the exit code. Sequences, their captures and identity sessions are declared in the
configuration (version 2) and run as part of this command.

positional arguments:
  capsule               a capsule written by `veridor capsule`. Runs the single check
                        it carries and prints CONFIRMED / NOT_REPRODUCED /
                        INCONCLUSIVE. This is what a maintainer receives attached to a
                        disclosure

options:
  -h, --help            show this help message and exit
  --config CONFIG       path to veridor JSON config
  --output-dir OUTPUT_DIR
                        run history directory (default: .veridor)
  --baseline BASELINE   prior run JSON for regression comparison
  --base-url BASE_URL   override target.base_url
  --wait-for-target SECONDS
                        before the first request, try a plain TCP connection to the
                        target's host and port once a second until one is accepted or
                        SECONDS pass (default: 0, no wait). Either way the run then
                        goes ahead and one stderr line says which it was; a target
                        that never accepted shows up as the probes' own transport
                        errors. For a target that is still starting; an accepted
                        connection does not mean the new version is serving (a
                        previous version's pod accepts at once). The check connects
                        directly even when a proxy carries the requests, and its line
                        then says so. Never changes a verdict
  --json JSON_PATH      write run JSON to this path
  --sarif SARIF         write SARIF 2.1.0 to this path
  --fail-on {new+confirmed,new,any,confirmed,none}
                        policy used for the process exit code (default:
                        new+confirmed). new+confirmed: anything absent from the
                        baseline, PLUS anything proved by a marker regardless of age
                        -- the default because `new` alone is decided entirely by a
                        file the README asks you to commit, so a pull request that
                        adds a finding id to the baseline turns a live confirmed leak
                        into a green build. new: a finding absent from the baseline.
                        any: any finding at all. confirmed: only a finding whose
                        response actually carried another identity's marker. none:
                        never fail
  --read-only           refuse to load a configuration that can issue any request
                        outside GET, HEAD, or OPTIONS. Fails with exit 2 before a
                        single request is sent. Use this for any target you did not
                        personally provision
  --require-clean-teardown
                        additionally exit 3 when a sequence teardown did not complete
                        cleanly. A real finding always wins the exit code
  --no-prove-liveness   skip the liveness controls. ON by default: for each GET, HEAD
                        or OPTIONS request whose declaration accepts only denial
                        statuses, Veridor issues ONE probe carrying a credential it
                        fabricated, written into every header the identities declare
                        but a standard non-credential one such as Accept (a header two
                        or more identities send with one value and whose name is not a
                        credential's, such as an API version, is sent unchanged, and
                        one declared only beside another credential, such as a tenant,
                        is left out), so a denial that any caller would have received
                        cannot pass as a boundary that refused this one; and the same
                        for a GET, HEAD or OPTIONS request that serves an identity
                        whose credential must be proven live, so a route that serves
                        anyone proves nothing. One extra request per such request --
                        not per identity -- and the fabricated value authenticates to
                        nothing; none is sent with POST, PUT, PATCH or DELETE, inside
                        a sequence, or when no identity declares a credential header.
                        A denial counts as unproven only for an identity that presents
                        a credential, in a check or sequence where some identity was
                        served, that nothing else in the run proved live (a 2xx it
                        drew on a request where an identity with no credential, or the
                        fabricated credential, was refused; or its marker_probe), and
                        whose fabricated credential drew the same denial, was not
                        sent, failed, or drew neither a refusal nor a success. Each is
                        printed and recorded in liveness.unproven; it moves the exit
                        code (to 3) only with --require-complete, so under the default
                        flags an expired token on an identity expected only to be
                        denied still exits 0. Turn it off and that denial is not even
                        reported
  --require-complete    additionally exit 3 when any probe went unanswered -- a rate
                        limit, a 5xx, a timeout -- when an expectation accepts both
                        access and denial, or when a denial counts as unproven in
                        liveness.unproven (see --no-prove-liveness). Those boundaries
                        are unverified rather than verified, and a CI job reads an
                        exit code, not a summary. A real finding always wins the exit
                        code
  --report FORMAT=PATH  also write this run as FORMAT to PATH; repeatable. FORMAT is
                        one of sarif, junit, gitlab-sast, codequality, markdown.
                        Written whatever the gate decides, since a failing gate is
                        when CI needs them. PATH may not be '-': `run` prints its own
                        summary on stdout

boundary coverage (offline, static):
  --openapi OPENAPI     OpenAPI 3 or Swagger 2 JSON specification; enables boundary
                        coverage
  --coverage COVERAGE_PATH
                        write the machine-readable coverage report JSON to this path
  --badge BADGE         write shields.io endpoint JSON for the coverage badge to this
                        path
  --base-path BASE_PATH
                        spec path prefix to apply (default: auto-detect and show the
                        scoring)
  --allow-no-matches    report coverage as 'not measured' instead of failing when no
                        check path matches any operation

mutate

Guide: mutate.

usage: veridor mutate [-h] --config CONFIG [--output-dir OUTPUT_DIR]
                      [--require-evaluable] [--base-url BASE_URL] [--json JSON_PATH]
                      [--text TEXT_PATH] [--badge BADGE] [--read-only]
                      [--max-requests MAX_REQUESTS] [--min-interval MIN_INTERVAL]
                      [--request-budget REQUEST_BUDGET] [--tier {1,2}] [--hooks HOOKS]
                      [--i-understand-tier-2-runs-my-commands]

Rewrite your declared expectations one at a time and report which mutants your suite
failed to kill. A surviving mutant is a check that cannot fail, which is a vacuous
check. This measures your declaration, not your target: nothing it reports is a
finding, it emits no SARIF, and its exit code is its own (0 all killed, 1 at least one
survivor, 2 nothing measured or something unresolved). Unlike `map`, this verb does
NOT require --acknowledge-target for a public host: it re-issues the requests YOU
declared in a configuration you wrote naming a base_url, which is what `veridor run`
does, so it takes `run`'s posture there rather than `map`'s. `map` fans out from a
specification across every identity and is gated for that reason. --read-only and
--max-requests apply here.

options:
  -h, --help            show this help message and exit
  --config CONFIG       path to veridor JSON config
  --output-dir OUTPUT_DIR
                        artifact directory; reports land under <dir>/mutation
                        (default: .veridor)
  --require-evaluable   additionally exit 3 when a mutant could not be evaluated at
                        all. Those sit under LIMITS ON THE EVIDENCE and are kept out
                        of the score, which is right -- an unanswerable mutant is not
                        a measurement -- but it leaves a suite whose only cross-tenant
                        assertion cannot fail reporting 1.00 and exiting 0
  --base-url BASE_URL   override target.base_url
  --json JSON_PATH      also write the report JSON here
  --text TEXT_PATH      also write the rendered report here
  --badge BADGE         also write shields.io endpoint JSON here
  --read-only           refuse to load a configuration that can issue any request
                        outside GET, HEAD, or OPTIONS. Fails with exit 2 before a
                        single request is sent, at either tier. `mutate` re-issues
                        every request you declared, DELETE included, and then issues
                        more of its own: this is the same escape hatch `run --read-
                        only` gives you, on the same configuration
  --max-requests MAX_REQUESTS
                        refuse to start when the plan -- baseline probes included --
                        is larger than this (default: 5,000). The refusal happens
                        before the first request and reports the number it refused.
                        --min-interval bounds the rate this target is hit at; this
                        bounds the volume, and unlike the two flags below it applies
                        at BOTH tiers. At tier 2 the baseline is charged once per hook
                        plus once for the unmutated run, each with its liveness
                        controls (one per denial-only GET/HEAD/OPTIONS request).
                        Session logins and sequence steps are not counted at tier 2

tier 1 -- expectation mutation (the default), and its request envelope:
  --min-interval MIN_INTERVAL
                        minimum seconds between requests -- ALL of them, the baseline
                        probes included, because they are most of the traffic
                        (default: 0.5, i.e. 2 req/s, matching `map`). Pass 0 to turn
                        it off against a fixture you own
  --request-budget REQUEST_BUDGET
                        cap the ADDITIONAL target requests mutation may issue
                        (default: 500). The baseline probes are the ones `veridor run`
                        issues anyway, and are governed by --max-requests below rather
                        than by this flag

tier 2 -- target mutation (RUNS COMMANDS YOU SUPPLY):
  --tier {1,2}          mutation tier (default: 1)
  --hooks HOOKS         JSON file of target mutation hooks. EVERY HOOK'S apply_command
                        AND restore_command IS EXECUTED ON THIS MACHINE, as you, with
                        your environment. They are run as an argv list with no shell,
                        so nothing is expanded or interpreted -- but they are still
                        your commands and they still run. A hook without a
                        restore_command is refused before anything executes. Requires
                        --tier 2
  --i-understand-tier-2-runs-my-commands
                        required for --tier 2. Tier 2 alters your target and then puts
                        it back; without this flag nothing runs

init

Guide: init.

usage: veridor init [-h] [--for {run,drift}] --openapi OPENAPI --output OUTPUT
                    [--base-url BASE_URL] [--force]

Scaffold a configuration from an OpenAPI specification, for one of two paths. --for
run (default) writes one starter check per in-scope operation: every expectation is a
placeholder, not an observation, and the first run is red by design. --for drift
writes no checks at all -- two identities and one specimen per templated route --
because `map` grades no expectation and drift compares this run's matrix with an
earlier one. Edit before you trust.

options:
  -h, --help           show this help message and exit
  --for {run,drift}    which path to scaffold: 'run' declares expectations and gates
                       on them (default); 'drift' declares nothing and gates on what
                       moved
  --openapi OPENAPI    OpenAPI 3 or Swagger 2 JSON spec
  --output OUTPUT      configuration path to create
  --base-url BASE_URL  override the target base URL
  --force              replace an existing --output file

coverage

Guide: coverage.

usage: veridor coverage [-h] --config CONFIG --openapi OPENAPI
                        [--coverage COVERAGE_PATH] [--badge BADGE]
                        [--base-path BASE_PATH] [--allow-no-matches]

Measure how much of the OpenAPI inventory your declaration asserts an authorization
boundary for. Issues no request.

options:
  -h, --help            show this help message and exit
  --config CONFIG       path to veridor JSON config

boundary coverage (offline, static):
  --openapi OPENAPI     OpenAPI 3 or Swagger 2 JSON specification
  --coverage COVERAGE_PATH
                        write the machine-readable coverage report JSON to this path
  --badge BADGE         write shields.io endpoint JSON for the coverage badge to this
                        path
  --base-path BASE_PATH
                        spec path prefix to apply (default: auto-detect and show the
                        scoring)
  --allow-no-matches    report coverage as 'not measured' instead of failing when no
                        check path matches any operation

capsule

Guide: capsule and verify.

usage: veridor capsule [-h] --config CONFIG --check CHECK [--from-run FROM_RUN]
                       [--image IMAGE] [--output OUTPUT]

Write a portable, runnable proof of ONE finding: the request, the identities, the
declared expectation and the marker, in a single file you can attach to a disclosure.
The reader verifies it with `veridor verify <file>` against their own instance.
Credentials are never included -- a capsule that would carry one is refused, because a
published bearer token cannot be recalled.

options:
  -h, --help           show this help message and exit
  --config CONFIG      path to veridor JSON config
  --check CHECK        id of the check to prove
  --from-run FROM_RUN  a run JSON whose finding for this check becomes the CLAIM the
                       capsule carries, so the reader sees what was originally
                       observed
  --image IMAGE        container image the reporter used, ideally digest-pinned;
                       recorded verbatim
  --output OUTPUT      write here (default: stdout)

report

Guide: report, serve and new-marker.

usage: veridor report [-h] [--from-run RUN.json] [--from-drift DRIFT.json]
                      [--format {sarif,junit,gitlab-sast,codequality,markdown}]
                      [--output PATH] [--sarif SARIF]

Render a run JSON document as SARIF, JUnit XML, a GitLab SAST or Code Quality report,
or Markdown. Every format except SARIF reads the gate decision `veridor run` recorded
in the document, so a run that exited 3 because nothing was answered never renders as
a pass. SARIF carries findings only and has no gate: a run that exited 3 for an
unproven denial or an unclean teardown renders as zero results, so read the job's exit
code, not the SARIF, for the gate. A drift report (`map --baseline ... --drift PATH`)
renders to Markdown only: drift is never a finding.

options:
  -h, --help            show this help message and exit
  --from-run, --input RUN.json
                        the run JSON `veridor run --json` wrote (--input is the older
                        spelling)
  --from-drift DRIFT.json
                        a drift report written by `veridor map --baseline ... --drift`
  --format {sarif,junit,gitlab-sast,codequality,markdown}
                        the report to write
  --output PATH         where to write it; '-' or omitted writes to stdout
  --sarif SARIF         older spelling of --format sarif --output PATH

serve

Guide: report, serve and new-marker.

usage: veridor serve [-h] [--output-dir OUTPUT_DIR] [--host HOST]
                     [--i-understand-this-is-unauthenticated] [--port PORT]

Open the local, read-only run history UI.

options:
  -h, --help            show this help message and exit
  --output-dir OUTPUT_DIR
                        run history directory (default: .veridor)
  --host HOST           address to bind (default: 127.0.0.1). Anything other than
                        loopback publishes every run document -- targets, finding
                        messages, config paths -- to whoever can reach it, with no
                        authentication of any kind, and is refused without
                        --i-understand-this-is-unauthenticated
  --i-understand-this-is-unauthenticated
                        required to bind an address other than loopback. The viewer
                        has no login and never has: it is a local reader for artifacts
                        you already have on disk
  --port PORT

new-marker

Guide: report, serve and new-marker.

usage: veridor new-marker [-h]

Print a fresh, high-entropy identity marker.

options:
  -h, --help  show this help message and exit