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.

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 either -S --split-by-host (per-host outputs) or --db-isolate (merge into one --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; not combinable with --stateless).
  • --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.

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.

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). 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.
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.

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.