Skip to main content
A curated tour of the most common vigolium invocations, grouped by command. For the full flag list of any command, run vigolium <command> --help. For the same examples in your terminal, run vigolium --full-example.

Top-Level Commands

Exit codes

Since v0.4.5 the exit status is a table, not two values:
4 is not a failure. The scan ran to completion, wrote its output, and found something at or above the threshold — the opposite outcome from 1, where it never got that far. Before v0.4.5 both returned 1, so a CI job could not tell “the scanner crashed” from “the scanner worked and found a critical”. Branch on them separately: treating every non-zero as breakage either ignores real outages or reports every finding as one.
--fail-on <info|suspect|low|medium|high|critical> works on scan, scan-url, scan-request, and run, and output is always written first — the gate only changes the exit code. Under -P/--parallel the gate is evaluated per child, and the parent batch fails only when every target fails. The global --soft-fail flag forces an exit code of 0 even when a command fails (the error is still printed to stderr), and overrides --fail-on. It keeps a failing vigolium call from interrupting the wrapping script or CI pipeline:

Global flags

These work on every command (run vigolium --help for the authoritative list):
Mistype a long flag and Vigolium suggests the nearest match instead of a bare “unknown flag” error — e.g. --module yields unknown flag: --module. Did you mean --modules? (run 'vigolium scan --help' for all flags). Added in v0.3.0. Since v0.4.7 a short name has to be within one edit of a candidate before it is offered, and a name with no near match points at the command’s help rather than inventing one — --url used to be answered with “did you mean --all?”.

Error codes under -j

A failed command under -j writes a parseable object to stdout and exits non-zero. Branch on error.code, never on the message:
A requested artifact that was never written fails the run (v0.5.0). An export failure — an unwritable -o, a query that failed mid-stream — used to exit 0, report scan.finished status=completed and leave no file: three success signals at once. scan, run, scan-url, scan-request and an agentic -S run now exit 1 with error.code: "export_failed".export_failed outranks gate_tripped. The gate’s whole premise is that the output was written before the code was chosen; when it wasn’t, exit 4 would send a CI job to read a file that does not exist. Each --format is attempted independently, so with --format jsonl,sqlite one of them may well be on disk — the Exports summary on stderr lists the ones that landed. The findings reached the database either way.
A wrong --db path errors as of v0.4.7; it no longer reads as empty. Opening a database used to create it, so a pure read against a typo’d or not-yet-scanned path returned {"total":0,"items":[]} with exit 0 — the same answer as a target nobody had scanned — and pointing --db at another tool’s SQLite file wrote Vigolium’s tables into it. Both now refuse, on finding, traffic, traffic body, traffic headers, db list, db stats, db export, log, log ls and export.replay and fuzz are exempt: they send traffic and record what they send, so bringing a store into being is a legitimate outcome for them. The built-in default database is also still created on first use.

Scanning

Run the full native pipeline against one or more targets.
The repeatable “opaque” flags — -H/--header, --auth, --auth-file, -t/--target, -T/--target-file, --spec-header, and --cookie — take each value verbatim: commas are literal, not separators. So a target URL with a query like ?ids=1,2,3, or a header/cookie whose value contains a comma, survives intact. To pass several values, repeat the flag (-t https://a -t https://b) rather than comma-joining them. Speed control — WAF-aware pacing. The per-host rate limiter now proactively pre-throttles a host the first time earlier-phase traffic fingerprints it behind a recognized CDN/WAF edge (CloudFront, Cloudflare, Akamai, Imperva/Incapsula, Sucuri, or Azure Front Door — detected from headers on ordinary 200s), dropping that host’s concurrency to MaxPerHost/4 and ramping back up on healthy responses. This keeps an aggressive active phase from bursting the edge into a rate-based block that would hide findings. A one-time-per-host [waf-pacing-armed] notice prints the drop (e.g. 40→10) to stderr and the session log. --no-waf-pacing disables this proactive pacing; the reactive back-off after a confirmed WAF block still applies. It’s a Speed Control flag, available on scan / scan-url / scan-request / run / ingest. Discovery — alternate-port sweep. --port-sweep-ports overrides the alternate HTTP(S) ports swept on the CLI target hosts (comma-separated). The sweep runs when --intensity deep is set or --follow-subdomains is on, so a host reachable on 8080/8443 (or any port you list) is discovered and scanned alongside the standard 80/443.

Pace: per-phase rate limits

The three speed dials — -c/--concurrency, --rate-limit, --max-per-host — each take an optional phase qualifier (v0.4.5), repeatable and mixable with the bare form:
Qualifiers accept the same phase aliases --only/--skip do (kis, cve, deparos, dast, …). Resolution per phase is phase-scoped → global → strategy/config default. A qualifier naming a phase the run doesn’t execute warns loudly rather than failing — a cap you believe is in force and isn’t is worse than an error.
--rate-limit now applies at its documented default when you don’t type it. It used to be enforced only when explicitly passed, so silence meant unlimited while the help text advertised 100 — the fail-open direction on a safety knob. No cap now requires asking for it with --rate-limit 0; a negative value is a usage error (exit 2), not “unlimited”.
Under --events ndjson, scan.started reports the pace that actually applied plus a phase_pace table for any phase that differs, so you can assert what was in force rather than inferring it from the flags you passed.

Machine event stream

--events ndjson on scan / run / scan-url / scan-request writes one JSON object per line to stdout while the scan runs. The human console keeps stderr byte-for-byte unchanged, so 2>/dev/null yields clean NDJSON with zero non-JSON lines, and an interactive operator sees no difference.
Four contracts you can build on:
  • Every line carries scan_uuid, stamped centrally so no call site can forget it. A sweep is several vigolium scan invocations; this is how you attribute an event to one.
  • v is the event-schema version. Gate on it.
  • scan.finished is always last, including status:"interrupted" on SIGINT/SIGTERM.
  • Its absence means the process was killed outright (SIGKILL can’t be caught). Treat a stream that stops without a terminal event as a hard kill, not a completed scan.
This replaces scraping vigolium log for [waf-block-detected] / [waf-pacing-armed] markers — every notice that reaches the log also reaches the stream. The WAF notifiers are deliberately not gated on --silent: that flag asks for a quiet terminal, and a driver passing --silent --events wants exactly that.
--events is refused under -P/--parallel. Each child’s stdout is captured to its own per-target console log, so the parent’s stdout — the one you’re reading — would receive nothing, and the NDJSON would contaminate a file meant to be a human transcript.

Less common scan flags

Available on scan / run (and, where relevant, scan-url / scan-request):

Parallel & isolated scans

Scan many targets at once, or let several parallel scans share one database without write contention.
  • -P, --parallel N — scan up to N targets concurrently as isolated child processes. Requires one of two isolation shapes, never a mix: -S -T --split-by-host (per-host output files, nothing persisted) or --db-isolate -T (every target merges into one shared --db). Real in-flight requests ≈ N × --concurrency.
  • --db-isolate — scan into a private temporary SQLite DB and merge results into --db at the end (SQLite only). Under --stateless it is ignored with a warning since -S keeps no database to merge from; before v0.4.8 the pair aborted the run.
  • --split-by-host — in stateless multi-target mode, write a separate base-<host>.<ext> output file per target.
  • --resume — resume a prior -S -T --split-by-host -P run from its <output>.progress.json manifest, scanning only the targets that didn’t finish. Run bare (no other flags) to auto-discover the manifest in the current directory and relaunch the saved run.

Running a Single Phase

vigolium run <phase> is an alias for scan --only <phase>, useful when you want one specific stage of the pipeline.
Phase names accept generous aliases - httpx/alive/sweep reach the probe, crawl reaches spidering, cve/kis reach the known-issue scan. vigolium strategy --json prints the authoritative set as phases[].canonical / phases[].aliases; see Phase Aliases.

run probe - the host sweep

vigolium run probe is the native prober: one request per target, passive technology fingerprinting and attack-surface scoring, no content discovery and no fuzzing. Built for host lists in the thousands, where run discover would spend its whole budget on the first handful.
A probe-only run flips its own defaults (run probe, scan --only probe): redirect hops recorded, --redirect-mode same-apex, the many-hosts connection profile, and proactive WAF pacing off - a sweep sends one request per host, so there is no burst to pre-empt and the pre-arm would print a pacing notice per host. Reactive back-off after a confirmed block still applies. Its JSONL envelope also narrows to http_record objects; --export-only http,findings restores the findings. Each default yields to an explicit flag, and none of them apply when the probe rides along inside a wider scan.--tls-probe and --record-redirect-chain on a run with no probe phase warn that they are inert instead of silently doing nothing.
Probe rows are labelled source: probe, so a sweep is separable from a real scan in the same project. Rank a finished sweep with vigolium traffic --source probe --sort surface_score. Full details on the Probe phase page.

Input Modes

Feed traffic into a scan from OpenAPI, Burp, curl, HAR, or stdin.
Run vigolium --list-input-mode to see every supported input format with examples. Accepted formats are urls, openapi/swagger, postman, curl, burpraw/raw, burpxml/burp, burpscope/burp-scope, har, nuclei, and deparos. An unknown -I/--input-mode value is rejected up front with a clear error (it previously fell through to the Nuclei parser silently, so a typo could yield zero or partial records with no warning).
-i/--input vs -T/--target-file is not a free choice. -T reads its file as one target URL per line, so a spec or an export must go through -i — pointing -T at a YAML spec makes every line of that file a target (and under -S fans out one child scan per line). The two exceptions are the formats that genuinely are target lists: urls and burpscope.
-T actually seeds the scan since v0.4.6. It used to populate the input source but never Options.Targets, so vigolium run spidering -T hosts.txt finished in 0s printing a truthful-looking Targets: N banner and crawling nothing. Piped URL lists (including an explicit -I urls) promote the same way now, and an unparseable line is skipped rather than aborting the whole scan on the first junk URL in a recon export.

Schemeless targets

A target without a scheme — example.com, example.com:8443, or a -T file of bare hostnames straight out of a subdomain tool — is normalized to http:// before anything else runs:
http rather than https keeps the normalized form byte-identical to the request the HTTP path already built for a bare host; a host that only answers on 443 redirects there on the first hop, which --redirect-mode follows. Normalization runs before the target dedup, so example.com and http://example.com are one target, not two, and the normalized spelling is what the banner prints. A target that already names a scheme is left untouched, whatever that scheme is.
Before v0.4.8 a bare hostname silently skipped spidering. Probe, discovery and known-issue-scan tolerated it, but the browser phase parses its seed with stricter rules — example.com reads as a path with no host, and example.com:8443 fails harder still on the colon. Spidering therefore hard-failed per target on the exact host lists every other phase scanned, and because a failed crawl is reported as a completed one with 0 records, scan --only spidering --soft-fail exited 0 with an empty export. If you have a pipeline that quietly produced no crawl results from a hostname list, this was why.
The gau tool shim is the one exception: it defaults a bare domain to https://, because gau’s own callers write bare domains for sites they reach over TLS.

burpscope — scan a bug-bounty scope file

burpscope parses a Burp Suite project-config scope export — the {"target":{"scope":{"include":[…],"exclude":[…]}}} JSON a bug-bounty program hands out, not the proprietary binary .burp project file — and expands its include rules into seed URLs:
It is content-sniffed on both the -T and -i paths, so it works without -I; an explicit -I always wins. Three rules govern the expansion:
  • A wildcard host is never guessed at. ^.*\.example\.com$ names a set, not a server — synthesizing the apex would send traffic at a host the scope never listed. Those are reported separately rather than scanned.
  • An http:// candidate collapses into its https:// twin when the same host and path are in scope over both (Burp writes both for every host, and the http one is a redirect in practice). An explicit non-default port survives as its own distinct service.
  • Exclude rules are applied to the derived targets before anything is emitted.
A scope file that yields zero targets is a hard error, not an empty run.

Ingestion

Push HTTP traffic into the database without running a scan, useful for building a project corpus before scanning, or for sending traffic to a remote server.

Batch ingest — N sources, one process

Since v0.4.5 -i is repeatable and --dir walks a directory, so a folder of captures costs one process instead of N:
A failing source does not discard its siblings — one bad HAR in fifty must not cost the other forty-nine — but the command still exits non-zero and names how many failed. An empty --dir match is a hard error: “ingested 0 records” for a typo’d path is indistinguishable from an empty capture.
--dir is deliberately non-recursive. Walking subdirectories would silently widen what you pointed at, and record ids are assigned in ingest order.Concurrent vigolium ingest processes against one SQLite file are safe anyway (every open sets busy_timeout, WAL, and an immediate write lock, so writers serialize rather than failing with SQLITE_BUSY). Batch mode is still the right shape — it pays one process start and one schema check, not N.
-S on ingest and server is a deprecated alias for --scan-on-receive and warns. Everywhere else in the CLI -S means --stateless. Use the long --scan-on-receive spelling on these two commands.

Server

Start the REST API and ingest proxy.
See Transparent Proxy for the full MITM workflow. Narrowing the exposed surface. --view-only is the strictest: the server answers reads and nothing else — scanning, ingestion, agent runs, and every write endpoint are refused. --no-agent keeps the scanner and ingestion but removes all /api/agent/* routes plus agent subprocess warm-session pooling (--disable-warm-session turns off only the pooling, keeping the endpoints). --no-swagger drops the Swagger UI and the spec route. --demo-only exposes just a read allowlist — GET /api/findings[/:id], /api/http-records[/:uuid], /api/modules, /api/stats, /api/extensions[/:name|/docs] — for a public demo instance. --alternative-ingest-key (repeatable) adds extra API keys accepted only on the ingestion endpoints, so a collector can push traffic without holding the main key.
--catchup-threads and --disable-catchup are deprecated no-ops kept for compatibility — catch-up scanning is disabled. They still appear in --help and in older examples.
--burp-bridge-url <url> (alias --caido-bridge-url) points the server at a running Burp Suite’s or Caido’s loopback live bridge (default listener http://127.0.0.1:9009, exposed by the burp-vigolium extension or the caido-vigolium plugin) and merges its live proxy rows into GET /api/http-records — labelled source: burp or source: caido according to which listener answered — for the UI and API. The flag also reads the VIGOLIUM_BURP_BRIDGE_URL env var as a fallback. See Using Vigolium with Burp Suite or with Caido for bridge setup. --passive-only (with --scan-on-receive) restricts scanning to passive modules only — no active scan traffic is sent, and secret detection is included. It’s the safest way to analyze forwarded Burp/proxy traffic in place. Combining it with --full-native-scan-on-receive still crawls (discovery + spidering send requests); for zero active traffic, use --scan-on-receive without the full-native flag.

Database & Results

Browse, export, and prune scan data.
finding/traffic accept -S/--stateless + --db <file> to read a --format jsonl export or a standalone .sqlite directly, and --markdown to print the matched items as Markdown (under -S, add --compact to window long responses around the match). Severity filters. --severity (alias --sev on finding) takes an explicit comma-separated set, accepting single-letter shorthands (h,c) and any unambiguous prefix (crit, me, info). It differs from --min-severity, which is a floor--min-severity high expands to high,critical. When both are given, --severity wins. db ls carries --severity too (findings tables). Record-kind & module-type filters. --record-kind (on finding and db list) filters by record kind — finding, candidate, or observation (comma-separated; default finding). --module-type (same commands) filters by the producing module typeactive, passive, nuclei, agent, source-tools, oast, or extension (comma-separated). Exclude filters. --exclude-search / --exclude-header / --exclude-body (on finding and traffic) are the inverse of --search / --header / --body: they drop any row where the term appears. --exclude-search is repeatable, and a row is dropped if any term matches (contrast --search, whose repeated terms AND-narrow the kept set).

What each search flag actually searches

Since v0.4.7 the three text filters cover three different regions, and the difference is load-bearing: The region split happens in SQL, so total and pagination agree with the rows you get back rather than with a wider match set. The same applies to --exclude-header / --exclude-body.
Search terms are matched literally. % and _ used to reach SQLite’s LIKE unescaped and be interpreted as wildcards, so --body '%' returned every record and --body 'api_key' also matched api-key and api.key. Every substring filter now escapes the term and carries an ESCAPE clause.--host and --path are the deliberate exceptions: they stay patterns, where * is the wildcard.
Selecting an exact URL. --url (on traffic and db ls) selects records whose stored URL matches exactly. It is repeatable and OR-ed, and applied before pagination, so a match can never be hidden by -n/--limit. There is no normalization or case folding — a trailing slash or a different query-parameter order is a different URL. For substring matching use --path or --search; for identity use --uuid.

Extracting one message: traffic body / traffic headers

Read one side of one stored exchange, without re-sending anything (that is vigolium replay). Both take exactly one --uuid and one side — --response (the default) or --request — and always open the source read-only.
  • --representation defaults to decoded (supported content encodings removed); stored returns the bytes exactly as captured. A body whose decode would be partial fails with body_incomplete rather than writing a prefix — pass --allow-incomplete to write it anyway, and the receipt keeps complete: false.
  • traffic headers returns an ordered array, not a map. A map loses the ordering that distinguishes a proxy-prepended header from an origin one, and collapses three Set-Cookie headers into one. --name matches case-insensitively and returns every matching entry, with present: true|false in the receipt.
  • A zero-length body that was captured succeeds, with empty: true. That is a different answer from body_unavailable, which means the side was never captured at all — the two used to be the same empty file.
  • Printing a binary body to a terminal is refused (write it with -o, pipe it, or pass --force).
The error codes record_not_found, body_unavailable, body_decode_failed and body_incomplete distinguish the four failure states.

Saving a -j result to a file

traffic, finding and db ls accept -o <path> alongside -j. The result document goes to the file — byte-identical to what the same command prints without -o — and a receipt takes its place on stdout:
complete: false means the file holds one page — raise -n/--limit (or -a/--all on traffic) for the whole set. -o - forces the document back to stdout. The receipt never contains the document; a caller who wants both can ask twice. For a rendered report or a bulk dump, vigolium export -o <path> --format <fmt> is still the right command. Counting instead of listing. traffic --group-by <field> counts the matched records by one field rather than printing them: host, method, status_code, response_content_type, source, scan_uuid, ip, or is_authenticated. An unknown name is a usage error (exit 2) that lists the valid set. --group-limit <n> bounds the buckets, largest first (default 20, 0 = every bucket); the tail is reported as a count, never dropped. It runs the same filters the listing would, so the buckets describe exactly the rows traffic would have shown - the answer to “how many records share this value” without paying for the records. Under -j, items is the bucket list ({value, count}), total is the number of buckets, and total_records / other_groups / other_records state what was counted and what the limit left out. It cannot be combined with a flag that renders records (--raw, --burp, --markdown, --tree, --replay, the --save-to-* pair): one counts, the other prints. Source, risk & remark filters. --finding-source (on finding and db list) filters findings by where they came from — dynamic-assessment, spa, agent, oast, source-tools, or extension. On db list, --min-risk <n> keeps only records whose risk score is at or above n, and --remark <text> matches text in a record’s remarks.
Which database am I looking at? db stats names its source: the human view prints a Source: line under Driver:, and db stats -j fills items.database.path (the file actually opened) and items.database.size (its bytes, including the -wal/-shm siblings — on a WAL store the main file alone can be a fraction of what the store occupies). Both fields were declared and never filled before v0.4.7, so every db stats -j reported "path": "", "size": 0 for a database it had open. Note that items here is an object, not a row array, so total/offset/limit describe the one report.

Surface score vs risk score

db ls --min-surface <n> keeps only records whose attack-surface score is at or above n, and surface_score is a --sort key on both db ls (which also gains a SURFACE column) and traffic. It is a different question from --min-risk, so filtering on the wrong one quietly gives you the wrong shortlist:
Treat the number as a percentage, not a signal count. The signal set grows between releases and the scale rescales with it, so a threshold ports across versions but “N of M signals” does not. Scores written before v0.4.6 are not comparable to new ones - the scale changed from ten points per signal to a percentage of the set.A bare host sweep (run probe) only ever observes a GET with no session, so the signals that need a request you shaped cannot fire. Probe scores top out well under 100 - compare them against each other, not against a full scan’s.
Interactive TUI. finding, traffic, log ls, project list, and agent session all take --tui to open an interactive browser (arrow keys navigate, enter opens details, c copies the id). --no-tui forces it off — an escape hatch for scripts, in case the TUI ever becomes the default for a command. Importing findings. vigolium finding load ingests findings from JSON, JSONL, or markdown-wrapped agent output, auto-detecting the shape (an agent {"findings": [...]} envelope, a single finding object, ResultEvent JSONL, or raw database findings). It reads a positional path, --finding-file <path>, or stdin, and --scan-uuid associates the import with a scan:
Live proxy bridge. With a running Burp Suite or Caido exposing the loopback bridge (default listener http://127.0.0.1:9009, from the burp-vigolium extension or the caido-vigolium plugin; env fallback VIGOLIUM_BURP_BRIDGE_URL), vigolium traffic --burp-bridge-url <url> merges the proxy’s live history into the traffic view. -B and --caido-bridge-url are the same flag, and live rows are labelled with the vendor that answered (source: burp / source: caido), so --source caido selects exactly the traffic Caido served. Add --save-to-vigolium-db to persist those bridge rows into the DB (the current page, or every match with --all), or --save-to-burp to copy DB traffic into Burp’s Target Site map (the two --save-to-* flags are mutually exclusive). See Using Vigolium with Burp Suite for bridge setup. Push findings to Burp. vigolium finding --push-to-burp (paired with --burp-bridge-url) hands the selected finding(s)’ evidence request+response to Burp’s Organizer — severity-coloured, one item per finding — for manual confirmation. --to-repeater opens the finding’s request in a Repeater tab (Burp caps Repeater at ~30 tabs/min, so prefer --push-to-burp for a large selection), and --send-via-burp re-issues the request through Burp’s engine to capture a fresh response (with --http-mode auto|http1|http2|http2_ignore_alpn). These honour the usual finding selectors (--severity, --min-severity, a fuzzy term, --id). Since v0.4.5, --push-to-burp and --to-repeater are independent destinations that compose — one invocation can file a finding in the Organizer and open it in Repeater, exactly as replay --to-organizer --to-repeater already did. They used to be mutually exclusive here and composable on replay: the same operation with two different rules.
Cleanup safety. A bare vigolium db clean with no selector is rejected — narrow the delete with a filter (--scan-uuid, --host, --before, --status, --severity, --search, --orphans, --findings-only, --table), use db clean --all --force to empty the data tables, or vigolium db reset --force to delete and recreate the SQLite database file from scratch (it VACUUMs automatically; without --force on a TTY it prompts for interactive confirmation).

Several export formats in one run

vigolium export --format takes a comma-separated list — html, report, pdf, jsonl, markdown (alias md), sarif, bundle (alias gz), fs (alias file-system). The database is read once and every format renders from that one result set, so three formats cost one query rather than three:
  • One format keeps -o verbatim. --format html -o report writes exactly report; extension-deriving only kicks in from the second format onward. Because one -o cannot name three files, it becomes required as a base path as soon as you pass a second format. An extension already on the base is replaced, not stacked.
  • {ts} and {project-uuid} expand once for the whole run, before per-format derivation, so every file carries the same timestamp even when a slow renderer (pdf shells out to headless Chrome) finishes seconds after its siblings.
  • A failing format does not discard its siblings. The error is reported inline, the run continues, and the command exits non-zero with an N of M formats failed summary. A single format still returns its bare error unchanged.
The same applies to a gs:// base — each format is uploaded under its own key. vigolium db export -f remains single-valued.

Import

Pull external scan data back into a database. The input type is auto-detected from the path.
--burp-bridge-url <url> (alias --caido-bridge-url) turns import into a one-shot persist of a running Burp’s or Caido’s live proxy history into the database (default listener http://127.0.0.1:9009; env fallback VIGOLIUM_BURP_BRIDGE_URL). It’s an import source, so it cannot be combined with path arguments or --glob-db. Persisted rows keep the vendor they came from (source=burp / source=caido) and remain available after the proxy stops. See Using Vigolium with Burp Suite or with Caido.
An unfiltered import -B is refused (exit 2) since v0.4.5. Without a narrowing filter it used to copy every host the operator had ever browsed — carrying those hosts’ cookies and tokens — into the destination database. Where that database is shared with an autonomous agent, that is a cross-engagement leak with nothing in front of it: banking sessions, internal tools, an unrelated client’s work.It now accepts the same filter set traffic -B has, spelled the same way: --host (wildcards), --path, --method, --status, --search, --exclude-search, --from/--to, -n/--limit. Pass --all-hosts to opt into the unfiltered form on purpose. A pre-flight prints how many records are about to cross before anything is written and asks for confirmation on a TTY; --yes skips the confirmation, never the refusal.
Import is idempotent either way — new requests are inserted, changed responses refresh the existing row, unchanged traffic is skipped — so re-running it during an engagement is safe. Report metadata. When import / export render an HTML report, three flags fill in header fields the source data can’t supply: --report-url <url> sets the target of the report’s “Raw Report URL” button (overriding VIGOLIUM_REPORT_SHARED_URL; also available on scan / run), --report-duration "10h42m5s" states the scan duration, and --report-generated-at 2026-08-01T03:00:00Z stamps the generation time. import --upload pushes the artifact to cloud storage, with --upload-key overriding the default imports/<basename>-<ts>.<ext> object key. A SQLite database input (detected by its magic header, any extension) is a lossless, idempotent SQLite→SQLite merge: HTTP records, findings, scans, agentic scans, OAST interactions, and projects are deduped on their natural keys, and each row keeps its original project. Re-importing the same database adds nothing the second time. The destination is the --db target (or the configured default database when --db is omitted). Add -j/--json to print a per-table merge summary (rows inserted vs. skipped). This pairs with scan -S --format sqlite: fan out per-host .sqlite files, then merge them back into one queryable DB.

Replay

Re-send stored traffic — verbatim, or with an exact-byte override — to confirm a finding or push a whole corpus back through a proxy. For payload / insertion-point fuzzing, reach for vigolium fuzz instead (replay’s -m/--mutate was removed). The banner is suppressed so bulk output stays pipe-clean.
Exact-byte override. --raw-request <string> sends those exact bytes instead of the resolved baseline — for a hand-crafted smuggling prefix, a deliberate Content-Length, or an unusual method; --raw-request-file <path> reads the same from a file (the two are mutually exclusive). -i/--input (curl / raw HTTP / Burp) likewise replaces the whole request. For wordlist-scale payload injection at an exact position, use vigolium fuzz. --save-to-burp (paired with --burp-bridge-url) adds each replayed request and its fresh response straight into Burp’s Target Site map — without proxying it twice — so a confirmed exploit lands back in Burp for manual follow-up (default listener http://127.0.0.1:9009; env fallback VIGOLIUM_BURP_BRIDGE_URL). See Using Vigolium with Burp Suite. Send through Burp’s engine. --send-via-burp routes the send through Burp’s own HTTP stack so the exact bytes reach the wire — a deliberate Content-Length, a smuggling prefix, or an unusual method is preserved instead of being normalised by Go’s client. Pick the wire protocol with --http-mode auto|http1|http2|http2_ignore_alpn (default auto; use http1 for request smuggling / desync so auto doesn’t renegotiate HTTP/2 and reframe the request). Independently, --to-repeater (with --repeater-tab <name>, default vigolium) stages the request in a Burp Repeater tab, and --to-organizer (with --notes and --highlight) stores the request + response pair in Burp’s Organizer. All require --burp-bridge-url; without them the send path is unchanged. See Send traffic through Burp’s engine. Against Caido the same flags apply, mapped to the closest equivalent: --to-repeater opens a Replay session, --to-organizer stores the pair in a named Replay collection, and the raw send is always HTTP/1.1.

Bulk replay

A positional vigolium replay <term> does a broad fuzzy match over stored traffic (URL, path, host, method, content-type, source, and the raw request/response) — exactly like vigolium traffic <term> — and switches replay into “iterate every matching stored record” mode. Passing --all or any selector flag does the same. Each matched record is re-sent verbatim through the diff engine, streaming one JSONL object per record with per-record error isolation.
The selection surface mirrors vigolium traffic:
  • Match: a positional term, repeatable --search (AND-combined; matches URL/path and the raw request/response), --host, --method (repeatable), --status (repeatable), --path, --source, --body.
  • Exclude: repeatable --exclude-search and --exclude-body drop matching records.
  • Range & order: --from / --to (see Date filtering for the accepted forms), --sort uuid|created_at|sent_at|method|status|time (default created_at), --asc (default: newest-first), --offset <n> for pagination.
  • --all lifts the default -n/--limit cap (100); narrow the set with the selectors instead. Each record is re-sent verbatim — payload / insertion-point fuzzing lives in vigolium fuzz.
  • Throttle with -c/--concurrency (default 10); cap the set with -n/--limit (default 100, lifted by --all).
  • Combine with --proxy, --save-to-burp, or --send-via-burp as with single-record replay.
  • -S/--stateless --db <file> reads baselines from a standalone .sqlite/.jsonl export with project scoping off — it never writes to your project DB.

Replay through a real browser

--with-browser loads each matched record’s URL in a real browser routed through --proxy, instead of re-sending the stored bytes. Use it when an intercepting proxy needs genuine browser traffic — real TLS fingerprint, JS execution, and subresource loads — rather than a replayed byte stream:
This is not a diff mode. A navigation exposes no status code and no response body, so there is nothing to compare against the baseline. Those runs emit a browser object — requested_url, final_url, title, dialogs, and original_method when the record’s method wasn’t GET (a navigation can only issue a GET) — and leave result null, rather than a hollow summary whose zero-valued status would read as a real comparison.
Because nothing is re-sent, --with-browser is rejected alongside every flag downstream of the send: --in-replace, --raw-request, and the Burp send/stage targets. Without --proxy it warns — browser traffic isn’t being routed anywhere to be captured.

Date filtering

--from / --to (aliases --since / --until) are available on finding, traffic, db ls, db export, and replay, all sharing one parser. db clean --before and swarm’s --records-from since=|until= accept the same forms:
Two behaviours are worth knowing. Bare dates and wall-clock times resolve in your local zone — resolving them as UTC would silently drop the first hours of the local day, and --since today is the flag’s whole point. And a bare date on an upper bound snaps to end-of-day, so --from D --to D is the full day D rather than an empty range. An inverted range is a hard error, not silently-empty output.
db clean --before deliberately does not snap to end-of-day, so the named day survives the delete.

Fuzz

vigolium fuzz injects a caller-supplied payload set into chosen positions of one request and streams per-payload response signals (status, size, words, lines, time, reflection, baseline-delta) with match/exclude gating and auto-calibration against the target’s catch-all. It is a low-level primitive, not a scanner: it sends exactly the payloads you give it at exactly the positions you pick and makes no vulnerability decision — it emits raw signals, not findings. The intelligence comes from the caller (typically a coding agent). For confirmation-backed detection of known classes, use the module scanner instead: vigolium scan-request -i req.txt -m xss,sqli -j.
Source (one of): a positional URL (with -X/--request, -H/--header, and -d/--data to build the method, headers, and body), -i/--input (curl, raw HTTP, Burp XML, base64, URL, or - for stdin; --input-file reads that same value from a file), -u/--record-uuid (a stored HTTP record), or a request piped on stdin. -t/--target overrides the scheme/host/port the request is actually sent to. -X, -H, and -d apply to every source, not just a positional URL, and are applied before positions are resolved — so a marker introduced by -H 'X-Forwarded-For: FUZZ' is discovered. Positions (what to fuzz): a literal FUZZ marker anywhere in the request (request line, path, header, or body) wins if present; otherwise --fuzz method|path|params|param-name|headers|cookies|all (default: all discovered insertion points), --point TYPE:name (e.g. URL_PARAM:id, repeatable), or --fuzz-header <name> (repeatable). --fuzz-header injects a header the request doesn’t already carry rather than erroring — X-Forwarded-For is interesting precisely because the client never sends it. The marker keyword is configurable with --keyword. Payloads (combine freely): --class selects a built-in vulnerability class — xss, sqli, ssti, ssrf, lfi, path_traversal, xxe, cmdi, open_redirect, crlf (aliases like traversal, rce, sql, template accepted); -w/--wordlist takes a builtin name (fuzz, dir-short, dir-long, file-short, file-long) or a file path (repeatable); -p/--payload adds an inline literal (repeatable). Attack modes (--mode). Numbered markers FUZZ, FUZZ2, FUZZ3… become separate positions, and -w users.txt:FUZZ binds a wordlist to one of them: Multi-position modes are marker-only: an httpmsg insertion point is built against the request it was analyzed on, so applying a second would discard the first’s edit. Clusterbomb expansion is bounded, so a runaway product is a clear error rather than an out-of-memory kill. Matchers & excludes. Matchers keep a response (OR-combined; empty keeps all): --match-status-code (accepts all), --match-size, --match-words, --match-lines, --match-regex, --match-time (ms), --match-time-z, and --match-header. Each has an --exclude-* counterpart that drops a response. --match-mode all requires every category instead of any (likewise --exclude-mode). Numeric flags take a comparison predicate, not just exact equality — N, N-M, >N, >=N, <N, <=N, !N, comma-separated. Exact equality alone is near-useless for sizes, since a page carrying a timestamp or CSRF token is never byte-identical twice:
--match-header matches response headers — header reflection is the whole signal for open redirect and response splitting. Reflection is likewise reported as reflected_raw (verbatim) separately from an escaped echo, with reflected_in naming the location (body, header:Location), because conflating a verbatim echo with an HTML-escaped one hides the difference between “this might execute” and “the app escaped it”. Auto-calibration (on by default) probes the target’s wildcard/catch-all and suppresses matches identical to it — suppressed results carry "calibrated":true; disable it with --no-calibrate. Anomaly scoring (-a/--anomaly) is the alternative to writing matchers by hand. Writing a matcher means knowing the interesting size or status up front; instead, each response is scored against the baseline and against the run’s own population, and the ones that stand out are kept:
The population reference is what makes this work on an unknown target — a status change that every payload triggers is the endpoint’s normal behaviour, not a signal, and only rarity distinguishes those. Signals include a leaked SQL/stack/template error the baseline lacked, a 5xx, a 4xx→2xx flip (auth-bypass-shaped), size and time outliers, a body no other payload produced, a rare status, a changed Location/WWW-Authenticate, and unencoded reflection. Outliers use median-absolute-deviation rather than standard deviation, since a fuzz population is full of outliers by construction and they would inflate a stddev enough to hide themselves. Every result carries anomaly_score and anomaly_reasons. --anomaly-threshold low|medium|high (or a number) sets the bar; --anomaly-min-population (default 12) is how many responses must land before rarity and outlier signals count. With --anomaly and no explicit matchers, “interesting” replaces “keep everything” as the gate.
Anomaly scoring reports where to look, never a verdict — the primitive stance holds. Confirm a lead with the module scanner: the -j summary hands you a ready scan-request query for exactly that.
Baseline timing. --baseline-samples N sends the un-fuzzed request N times to measure timing jitter, giving each result a time_z (standard deviations above the baseline mean) that --match-time-z can gate on. Default is 1, or 3 under --anomaly. This is how to express a time-based signal without guessing a per-target millisecond threshold. Curl parity. fuzz accepts curl’s long-form flags so a request pasted from DevTools works with minimal editing: --cookie, --user, --user-agent, --referer, --data-raw, --data-binary, --data-urlencode, --form, --form-string, --get, --head, --insecure, --cacert, --cert, --key, --max-redirs, --connect-timeout, --max-time, --compressed, --http1.1, --http2, --resolve, --path-as-is. Long forms only — the short spellings are already taken by vigolium meanings (-u record UUID, -i input, -w wordlist, -c concurrency). Body assembly is shared with the curl input parser, so --data-urlencode and --form behave identically whether they arrive as flags or inside a pasted curl command. --auth-session / --session-id / --no-cookies mirror replay. Dry run. --dry-run resolves positions and payloads and prints the exact bytes each would send, with zero network traffic — the pre-flight to run before committing to a wordlist-scale job. --ignore-scope overrides the project-scope guard when the target is outside configured scope. Output. Default is JSONL (one object per send) to stdout — matched-only unless --all-results; --pretty renders a human-readable table, -o/--output writes to a file. Under -j/--json, the JSONL streams to stderr and a single summary object prints to stdout: {target, sent, matched, calibrated, baseline, top_results (ranked anomalies), query (a ready scan-request confirmation)} — a handle a coding agent can act on directly. --fail-on-match exits 3 when any result matches, for CI/agent gating. Throttle with -c/--concurrency (default 10) and --delay <ms>; --timeout sets the per-request timeout and --no-redirects stops following 30x. Honors HTTP_PROXY/HTTPS_PROXY for Burp inspection. Send through Burp’s engine. --send-via-burp (with --burp-bridge-url) routes every payload through Burp’s own HTTP stack so exact bytes hit the wire — the way to fuzz malformed/smuggling requests (pair with --http-mode http1); --matches-to-organizer pushes each matched request into Burp’s Organizer for triage. See Send traffic through Burp’s engine.
replay’s -m/--mutate flag was removedreplay now re-sends stored traffic (verbatim, or with an exact-byte --raw-request override) and confirms, while fuzz owns wordlist-scale payload injection at an exact position. (The pkg/replay library and the agent’s in-process replay_request tool still apply mutations.)

Kit (standalone utilities)

vigolium kit exposes the scanner’s internal primitives as one-shot, stateless tools — no database, no project scope, no scan pipeline. Added in v0.4.1. Full guide: Utility Toolbox.
secret-scan --fail-on-match and jwt-crack --fail-on-crack exit 3 on a hit, for CI/agent gating. js-beautify never exits non-zero for a degraded analysis — read its status and diagnostics fields, not the exit code.

Tool shims

A coding agent reaching for ffuf, nuclei, katana, gau or arjun will often find them on the host — and then the whole run happens outside the pinned database, outside the phase model, invisible to every later finding/traffic/replay call. So Vigolium accepts each tool’s own argv and routes it to the native phase, printing the translation before it runs:
Mapped flags include the pace knobs (-t/-threads/-c--concurrency, -rate/-rl--rate-limit), headers, proxy, and each tool’s own selectors. Both -u X and -u=X parse.
  • An unmapped argument is a hard error naming the native command, never a silent drop — a dropped flag is a scan that ran with a scope nobody chose. vigolium ffuf --mc 200 tells you to run vigolium run discovery --help.
  • Where a concept doesn’t exist, the shim says so rather than approximating. katana -d 3 errors: Vigolium’s spider is a state machine over DOM snapshots, not a depth-limited link follower, so bound it with --spider-max-time instead.
The shims are a redirect for muscle memory, not a full port — once you know the native form, prefer it. It takes every Vigolium flag; the shims take a subset.

Strategies & Phases

Inspect scanning strategy presets and the phases that make up a scan.

Modules

Manage the active and passive scanner modules.
Module IDs carry no active- / passive- prefix — the id in vigolium module ls is exactly what -m / --module-id expects. See the Scanner Modules Reference for the full table.

Skills

Install the coding-agent skill bundles shipped inside the binary (so they always match your CLI version) into a coding agent’s skills directory. See Using Vigolium in your agent.
An already-installed skill is skipped unless --force is given. Because the content is embedded in the binary it always matches the installed version — prefer vigolium skills install over copying stale files by hand.

Extensions

Run and manage JavaScript extensions that hook into the scanner.
vigolium js executes a script with the whole vigolium.* API in scope. It reads stdin by default; --code takes an inline string and --code-file a .js/.ts path. --target seeds the TARGET variable, --timeout bounds execution (default 30s), and --format json|text picks the output shape.

Scope

Control what’s in-scope. Source code is attached per scan via the --source flag on vigolium agent <subcommand> (autopilot, swarm, query, audit).

Agent (AI)

Run agentic and source-audit modes. See Agent Mode for the full list of subcommands.

agent query: single-shot prompts

agent triage: confirm or retire one finding

Picks one stored finding and asks an agent whether it’s real. The agent reads the finding’s description and captured request/response, may re-probe the live target with its HTTP tool, and writes back a verdict plus reasoning. A false_positive verdict downgrades the finding’s severity to info and appends the reasoning to its description, so the original detection context survives. --max-duration defaults to 5m (0 = no limit). It takes the same olium provider overrides as the other agent subcommands.

agent swarm: AI-guided multi-phase scan

agent swarm accepts the olium override flags --provider, --model, --base-url, --llm-api-key, --oauth-cred, and --oauth-token; each falls back to agent.olium.*. For the Vertex providers, --gcp-project and --gcp-location set the GCP project and region (falling back to $GOOGLE_CLOUD_PROJECT / $GOOGLE_CLOUD_LOCATION, then agent.olium.google_cloud_*, then us-central1). --prompt and the positional [prompt] are the same task-guidance slot and stay verbatim when structured target/source flags are also present. Choosing the input records. Without --discover, swarm plans from the traffic it’s given: --all-records uses every HTTP record in the active project, and --records-from filters them by a spec string (host=, path=, method=, status=, source=, since=, until=, comma-separated — since/until take the shared date grammar). --max-plan-records caps how many reach the plan agent — it selects the most interesting with one slot per URL prefix, and --intensity overrides the default (quick 10, balanced 25, deep 50; 0 = no limit). Tuning the run. --probe-concurrency (default 10), --probe-timeout (default 10s), and --max-probe-body (default 2 MB) bound the probing stage; --sub-agent-concurrency (default 3) caps parallel source-analysis sub-agents (routes, auth, extensions); --max-master-retries (default 3) is how many times a master-agent parse failure is retried. --disable-guardrail skips the prompt-safety classifier on the natural-language prompt — use it only when a known-good prompt is being refused.

agent autopilot: autonomous agentic scan

Autopilot and swarm no longer expose CLI flags named --focus, --instruction, --instruction-file, --browser, or --credentials. Put those details in --prompt. The REST agent endpoints keep structured equivalents for API clients. Knowledge base. --knowledge-base <file|dir> front-loads operator-supplied docs about the app. Prose (markdown / txt / rst) is LLM-distilled into a compact brief plus a document index, with the full documents left on disk for the agent to read on demand. HTTP-traffic exports found in the same path — HAR, Burp XML, curl, OpenAPI/Swagger, Postman, URL lists, raw HTTP — are auto-detected, parsed, and ingested into the project database as normal traffic (source=knowledge-base), with a sample folded into the brief. So a knowledge base can seed the agent with real requests, not just prose. --knowledge-base-no-traffic turns that off and treats every file as prose; --knowledge-base-raw skips the LLM distillation and inlines the deterministic index only (offline / reproducible runs). Both are no-ops without --knowledge-base. Other autopilot controls. --disable-guardrail skips the prompt-safety classifier on the natural-language prompt (only when a known-good prompt is being refused). --post-halt-gap-threshold <n> sets how many new (method, URL) routes the post-halt probe must turn up before the agent is re-entered (0 = the built-in default of 5).

agent audit: unified source audit (vigolium-audit + piolium)

Runs the vigolium-audit harness and/or piolium against a single source tree under one AgenticScan, with per-driver session subdirs and a post-pass findings dedup. vigolium-audit is the embedded harness name; the CLI driver value is audit.
On agent audit, --intensity deep resolves to the deep,confirm mode chain (the modes run back-to-back), matching the POST /api/agent/run/audit endpoint; quick maps to lite and balanced to balanced (both single-mode). -S/--stateless runs the whole audit into a throwaway temp DB (your main DB is untouched, mirroring scan -S) and, on completion, renders a self-contained HTML report from the run’s findings to vigolium-result/vigolium-audit-report.html (override with -o/--output, which supports gs:// and {ts}). --output-dir <dir> (stateless-only) additionally bundles that report and a copy of each ran driver’s raw vigolium-results/ tree into one folder — a single driver lands flat at <dir>/vigolium-results/, multiple are namespaced under <dir>/<driver>/. -S is rejected with --interactive. --keep-raw is on by default for the CLI (it retains the <source>/vigolium-results/ copy); --clean-raw removes that source copy after the run. Bring-your-own-key (audit leg). Three mutually exclusive flags supply credentials for the run without touching your config; each accepts a literal value, $ENV_NAME, or @path:
  • --api-key — API key for the resolved agent (claude → ANTHROPIC_API_KEY, codex → OPENAI_API_KEY). Empty inherits agent.olium.*.
  • --oauth-token — Anthropic OAuth bearer token, Claude only (produced by claude setup-token).
  • --oauth-cred-file — OAuth credential file (Codex ~/.codex/auth.json shape). For piolium runs it’s staged under the pi agent dir with backup-and-restore.
Run controls. --show-thinking renders the agent’s internal thinking blocks in the live stream (off by default — they’re verbose). --preflight-timeout <dur> bounds the per-driver preflight check (default 30s, applies to both pi and claude). The piolium retry knobs are --plm-phase-retries, --plm-command-retries, and --plm-longshot-timeout (per-file kill timer in ms, longshot mode); 0 on any of them means “use piolium’s own default”.

log: replay an agentic session

Every olium agent run (autopilot, swarm, query, olium) writes a Pi-compatible transcript.jsonl. Replay it as a rendered conversation, or dump the raw JSONL:
Three v0.4.5 fixes worth knowing:
  • -S works. log used to reject it outright with unknown shorthand flag — a non-zero exit and no output, i.e. a control that looked live and could only ever report nothing. It now reads through the same path finding/traffic do, so $VIGOLIUM_DB_PATH or -S --db <file> reaches that session’s database with project scoping off.
  • --follow no longer hangs on a dead scan. Auto-follow used to fire whenever the scan row said running, and a scan reaped by a deadline or SIGKILL says that forever — so an omitted --follow parked the read until the caller’s own timeout, on precisely the runs that had already timed out. Now a non-TTY stdout never auto-follows, a row whose log hasn’t been written to for ~2 minutes is treated as stale, and an explicit --follow / --follow=false still wins. vigolium log <uuid> | cat terminates promptly.
  • WAF notices survive the tail window. They fire early (the edge is fingerprinted on the first clean response), so the default 200-line tail was structurally the wrong end of the file for the most important thing in it. Any [waf-block-detected] / [waf-pacing-armed] line above the window is now reprinted above it under an “N earlier notice(s)” header.
For live progress from a program, prefer --events ndjson on the scan itself — a structured stream rather than a rendered log.

Configuration

vigolium config ls (aliases list / view) redacts sensitive values — API keys, tokens, credentials — as [redacted] by default. Pass --show-secrets to reveal them in plaintext; it prints a warning to stderr. The generic -F/--force no longer reveals secrets (it only skips confirmation prompts). Both accept an optional key prefix or glob (config ls server, config view 'notify.*') to print just a subtree. vigolium config set <key> <value> takes a dotted path and creates missing intermediate sections, so a previously-unset nested key — notify.telegram.bot_token, server.burp_bridge_url, server.enable_burp_bridge — can be set directly instead of failing with “key not found”. The value is coerced to the field’s declared type (bool / int / float / comma-separated list), and the key is validated against the config schema so a typo is rejected up front.
This page covers the most common invocations. Every command supports --help for the full flag reference, and most commands accept the global flags shown by vigolium --help.