Vigolium
CLI-first web vulnerability scanner, built to be driven by a coding agent.
Full docs: docs.vigolium.com.
TL;DR
Five commands cover most work:
vigolium scan -t https://target.example --fail-on high # scan into the DB, gate CI
vigolium finding -j --min-severity high --compact # read what it found
vigolium replay --finding-id 42 # confirm one finding
vigolium agent autopilot -t https://target.example # let AI drive the whole scan
vigolium traffic -j --host target.example --compact # browse stored requests
Anything not listed here: vigolium <command> -h is authoritative for the
version you have installed. vigolium --full-example prints worked examples by
section.
Mental model
- The database is the state. Scans write findings + HTTP records; query
commands read them back. Commands compose through the DB, not through pipes.
- Three machine contracts — don't confuse them:
-j/--json on finding/traffic/db → one compact, token-bounded
envelope. Parse this during triage.
--format jsonl / export → bulk {"type":…,"data":{…}} stream, one per
line, full fidelity. Archival, not triage.
--events ndjson on the scan commands → a live event stream on stdout
while the scan runs. This is how you learn what a 15-minute crawl is doing;
see "Watching a scan while it runs" below.
- One
-j envelope, every command. {schema_version, command, project_uuid, project_scoped, db_path, total, offset, limit, items, query} — items is the
row array, and now the only one. The old per-command names (records,
findings, scans, rows) duplicated every row on the wire and are no
longer emitted; --json-legacy-keys restores them for a caller still
migrating, at double the payload. Check schema_version (and vigolium version --json, which also reports db_schema_version) at startup rather than
discovering drift at parse time. Failures use the same spine:
{schema_version, command, ok:false, error:{code, message, exit_code}} on
stdout, human text on stderr — so branch on error.code instead of scraping
prose: usage_error · source_missing · source_unreadable (not a database,
or corrupt) · source_incompatible (valid SQLite that is not a vigolium store)
· gate_tripped · failed.
- A wrong
--db path does not error — it reads as empty. Opening a database
creates it, so a typo'd or not-yet-scanned path returns {"total":0, "items":[]} and exit 0, and pointing --db at another tool's SQLite file
writes vigolium's tables into it. "Nothing found" and "wrong file" are the same
output. Add --read-only to any read whose store must already exist: only
then does a missing path become source_missing and a foreign store become
source_incompatible. Otherwise assert db_path and sanity-check total
against a query you know matches.
- Non-interactive by default. TUI is opt-in (
--tui). Destructive commands
need --force. Use --no-color (or NO_COLOR=1) for clean text.
- Everything is project-scoped —
--project-name, --project-uuid,
VIGOLIUM_PROJECT_UUID, or VIGOLIUM_PROJECT_NAME. Every -j envelope also
names the db_path it opened, so assert that rather than trusting a pin
survived a subprocess chain.
- JSON summaries hand you the next command. Agentic scans,
fuzz, and the
read commands emit a query field with a ready follow-up. Run that rather
than composing your own.
The agent loop
scope → scan → read → confirm → hand off
| Step |
Command |
Notes |
| Scope |
vigolium doctor --json / db stats --json |
environment + what's already stored |
| Scan |
scan, scan-url, scan-request, run, or agent … |
add --fail-on high to gate |
| Read |
finding -j --compact --fields … |
survey first, then drill with --id --with-records |
| Confirm |
replay --finding-id <id> |
re-sends and diffs against the baseline |
| Hand off |
finding --markdown, --push-to-burp, export --format html |
report or escalate to a human |
Full walkthrough with output shapes, filters, and exit codes:
references/agent-loop.md — read this first if you are driving vigolium
from an agent.
Watching a scan while it runs
A full scan is silent for minutes at a time. Don't guess whether it is working,
and don't scrape vigolium log — ask for the event stream:
vigolium scan -t https://target.example --events ndjson 2>/dev/null | jq -c .
One JSON object per line on stdout, flushed per event; the human console
stays on stderr, so 2>/dev/null yields clean NDJSON with zero non-JSON lines.
Types: scan.started · phase.started|progress|finished · waf.block ·
waf.pacing · finding.new · error · scan.finished.
Every line carries scan_uuid and a schema version v. scan.finished is
always last (status:"interrupted" on SIGINT/SIGTERM); its absence means the
process was killed outright. phase.progress.requests_sent every 5s is the real
liveness signal — a crawl that is working versus one that is wedged.
Full event table and field-by-field notes:
references/agent-loop.md.
Command router
| I need to… |
Use |
| Scan one or more target URLs |
vigolium scan -t <url> |
| Scan a single URL with custom method/headers |
vigolium scan-url <url> --method POST --body '...' |
| Scan a raw HTTP request from file/stdin |
vigolium scan-request -i request.txt |
| Run only one scan phase |
vigolium run <phase> or scan --only <phase> |
| Sweep a big host list (httpx-style) |
vigolium run probe -T hosts.txt --json --no-response |
| Tune scan aggressiveness (phases + profile) |
vigolium scan -t <url> --intensity quick|balanced|deep |
| Content discovery with a custom wordlist |
vigolium scan -t <url> --discover --discovery-wordlist ./words.txt |
| Watch a running scan from a program |
vigolium scan -t <url> --events ndjson 2>/dev/null |
| Cap one phase without capping its siblings |
vigolium scan -t <url> --rate-limit known-issue-scan=20 |
| Import an OpenAPI/Swagger spec and scan |
vigolium scan -I openapi -i spec.yaml -t <base-url> |
| Import Burp/HAR/cURL traffic |
vigolium scan -I burp -i export.xml |
| Filter modules by tag |
vigolium scan -t <url> --module-tag spring --module-tag injection |
| Analyze a target with zero attack traffic |
vigolium scan -t <url> --passive-only |
| One-shot scan, results on stdout, no DB |
vigolium scan -t <url> -S --silent --print-finding |
| Resume an interrupted fan-out run |
vigolium scan --resume (bare, in the run's output dir) |
| Ingest traffic without scanning |
vigolium ingest -t <url> -I openapi -i spec.yaml |
| Ingest many files in ONE process |
vigolium ingest -i a.har -i b.har or --dir ./captures --dir-glob '*.har' |
| Start the API server |
vigolium server |
| Start server and auto-scan new traffic |
vigolium server -t <url> --scan-on-receive |
| Mirror ingested traffic to a live file tree |
vigolium server --mirror-fs ./mirror |
| Autonomous AI-driven scan |
vigolium agent autopilot -t <url> |
| Autopilot from a natural-language prompt |
vigolium agent autopilot "scan VAmPI at ~/src/VAmPI on localhost:3005" |
| Full-scope AI scan (discover → plan → scan → triage) |
vigolium agent swarm -t <url> --discover |
| Deep AI scan of one endpoint |
vigolium agent swarm -t <url> |
| Whitebox source audit (no target needed) |
vigolium agent audit --source . |
| AI code review, one shot |
vigolium agent query --prompt-template security-code-review --source ./src |
| Interactive AI agent TUI |
vigolium olium (alias ol) |
| AI-confirm one finding |
vigolium agent triage 42 |
| List agent sessions |
vigolium agent session |
| Browse stored HTTP traffic |
vigolium traffic or vigolium traffic <search> |
| Count records by a field instead of listing them |
vigolium traffic --group-by status_code (--group-limit N) |
| Browse findings |
vigolium finding or vigolium db ls findings |
| Compact agent JSON + linked records |
vigolium finding -j --with-records --min-severity high |
| Re-send one request + baseline diff |
vigolium replay --record-uuid <uuid> |
| Re-confirm a finding's evidence |
vigolium replay --finding-id 42 |
| Bulk re-send stored traffic by pattern |
vigolium replay <search> --proxy http://127.0.0.1:8080 |
| Re-send under a different session |
vigolium replay -u <uuid> -H 'Cookie: session=other' |
| Payload / insertion-point fuzzing |
vigolium fuzz -u <uuid> --point URL_PARAM:id --class sqli -a |
Fuzz a wordlist at a FUZZ marker |
vigolium fuzz https://t/FUZZ -w file-long --match-status-code 200,301 |
| Scan files/stdin for leaked secrets |
vigolium kit secret-scan <files|dirs|-> --fail-on-match |
| Unminify / unpack a JS bundle |
vigolium kit js-beautify <file|url|-> |
| OOB (OAST) callback URL + polling |
vigolium kit oast new → vigolium kit oast poll --session run.yaml |
| Harvest known URLs for a domain |
vigolium kit harvest target.example |
| Crack a JWT's HMAC secret |
vigolium kit jwt-crack <token> |
| List/print built-in wordlists or payloads |
vigolium kit wordlist [name] / vigolium kit payload --class sqli |
| Hand a finding to Burp |
vigolium finding --id 42 --push-to-burp -B http://127.0.0.1:9009 |
| Pull live Burp Proxy history into the DB |
vigolium import -B http://127.0.0.1:9009 --host target.example |
| Send exact bytes through Burp's engine |
vigolium replay --raw-request-file req.txt --send-via-burp --http-mode http1 -B … |
| Persist cookies across replays |
vigolium replay --session-id login --record-uuid <uuid> |
| View database statistics |
vigolium db stats |
| Export results |
vigolium export --format jsonl -o results.jsonl |
| Export a browsable file tree |
vigolium scan -t <url> --format fs -o run |
| Export the run's standalone SQLite DB |
vigolium scan -t <url> -S --format sqlite -o run.sqlite |
| Fail CI on a severity |
vigolium scan -t <url> --fail-on high |
| Split multi-target output per host |
vigolium scan -T targets.txt -S --split-by-host --format fs |
| Clean database records |
vigolium db clean --host <hostname> |
| List scanner modules |
vigolium module ls or vigolium scan -M |
| Enable/disable modules |
vigolium module enable xss / module disable sqli |
| Manage JS extensions |
vigolium ext ls / ext docs / ext preset |
| Run a custom extension |
vigolium run extension -t <url> --ext custom-check.js |
| Execute arbitrary JS with the vigolium API |
vigolium js --code 'vigolium.http.get("https://t/")' |
| View/modify configuration |
vigolium config ls / config set <key> <value> |
| Manage auth sessions |
vigolium auth lint / auth list / auth load / auth totp |
| Manage scope rules |
vigolium scope view |
| Manage projects |
vigolium project create|list|use <name> |
| Cloud storage |
vigolium storage ls|upload|download|presign|rm |
| Import an audit folder or JSONL/SQLite export |
vigolium import <path> |
| View runtime logs for a scan/agent run |
vigolium log <uuid> (-f to follow) |
Initialize ~/.vigolium/ |
vigolium init |
| Health check |
vigolium doctor (--fix installs what's missing) |
| Refresh nuclei templates without touching the binary |
vigolium update --skip-binary |
| Copy an extension authoring template |
vigolium ext example -l, then ext example <key> |
Which scan command? Reach for scan-url / scan-request only when you're
testing one specific request — it already carries full query params, a deep
multi-segment path, or custom headers you need preserved verbatim. Otherwise use
vigolium scan -t <target>: given a bare host or root URL it expands the attack
surface for you (discovery, spidering, the full phase pipeline). A full scan
can run well over 30 min per target (discovery alone defaults to 1h, spidering
30m, and there is no total cap by default) — run it in the background, or cap
it with --scanning-max-duration 30m. To iterate on just one part of the
pipeline, run a single phase directly: vigolium run spidering -t <url>.
Replacing standalone recon tools
Never go shopping for a binary. If you shell out to the real ffuf /
nuclei / katana / gau, the run's whole mapping happens outside the pinned
traffic DB, outside the phase model, with flags nobody scoped — and nothing
downstream can see it.
You don't have to remember the native spelling. Vigolium accepts each tool's
own argv and routes it:
vigolium ffuf -u https://t/FUZZ -w words.txt
→ routed to: vigolium run discovery -t https://t --discovery-wordlist words.txt
The shim prints the translation, runs it through the same command tree, and
writes to the pinned DB. An argument it can't map is a hard error naming the
native command — never a silent drop, because a dropped flag is a scan that
ran with a scope nobody chose.
| Instead of |
Shim |
Native |
ffuf / feroxbuster |
vigolium ffuf … |
run discovery --discovery-wordlist, or fuzz https://t/FUZZ -w file-long |
nuclei |
vigolium nuclei … |
run known-issue-scan -t <url> |
katana / gospider |
vigolium katana … |
run spidering -t <url> |
gau / waybackurls |
vigolium gau <domain> |
run external-harvest -t <url> (into the DB), or kit harvest <domain> (stdout, no DB) |
arjun |
vigolium arjun -u <url> |
fuzz --fuzz param-name --anomaly <url> |
httpx |
— |
run probe -T hosts.txt --json (aliases: run httpx/alive/sweep) |
subfinder / amass |
— |
not covered — pass a host list with -T |
The shims are a redirect for muscle memory, not a full port: prefer the native
form once you know it (it takes every vigolium flag). Recipes:
references/scanning.md.
Reference router
Load the file that matches the task — don't read them all.
| Topic |
Reference |
Load when |
| Driving vigolium from an agent |
references/agent-loop.md |
triage, -j contracts, replay, exports, exit codes |
| Scanning commands |
references/scanning.md |
scan / scan-url / scan-request / run flags, phases, strategies, output formats, replacing nuclei/ffuf/katana/httpx |
| Fuzzing |
references/fuzzing.md |
vigolium fuzz — positions, markers, attack modes, payload classes, anomaly scoring, matchers |
| Utility toolbox |
references/kit.md |
vigolium kit — secret-scan, js-beautify, oast, harvest, jwt-crack, wordlist, payload |
| Burp Suite / Caido |
references/burp.md |
Burp or Caido bridge setup, live history, Repeater/Organizer/Site map handoff, --send-via-burp, proxy channel — one loopback protocol, either vendor |
| AI agent modes |
references/agent-modes.md |
agent query / autopilot / swarm / audit / olium / triage / session, intensities, providers, templates |
| Auth & sessions |
references/auth.md |
--auth-file / --auth, YAML format, extract rules, authenticated scanning |
| Data & management |
references/data.md |
db, finding, traffic, module, extensions, js, config, scope, export, import, log, project, storage |
| Server mode |
references/server.md |
vigolium server — REST API, recording/MITM proxy, scan-on-receive, live mirror, endpoints |
| Ingesting traffic |
references/ingest.md |
vigolium ingest — local/remote, per-format input examples, spec flags |
| Writing extensions |
references/extensions.md |
custom JS scanner modules, vigolium.* API |
| Any specific flag |
references/flags.generated.md |
generated from the command tree — grep it by flag name |
Not covered? vigolium <command> -h is the authoritative, version-matched
flag list. Then search docs.vigolium.com — start
with the cheat sheet.
This skill is a curated subset; the docs are the source of truth.
Token discipline
The single most important habit when driving vigolium from an agent: survey
cheap, drill narrow.
# 1. Survey — metadata only, chosen fields.
vigolium finding -j --min-severity high --compact --fields id,severity,module_id,url
# 2. Drill — one finding, self-contained with its HTTP records.
vigolium finding -j --id 42 --with-records
# 0. Cheaper still — when the question is a shape, not a row set, count in SQL.
vigolium traffic -j --group-by status_code --host target.example
Never dump rows to count them. --group-by runs the same filters the listing
would and returns buckets, so "what status codes did we see" costs a dozen
integers instead of the corpus.
Under --json, bodies are preview-capped with body_size/body_sha256/
body_truncated, binaries are stubbed body_omitted:"binary", and findings get
a ±240-char response_evidence snippet windowed on the match. The full
shaping-flag table (--compact, --fields, --full-body, --with-records,
--min-severity, --pick, --markdown, --raw, --agentic-scan) lives in
references/agent-loop.md.
Three things worth knowing about that habit — details in
references/agent-loop.md:
- An unknown
--fields name is a usage error (exit 2) that lists the valid
set for that view, so a typo fails loudly instead of returning a row with the
key quietly missing. --fields composes with --with-records; use
--record-fields / --record-limit to shape the nested records.
- A gzip body over 1 MiB comes back as a prefix, flagged
decoder_capped: true with body_size/body_sha256 describing the whole body. When you see
that flag, do not conclude a string is absent — pull the body whole with
vigolium db export --format fs --uuid <uuid>.
- Read one record by identity with
traffic --uuid <uuid>, applied before
pagination. A UUID as the positional term searches text, not identity.
Global flags
These work on every command, and a per-command flag of the same name wins.
The ones an agent reaches for, beyond -j, --db, --project-*, --format,
--no-color and --silent covered above:
| Flag |
Use it when |
--dump-traffic |
you need to see the actual bytes. Prints every request/response pair to stderr, Burp-style, bypassing the logger - so it composes with -j on stdout instead of corrupting it |
--debug |
a scan behaves unexpectedly; adds debug-level logging including outgoing request lines. -v/--verbose is the milder step |
--log-file <path> |
you want the run's logs as JSON on disk instead of scrolling past - the thing to attach to a bug report |
--mem-limit |
a big crawl is being OOM-killed. A soft GOMEMLIMIT ceiling; default auto is ⅓ of RAM, scaled down by -P so the children together stay under ⅔. Takes off, 6GiB, or 50%. An existing GOMEMLIMIT env var overrides it |
--read-only |
the database is evidence - no mkdir, no journal change, no checkpoint (see Invariants) |
--skip-dependency-check |
a container/CI run must not stall on the first-run chromium + nuclei-template check |
-M/--list-modules |
you want the module list without module ls |
--list-input-mode |
you forget which -I values exist; prints each with an example |
--width <n> |
table output is wrapping badly (default 70 columns) |
--config <path> |
pinning a config file explicitly, the way --db pins the store |
--dump-traffic is the right debugging reflex, not --debug. When the
question is "what did we actually send", it answers directly and keeps stdout
clean for a parser; --debug buries the same bytes in log lines.
Invariants
Things -h won't tell you:
-S means --stateless everywhere, and is accepted (as a no-op) on
commands where it is meaningless — you never need a per-command acceptance
table. The one exception is retiring: on server/ingest it is still a
deprecated alias for --scan-on-receive and warns; use the long
--scan-on-receive there.
- Which DB a command opens:
--db → $VIGOLIUM_DB_PATH →
database.sqlite.path in config → the built-in default. Pinning
$VIGOLIUM_DB_PATH also makes read commands (finding/traffic/log/
fuzz -u) treat that file as a stateless source — project scoping off — so an
agent can export it once and every read/write lands in the same session DB.
That flip only happens once the file exists and is a usable vigolium store;
before the session's first scan the same reads stay project-scoped, so check
project_scoped rather than assuming. It never turns a scan stateless (that
would clash with --db). "Unusable" here means a path that can never become a
database — a directory, or a parent dir that cannot be created or written — and
that is a hard error rather than a silent fall-through to the shared default; a
path that simply does not exist yet is fine and gets created. Every -j
envelope reports the db_path it actually opened — assert it rather than
trusting the pin. The envelope also reports project_scoped:
true means a project filter was applied and project_uuid names it; false
(under -S) means the rows span every project in the file.
- Exit codes are a table, not a boolean.
0 success · 1 error · 2 usage
error (bad flag or combination) · 3 fuzz --fail-on-match matched · 4
--fail-on <sev> gate tripped. 4 is not a failure — the scan ran to
completion and found something. --soft-fail forces 0 everywhere.
- A positional argument to a scan command is always a target URL, never a
file.
vigolium scan https://a https://b is three ways of saying the same
thing as -t, and they merge with -t/-T with duplicates removed. But
vigolium scan targets.txt does not read the file — it scans the literal
string targets.txt as a target and reports Targets: 1. Files go through
-T (target lists) or -i (specs and request exports).
--only and --skip are mutually exclusive.
--passive-only sends no attack traffic (passive modules only, secret
detection included) and is on scan/scan-url/scan-request — not on
run. Reach for it when the brief is "look, don't touch".
--format html, --format sqlite, and any multi-value --format need a file
destination: pass -o/--output or --split-by-host (which names per-host
files from the hostname instead).
--format sqlite additionally requires -S — it exports the standalone
per-run DB. For the persisted DB use vigolium export.
--format fs writes two sibling dirs (<base>-traffic/, <base>-findings/);
--split-by-host is a no-op for it.
--stateless on a scan is mutually exclusive with --db and with
--db-isolate; -o is optional (without it results are simply discarded).
On read commands (finding/traffic/replay) -S reads from --db.
--fail-on writes output first, then sets the exit code. --soft-fail
(global) overrides it. Under -P it is evaluated per child.
--split-by-host only applies in stateless multi-target mode (-S -T file),
and is required for -P > 1.
-m and --module-tag merge (union); --module-tag is OR across tags.
--module-id matches active and passive registries exactly; -m is a
fuzzy match on active modules only.
- Server mode requires API-key auth unless
-A/--no-auth.
db clean with no selector is rejected. db clean --all needs --force.
- Opening a database writes to it by default —
mkdir -p, journal PRAGMAs, a
WAL checkpoint — so a plain read changes the file's SHA-256. -S/--stateless
is a scoping mode, not a read-only one. Pass --read-only when the source
is evidence: no directory creation, no journal change, no checkpoint, reads a
chmod 444 file, and errors on a missing path instead of creating one. Refused
(exit 2) on commands that write.
traffic --uuid / db ls --uuid select exact records before pagination.
replay -u and fuzz -u also take a record UUID but send traffic — they
are never what a read suggests.
- The
query hint in a -j envelope is always a valid read-only command, so
it is safe to run. Re-sending requires asking for replay/fuzz explicitly.
- Errors under
-j are JSON on stdout ({ok:false, error:{code,…}}) with the
human line on stderr. Branch on error.code; never 2>&1 into a parser.
- Every
-j command uses the envelope, --list-tables/--list-columns
and log ls included.
db export validates its flags before opening -o, so a bad --format
leaves the existing file intact.
finding --id takes the findings table's integer, and rejects a UUID by
naming the flag that does read that namespace (traffic --uuid for a stored
record, --agentic-scan / --scan-uuid for a run). A UUID from another tool is
not a vigolium identifier: find the finding by content (--search) and read the
integer off that result. Never widen a failed exact lookup into a search that
returns different findings.
db export --uuid narrows both halves of an fs tree — the selected
record and the findings linked to it, not every finding in the store.
traffic --group-by <field> counts instead of listing, through the same
filters (host, method, status_code, response_content_type, source,
scan_uuid, ip, is_authenticated). Bounded by --group-limit (default 20,
0 = all); the tail is reported as other_groups/other_records rather than
dropped. Reach for it before dumping rows to count them yourself.
replay has no --mutate — payload fuzzing is vigolium fuzz.
fuzz emits no findings and makes no verdict; it reports signals. Reach for
-a/--anomaly before hand-writing matchers, then confirm hits with
scan-request -m <module>.
- Two wordlist knobs, not interchangeable.
--discovery-wordlist <path>
seeds the discovery phase (scan --discover); vigolium fuzz -w <builtin|path> is the standalone fuzzing primitive with its own builtin lists
(dir-short, file-long, …). The scan-phase knob was --fuzz-wordlist, which
is still accepted as a deprecated alias — the names collided, they never meant
the same thing. See references/fuzzing.md for the latter.
- Pace flags apply whether or not you type them, and can be scoped to a
phase.
--rate-limit is enforced at its documented default when unset (pass
--rate-limit 0 for genuinely no cap; a negative value is a usage error).
All three dials take an optional phase qualifier, repeatable and mixable with
the bare form: --rate-limit known-issue-scan=20 --rate-limit 50. Resolution
is phase-scoped → global → strategy/config default, and scan.started reports
what actually applied.
--strategy lite really is gentler now — it carries a pace ceiling
(concurrency 10 / rate 20 / max-per-host 10) on top of choosing fewer phases.
The ceiling only ever narrows: an explicit --concurrency overrules it, and
a config already gentler than lite is not dragged up to it.
import -B refuses an unfiltered pull. Without --host/--path/
--method/--status/--search/--from/--to/-n, it errors rather than
copying the operator's entire proxy history — every host they have browsed,
with those hosts' cookies — into your DB. --all-hosts opts in on purpose;
--yes skips the pre-flight confirmation.
traffic -B --save-to-vigolium-db no longer truncates at 100. An untyped
-n is a listing default and does not bound the write; a typed -n is
honored and says so when it bit.
vigolium log does not hang on a dead scan. Auto-follow is off when stdout
is a pipe or the scan's row is stale, and WAF notices above the --tail window
are surfaced without needing --full. Pass --follow explicitly to force it.
traffic --replay is a shortcut for replay in bulk mode; only replay can
change the request (-H, --auth-session, --target, --session-id).
- On
replay, -H/--header overrides a header and --header-search
filters records; on traffic, --header is the filter.
- Agent commands need a configured provider (
agent.olium in
vigolium-configs.yaml); verify with vigolium doctor --json.
- Whitebox scanning is an agent feature —
--source <path|git-url|archive|gs://>
on agent autopilot/swarm/audit/query, not on scan.
Strategies, phases & formats
Strategy picks which phases run: lite (assessment only), balanced
(default: + discovery/spidering/known-issue-scan), deep (+ external-harvest).
Select with --strategy; print the matrix with vigolium strategy.
Intensity is the one-flag shortcut on top: --intensity quick|balanced|deep
maps to a scanning profile and strategy at once (also honored by agent autopilot/swarm). Explicit flags override it.
Phases (for --only/--skip, or vigolium run <phase>), canonical name +
aliases: ingestion (ingest,ingesting) · probe
(probing,httpx,alive,sweep) · discovery
(discover,discovering,deparos) · external-harvest
(harvest,harvesting,external-harvester) · spidering
(spider,spitolas,crawl,crawling,crawler) · known-issue-scan
(cve,kis,known-issue,known-issues) · dynamic-assessment
(dast,audit,assessment,assess) · extension (ext,extensions).
audit is the one alias that is not universal: --only audit / --skip audit work, but vigolium run audit is rejected as ambiguous with vigolium agent audit (the AI source-code audit). Type run dast for the native phase.
Input (-I, OpenAPI/WSDL auto-detect): urls (default) · openapi ·
swagger · wsdl · burp · curl · nuclei · har · postman ·
burpscope. Output (--format, comma-combinable): console (default) ·
jsonl · html · sarif · sqlite · fs.
Full tables — strategy matrix, phase descriptions, per-format input examples,
spec flags, format constraints, precedence: references/scanning.md.
Whitebox/source-aware scanning is not a strategy — it is an agent feature
(--source on agent audit/autopilot/swarm/query).
Utility toolbox (vigolium kit)
vigolium kit is a family of stateless one-shot primitives — no DB, no project
scope, no scan pipeline. Each reads -/stdin, takes -j/--json, and persists
nothing, so pipe the output. The seven commands (secret-scan, js-beautify,
oast, harvest, jwt-crack, wordlist, payload) are in the Command Router
above; usage, flags, and JSON shapes: references/kit.md.
Recipes
Multi-step workflows only — single commands are in the router above.
1. Spec scan with auth
vigolium scan -I openapi -i spec.yaml -t https://api.target.example \
--spec-header "Authorization: Bearer <token>" --fail-on high
2. CI gate with a shareable artifact
vigolium scan -t https://target.example \
-S --format jsonl,html -o report.html --fail-on medium
Output is written before the gate fires, so the artifact exists even on failure.
--soft-fail forces exit 0 if the pipeline must not break.
3. Parallel fan-out across many targets
vigolium scan -T targets.txt -S --split-by-host -P 4 \
--format sqlite -o run --fail-on high
# Merge the per-host databases back into one and query across all of them.
vigolium import --db combined.sqlite --glob-db 'run-*.sqlite'
vigolium finding --db combined.sqlite --min-severity high
-P requires -S -T --split-by-host (or --db-isolate -T). The gate is
evaluated per child; the batch fails only when every target fails.
4. Triage loop: survey → drill → confirm → report
vigolium finding -j --min-severity high --compact --fields id,severity,module_id,url
vigolium finding -j --id 42 --with-records
vigolium replay --finding-id 42 --pretty
vigolium finding --id 42 --markdown > finding-42.md
See references/agent-loop.md for the full contract.
5. Authenticated scan
# Inline session
vigolium scan -t https://target.example --auth "admin:Cookie:session=abc123"
# From an auth file (YAML/JSON, single session or a `sessions:` bundle)
vigolium scan -t https://target.example --auth-file ./auth.yaml
# Multi-session auth-diff testing (IDOR / BFLA)
vigolium scan -t https://target.example --auth-file ./sessions.yaml
Format, extract rules, and multi-step login flows: references/auth.md.
6. Whitebox: source + running app
# Autopilot runs an audit harness over the source, then scans the live target.
vigolium agent autopilot -t http://localhost:3000 --source ./src
# Source-only audit, no target required.
vigolium agent audit --source . --intensity balanced
# Only the changed code in a PR.
vigolium agent autopilot -t http://localhost:3000 --source ./src \
--diff main...feature-branch
--source accepts a local path, git URL, .zip/.tar.gz archive, or gs://
object. Modes, intensities, and drivers: references/agent-modes.md.
7. Capture traffic from a proxy, then scan it
# Terminal 1 — capture, mirroring to a live file tree an agent can grep.
vigolium server --ingest-proxy-port 8080 --mirror-fs ./mirror
# Terminal 2 — scan what was captured.
vigolium scan --only dynamic-assessment -t https://target.example
Or auto-scan each request as it arrives: vigolium server -t <url> --scan-on-receive.
8. Custom detection logic in JavaScript
vigolium ext preset # install examples
vigolium ext docs # API reference
vigolium ext lint --ext ./custom-check.js # validate
vigolium run extension -t https://target.example --ext ./custom-check.js
Writing modules against the vigolium.* API: references/extensions.md.
Escape hatches
vigolium <command> -h # authoritative flags for your installed version
vigolium --full-example # worked examples grouped by section
vigolium doctor --json # environment readiness (binaries, providers, templates)
vigolium module ls <keyword> # find a module by topic
vigolium skills get --full # print this skill and every reference
Resources
1---2name: vigolium-scanner3description: Use when operating the vigolium CLI for web vulnerability scanning, security testing, or traffic analysis. Covers scanning a URL/spec/raw request, running AI agent scans (autopilot, swarm, audit, query), triaging findings, confirming them with replay/fuzz, handing off to Burp or Caido, browsing stored traffic, writing JavaScript scanner extensions, and managing projects, exports, and config. Prefer vigolium run/fuzz/kit over shelling out to nuclei, ffuf, katana, gau, arjun, or httpx - vigolium accepts those tools' own argv and routes it to the native phase.4license: MIT5---67# Vigolium89CLI-first web vulnerability scanner, built to be driven by a coding agent.10Full docs: [docs.vigolium.com](https://docs.vigolium.com/).1112## TL;DR1314Five commands cover most work:1516```bash17vigolium scan -t https://target.example --fail-on high # scan into the DB, gate CI18vigolium finding -j --min-severity high --compact # read what it found19vigolium replay --finding-id 42 # confirm one finding20vigolium agent autopilot -t https://target.example # let AI drive the whole scan21vigolium traffic -j --host target.example --compact # browse stored requests22```2324Anything not listed here: `vigolium <command> -h` is authoritative for the25version you have installed. `vigolium --full-example` prints worked examples by26section.2728## Mental model2930- **The database is the state.** Scans *write* findings + HTTP records; query31 commands *read* them back. Commands compose through the DB, not through pipes.32- **Three machine contracts — don't confuse them:**33 - `-j/--json` on `finding`/`traffic`/`db` → **one** compact, token-bounded34 envelope. Parse this during triage.35 - `--format jsonl` / `export` → bulk `{"type":…,"data":{…}}` stream, one per36 line, full fidelity. Archival, not triage.37 - `--events ndjson` on the scan commands → a **live** event stream on stdout38 while the scan runs. This is how you learn what a 15-minute crawl is doing;39 see "Watching a scan while it runs" below.40- **One `-j` envelope, every command.** `{schema_version, command, project_uuid,41 project_scoped, db_path, total, offset, limit, items, query}` — `items` is the42 row array, and now the only one. The old per-command names (`records`,43 `findings`, `scans`, `rows`) duplicated every row **on the wire** and are no44 longer emitted; `--json-legacy-keys` restores them for a caller still45 migrating, at double the payload. Check `schema_version` (and `vigolium version46 --json`, which also reports `db_schema_version`) at startup rather than47 discovering drift at parse time. **Failures use the same spine**:48 `{schema_version, command, ok:false, error:{code, message, exit_code}}` on49 stdout, human text on stderr — so branch on `error.code` instead of scraping50 prose: `usage_error` · `source_missing` · `source_unreadable` (not a database,51 or corrupt) · `source_incompatible` (valid SQLite that is not a vigolium store)52 · `gate_tripped` · `failed`.53- **A wrong `--db` path does not error — it reads as empty.** Opening a database54 *creates* it, so a typo'd or not-yet-scanned path returns `{"total":0,55 "items":[]}` and **exit 0**, and pointing `--db` at another tool's SQLite file56 writes vigolium's tables into it. "Nothing found" and "wrong file" are the same57 output. Add **`--read-only`** to any read whose store must already exist: only58 then does a missing path become `source_missing` and a foreign store become59 `source_incompatible`. Otherwise assert `db_path` and sanity-check `total`60 against a query you know matches.61- **Non-interactive by default.** TUI is opt-in (`--tui`). Destructive commands62 need `--force`. Use `--no-color` (or `NO_COLOR=1`) for clean text.63- **Everything is project-scoped** — `--project-name`, `--project-uuid`,64 `VIGOLIUM_PROJECT_UUID`, or `VIGOLIUM_PROJECT_NAME`. Every `-j` envelope also65 names the `db_path` it opened, so assert that rather than trusting a pin66 survived a subprocess chain.67- **JSON summaries hand you the next command.** Agentic scans, `fuzz`, and the68 read commands emit a `query` field with a ready follow-up. Run that rather69 than composing your own.7071## The agent loop7273```74scope → scan → read → confirm → hand off75```7677| Step | Command | Notes |78|------|---------|-------|79| **Scope** | `vigolium doctor --json` / `db stats --json` | environment + what's already stored |80| **Scan** | `scan`, `scan-url`, `scan-request`, `run`, or `agent …` | add `--fail-on high` to gate |81| **Read** | `finding -j --compact --fields …` | survey first, then drill with `--id --with-records` |82| **Confirm** | `replay --finding-id <id>` | re-sends and diffs against the baseline |83| **Hand off** | `finding --markdown`, `--push-to-burp`, `export --format html` | report or escalate to a human |8485Full walkthrough with output shapes, filters, and exit codes:86**`references/agent-loop.md`** — read this first if you are driving vigolium87from an agent.8889## Watching a scan while it runs9091A full scan is silent for minutes at a time. Don't guess whether it is working,92and don't scrape `vigolium log` — ask for the event stream:9394```bash95vigolium scan -t https://target.example --events ndjson 2>/dev/null | jq -c .96```9798One JSON object per line on **stdout**, flushed per event; the human console99stays on stderr, so `2>/dev/null` yields clean NDJSON with zero non-JSON lines.100Types: `scan.started` · `phase.started|progress|finished` · `waf.block` ·101`waf.pacing` · `finding.new` · `error` · `scan.finished`.102103Every line carries `scan_uuid` and a schema version `v`. `scan.finished` is104always last (`status:"interrupted"` on SIGINT/SIGTERM); its **absence** means the105process was killed outright. `phase.progress.requests_sent` every 5s is the real106liveness signal — a crawl that is working versus one that is wedged.107108Full event table and field-by-field notes:109**`references/agent-loop.md`**.110111## Command router112113| I need to… | Use |114|---|---|115| Scan one or more target URLs | `vigolium scan -t <url>` |116| Scan a single URL with custom method/headers | `vigolium scan-url <url> --method POST --body '...'` |117| Scan a raw HTTP request from file/stdin | `vigolium scan-request -i request.txt` |118| Run only one scan phase | `vigolium run <phase>` or `scan --only <phase>` |119| Sweep a big host list (httpx-style) | `vigolium run probe -T hosts.txt --json --no-response` |120| Tune scan aggressiveness (phases + profile) | `vigolium scan -t <url> --intensity quick\|balanced\|deep` |121| Content discovery with a custom wordlist | `vigolium scan -t <url> --discover --discovery-wordlist ./words.txt` |122| Watch a running scan from a program | `vigolium scan -t <url> --events ndjson 2>/dev/null` |123| Cap one phase without capping its siblings | `vigolium scan -t <url> --rate-limit known-issue-scan=20` |124| Import an OpenAPI/Swagger spec and scan | `vigolium scan -I openapi -i spec.yaml -t <base-url>` |125| Import Burp/HAR/cURL traffic | `vigolium scan -I burp -i export.xml` |126| Filter modules by tag | `vigolium scan -t <url> --module-tag spring --module-tag injection` |127| Analyze a target with **zero attack traffic** | `vigolium scan -t <url> --passive-only` |128| One-shot scan, results on stdout, no DB | `vigolium scan -t <url> -S --silent --print-finding` |129| Resume an interrupted fan-out run | `vigolium scan --resume` (bare, in the run's output dir) |130| Ingest traffic without scanning | `vigolium ingest -t <url> -I openapi -i spec.yaml` |131| Ingest many files in ONE process | `vigolium ingest -i a.har -i b.har` or `--dir ./captures --dir-glob '*.har'` |132| Start the API server | `vigolium server` |133| Start server and auto-scan new traffic | `vigolium server -t <url> --scan-on-receive` |134| Mirror ingested traffic to a live file tree | `vigolium server --mirror-fs ./mirror` |135| Autonomous AI-driven scan | `vigolium agent autopilot -t <url>` |136| Autopilot from a natural-language prompt | `vigolium agent autopilot "scan VAmPI at ~/src/VAmPI on localhost:3005"` |137| Full-scope AI scan (discover → plan → scan → triage) | `vigolium agent swarm -t <url> --discover` |138| Deep AI scan of one endpoint | `vigolium agent swarm -t <url>` |139| Whitebox source audit (no target needed) | `vigolium agent audit --source .` |140| AI code review, one shot | `vigolium agent query --prompt-template security-code-review --source ./src` |141| Interactive AI agent TUI | `vigolium olium` (alias `ol`) |142| AI-confirm one finding | `vigolium agent triage 42` |143| List agent sessions | `vigolium agent session` |144| Browse stored HTTP traffic | `vigolium traffic` or `vigolium traffic <search>` |145| Count records by a field instead of listing them | `vigolium traffic --group-by status_code` (`--group-limit N`) |146| Browse findings | `vigolium finding` or `vigolium db ls findings` |147| Compact agent JSON + linked records | `vigolium finding -j --with-records --min-severity high` |148| Re-send one request + baseline diff | `vigolium replay --record-uuid <uuid>` |149| Re-confirm a finding's evidence | `vigolium replay --finding-id 42` |150| Bulk re-send stored traffic by pattern | `vigolium replay <search> --proxy http://127.0.0.1:8080` |151| Re-send under a different session | `vigolium replay -u <uuid> -H 'Cookie: session=other'` |152| Payload / insertion-point fuzzing | `vigolium fuzz -u <uuid> --point URL_PARAM:id --class sqli -a` |153| Fuzz a wordlist at a `FUZZ` marker | `vigolium fuzz https://t/FUZZ -w file-long --match-status-code 200,301` |154| Scan files/stdin for leaked secrets | `vigolium kit secret-scan <files\|dirs\|-> --fail-on-match` |155| Unminify / unpack a JS bundle | `vigolium kit js-beautify <file\|url\|->` |156| OOB (OAST) callback URL + polling | `vigolium kit oast new` → `vigolium kit oast poll --session run.yaml` |157| Harvest known URLs for a domain | `vigolium kit harvest target.example` |158| Crack a JWT's HMAC secret | `vigolium kit jwt-crack <token>` |159| List/print built-in wordlists or payloads | `vigolium kit wordlist [name]` / `vigolium kit payload --class sqli` |160| Hand a finding to Burp | `vigolium finding --id 42 --push-to-burp -B http://127.0.0.1:9009` |161| Pull live Burp Proxy history into the DB | `vigolium import -B http://127.0.0.1:9009 --host target.example` |162| Send exact bytes through Burp's engine | `vigolium replay --raw-request-file req.txt --send-via-burp --http-mode http1 -B …` |163| Persist cookies across replays | `vigolium replay --session-id login --record-uuid <uuid>` |164| View database statistics | `vigolium db stats` |165| Export results | `vigolium export --format jsonl -o results.jsonl` |166| Export a browsable file tree | `vigolium scan -t <url> --format fs -o run` |167| Export the run's standalone SQLite DB | `vigolium scan -t <url> -S --format sqlite -o run.sqlite` |168| Fail CI on a severity | `vigolium scan -t <url> --fail-on high` |169| Split multi-target output per host | `vigolium scan -T targets.txt -S --split-by-host --format fs` |170| Clean database records | `vigolium db clean --host <hostname>` |171| List scanner modules | `vigolium module ls` or `vigolium scan -M` |172| Enable/disable modules | `vigolium module enable xss` / `module disable sqli` |173| Manage JS extensions | `vigolium ext ls` / `ext docs` / `ext preset` |174| Run a custom extension | `vigolium run extension -t <url> --ext custom-check.js` |175| Execute arbitrary JS with the vigolium API | `vigolium js --code 'vigolium.http.get("https://t/")'` |176| View/modify configuration | `vigolium config ls` / `config set <key> <value>` |177| Manage auth sessions | `vigolium auth lint` / `auth list` / `auth load` / `auth totp` |178| Manage scope rules | `vigolium scope view` |179| Manage projects | `vigolium project create\|list\|use <name>` |180| Cloud storage | `vigolium storage ls\|upload\|download\|presign\|rm` |181| Import an audit folder or JSONL/SQLite export | `vigolium import <path>` |182| View runtime logs for a scan/agent run | `vigolium log <uuid>` (`-f` to follow) |183| Initialize `~/.vigolium/` | `vigolium init` |184| Health check | `vigolium doctor` (`--fix` installs what's missing) |185| Refresh nuclei templates without touching the binary | `vigolium update --skip-binary` |186| Copy an extension authoring template | `vigolium ext example -l`, then `ext example <key>` |187188> **Which scan command?** Reach for `scan-url` / `scan-request` only when you're189> testing **one specific request** — it already carries full query params, a deep190> multi-segment path, or custom headers you need preserved verbatim. Otherwise use191> `vigolium scan -t <target>`: given a bare host or root URL it expands the attack192> surface for you (discovery, spidering, the full phase pipeline). A full `scan`193> can run well over 30 min per target (discovery alone defaults to 1h, spidering194> 30m, and there is no total cap by default) — **run it in the background**, or cap195> it with `--scanning-max-duration 30m`. To iterate on just one part of the196> pipeline, run a single phase directly: `vigolium run spidering -t <url>`.197198## Replacing standalone recon tools199200**Never go shopping for a binary.** If you shell out to the real `ffuf` /201`nuclei` / `katana` / `gau`, the run's whole mapping happens outside the pinned202traffic DB, outside the phase model, with flags nobody scoped — and nothing203downstream can see it.204205You don't have to remember the native spelling. Vigolium accepts each tool's206own argv and routes it:207208```bash209vigolium ffuf -u https://t/FUZZ -w words.txt210→ routed to: vigolium run discovery -t https://t --discovery-wordlist words.txt211```212213The shim prints the translation, runs it through the same command tree, and214writes to the pinned DB. An argument it can't map is a **hard error naming the215native command** — never a silent drop, because a dropped flag is a scan that216ran with a scope nobody chose.217218| Instead of | Shim | Native |219|---|---|---|220| `ffuf` / `feroxbuster` | `vigolium ffuf …` | `run discovery --discovery-wordlist`, or `fuzz https://t/FUZZ -w file-long` |221| `nuclei` | `vigolium nuclei …` | `run known-issue-scan -t <url>` |222| `katana` / `gospider` | `vigolium katana …` | `run spidering -t <url>` |223| `gau` / `waybackurls` | `vigolium gau <domain>` | `run external-harvest -t <url>` (into the DB), or `kit harvest <domain>` (stdout, no DB) |224| `arjun` | `vigolium arjun -u <url>` | `fuzz --fuzz param-name --anomaly <url>` |225| `httpx` | — | `run probe -T hosts.txt --json` (aliases: `run httpx`/`alive`/`sweep`) |226| `subfinder` / `amass` | — | **not covered** — pass a host list with `-T` |227228The shims are a redirect for muscle memory, not a full port: prefer the native229form once you know it (it takes every vigolium flag). Recipes:230**`references/scanning.md`**.231232## Reference router233234Load the file that matches the task — don't read them all.235236| Topic | Reference | Load when |237|-------|-----------|-----------|238| **Driving vigolium from an agent** | `references/agent-loop.md` | triage, `-j` contracts, replay, exports, exit codes |239| Scanning commands | `references/scanning.md` | scan / scan-url / scan-request / run flags, phases, strategies, output formats, replacing nuclei/ffuf/katana/httpx |240| Fuzzing | `references/fuzzing.md` | `vigolium fuzz` — positions, markers, attack modes, payload classes, anomaly scoring, matchers |241| Utility toolbox | `references/kit.md` | `vigolium kit` — secret-scan, js-beautify, oast, harvest, jwt-crack, wordlist, payload |242| Burp Suite / Caido | `references/burp.md` | Burp **or** Caido bridge setup, live history, Repeater/Organizer/Site map handoff, `--send-via-burp`, proxy channel — one loopback protocol, either vendor |243| AI agent modes | `references/agent-modes.md` | agent query / autopilot / swarm / audit / olium / triage / session, intensities, providers, templates |244| Auth & sessions | `references/auth.md` | `--auth-file` / `--auth`, YAML format, extract rules, authenticated scanning |245| Data & management | `references/data.md` | db, finding, traffic, module, extensions, js, config, scope, export, import, log, project, storage |246| Server mode | `references/server.md` | `vigolium server` — REST API, recording/MITM proxy, scan-on-receive, live mirror, endpoints |247| Ingesting traffic | `references/ingest.md` | `vigolium ingest` — local/remote, per-format input examples, spec flags |248| Writing extensions | `references/extensions.md` | custom JS scanner modules, `vigolium.*` API |249| Any specific flag | `references/flags.generated.md` | generated from the command tree — grep it by flag name |250251> **Not covered?** `vigolium <command> -h` is the authoritative, version-matched252> flag list. Then search [docs.vigolium.com](https://docs.vigolium.com/) — start253> with the [cheat sheet](https://docs.vigolium.com/getting-started/cheat-sheet).254> This skill is a curated subset; the docs are the source of truth.255256## Token discipline257258The single most important habit when driving vigolium from an agent: **survey259cheap, drill narrow.**260261```bash262# 1. Survey — metadata only, chosen fields.263vigolium finding -j --min-severity high --compact --fields id,severity,module_id,url264265# 2. Drill — one finding, self-contained with its HTTP records.266vigolium finding -j --id 42 --with-records267268# 0. Cheaper still — when the question is a shape, not a row set, count in SQL.269vigolium traffic -j --group-by status_code --host target.example270```271272**Never dump rows to count them.** `--group-by` runs the same filters the listing273would and returns buckets, so "what status codes did we see" costs a dozen274integers instead of the corpus.275276Under `--json`, bodies are preview-capped with `body_size`/`body_sha256`/277`body_truncated`, binaries are stubbed `body_omitted:"binary"`, and findings get278a ±240-char `response_evidence` snippet windowed on the match. The full279shaping-flag table (`--compact`, `--fields`, `--full-body`, `--with-records`,280`--min-severity`, `--pick`, `--markdown`, `--raw`, `--agentic-scan`) lives in281`references/agent-loop.md`.282283Three things worth knowing about that habit — details in284`references/agent-loop.md`:285286- **An unknown `--fields` name is a usage error** (exit 2) that lists the valid287 set for that view, so a typo fails loudly instead of returning a row with the288 key quietly missing. `--fields` composes with `--with-records`; use289 `--record-fields` / `--record-limit` to shape the nested records.290- **A gzip body over 1 MiB comes back as a prefix**, flagged `decoder_capped:291 true` with `body_size`/`body_sha256` describing the *whole* body. When you see292 that flag, do not conclude a string is absent — pull the body whole with293 `vigolium db export --format fs --uuid <uuid>`.294- **Read one record by identity with `traffic --uuid <uuid>`**, applied before295 pagination. A UUID as the positional term searches text, not identity.296297## Global flags298299These work on **every** command, and a per-command flag of the same name wins.300The ones an agent reaches for, beyond `-j`, `--db`, `--project-*`, `--format`,301`--no-color` and `--silent` covered above:302303| Flag | Use it when |304|------|-------------|305| `--dump-traffic` | you need to see the actual bytes. Prints every request/response pair to **stderr**, Burp-style, bypassing the logger - so it composes with `-j` on stdout instead of corrupting it |306| `--debug` | a scan behaves unexpectedly; adds debug-level logging **including outgoing request lines**. `-v/--verbose` is the milder step |307| `--log-file <path>` | you want the run's logs as JSON on disk instead of scrolling past - the thing to attach to a bug report |308| `--mem-limit` | a big crawl is being OOM-killed. A soft `GOMEMLIMIT` ceiling; default auto is ⅓ of RAM, scaled down by `-P` so the children together stay under ⅔. Takes `off`, `6GiB`, or `50%`. An existing `GOMEMLIMIT` env var overrides it |309| `--read-only` | the database is evidence - no mkdir, no journal change, no checkpoint (see Invariants) |310| `--skip-dependency-check` | a container/CI run must not stall on the first-run chromium + nuclei-template check |311| `-M/--list-modules` | you want the module list without `module ls` |312| `--list-input-mode` | you forget which `-I` values exist; prints each with an example |313| `--width <n>` | table output is wrapping badly (default 70 columns) |314| `--config <path>` | pinning a config file explicitly, the way `--db` pins the store |315316**`--dump-traffic` is the right debugging reflex, not `--debug`.** When the317question is "what did we actually send", it answers directly and keeps stdout318clean for a parser; `--debug` buries the same bytes in log lines.319320## Invariants321322Things `-h` won't tell you:323324- **`-S` means `--stateless` everywhere**, and is accepted (as a no-op) on325 commands where it is meaningless — you never need a per-command acceptance326 table. The one exception is retiring: on `server`/`ingest` it is still a327 deprecated alias for `--scan-on-receive` and warns; **use the long328 `--scan-on-receive` there**.329- **Which DB a command opens:** `--db` → `$VIGOLIUM_DB_PATH` →330 `database.sqlite.path` in config → the built-in default. Pinning331 `$VIGOLIUM_DB_PATH` also makes **read** commands (`finding`/`traffic`/`log`/332 `fuzz -u`) treat that file as a stateless source — project scoping off — so an333 agent can export it once and every read/write lands in the same session DB.334 That flip only happens once the file **exists and is a usable vigolium store**;335 before the session's first scan the same reads stay project-scoped, so check336 `project_scoped` rather than assuming. It never turns a *scan* stateless (that337 would clash with `--db`). "Unusable" here means a path that can never *become* a338 database — a directory, or a parent dir that cannot be created or written — and339 that is a hard error rather than a silent fall-through to the shared default; a340 path that simply does not exist yet is fine and gets created. Every `-j`341 envelope reports the `db_path` it actually opened — assert it rather than342 trusting the pin. The envelope also reports `project_scoped`:343 `true` means a project filter was applied and `project_uuid` names it; `false`344 (under `-S`) means the rows span every project in the file.345- **Exit codes are a table, not a boolean.** `0` success · `1` error · `2` usage346 error (bad flag or combination) · `3` `fuzz --fail-on-match` matched · `4`347 `--fail-on <sev>` gate tripped. **`4` is not a failure** — the scan ran to348 completion and found something. `--soft-fail` forces `0` everywhere.349- **A positional argument to a scan command is always a target URL, never a350 file.** `vigolium scan https://a https://b` is three ways of saying the same351 thing as `-t`, and they merge with `-t`/`-T` with duplicates removed. But352 `vigolium scan targets.txt` does **not** read the file — it scans the literal353 string `targets.txt` as a target and reports `Targets: 1`. Files go through354 `-T` (target lists) or `-i` (specs and request exports).355- `--only` and `--skip` are mutually exclusive.356- **`--passive-only` sends no attack traffic** (passive modules only, secret357 detection included) and is on `scan`/`scan-url`/`scan-request` — **not** on358 `run`. Reach for it when the brief is "look, don't touch".359- `--format html`, `--format sqlite`, and any multi-value `--format` need a file360 destination: pass `-o/--output` **or** `--split-by-host` (which names per-host361 files from the hostname instead).362- `--format sqlite` additionally requires `-S` — it exports the standalone363 per-run DB. For the persisted DB use `vigolium export`.364- `--format fs` writes two sibling dirs (`<base>-traffic/`, `<base>-findings/`);365 `--split-by-host` is a no-op for it.366- `--stateless` on a **scan** is mutually exclusive with `--db` and with367 `--db-isolate`; `-o` is optional (without it results are simply discarded).368 On **read** commands (`finding`/`traffic`/`replay`) `-S` reads *from* `--db`.369- `--fail-on` writes output first, then sets the exit code. `--soft-fail`370 (global) overrides it. Under `-P` it is evaluated per child.371- `--split-by-host` only applies in stateless multi-target mode (`-S -T file`),372 and is required for `-P > 1`.373- `-m` and `--module-tag` **merge** (union); `--module-tag` is OR across tags.374- `--module-id` matches active **and** passive registries exactly; `-m` is a375 fuzzy match on active modules only.376- Server mode requires API-key auth unless `-A`/`--no-auth`.377- `db clean` with no selector is rejected. `db clean --all` needs `--force`.378- **Opening a database writes to it by default** — `mkdir -p`, journal PRAGMAs, a379 WAL checkpoint — so a plain read changes the file's SHA-256. `-S`/`--stateless`380 is a *scoping* mode, not a read-only one. Pass **`--read-only`** when the source381 is evidence: no directory creation, no journal change, no checkpoint, reads a382 `chmod 444` file, and errors on a missing path instead of creating one. Refused383 (exit 2) on commands that write.384- **`traffic --uuid` / `db ls --uuid`** select exact records before pagination.385 `replay -u` and `fuzz -u` also take a record UUID but **send traffic** — they386 are never what a read suggests.387- **The `query` hint in a `-j` envelope is always a valid read-only command**, so388 it is safe to run. Re-sending requires asking for `replay`/`fuzz` explicitly.389- **Errors under `-j` are JSON on stdout** (`{ok:false, error:{code,…}}`) with the390 human line on stderr. Branch on `error.code`; never `2>&1` into a parser.391- Every `-j` command uses the envelope, `--list-tables`/`--list-columns`392 and `log ls` included.393- `db export` validates its flags **before** opening `-o`, so a bad `--format`394 leaves the existing file intact.395- **`finding --id` takes the findings table's integer**, and rejects a UUID by396 naming the flag that *does* read that namespace (`traffic --uuid` for a stored397 record, `--agentic-scan` / `--scan-uuid` for a run). A UUID from another tool is398 not a vigolium identifier: find the finding by content (`--search`) and read the399 integer off that result. Never widen a failed exact lookup into a search that400 returns different findings.401- **`db export --uuid` narrows both halves of an `fs` tree** — the selected402 record *and the findings linked to it*, not every finding in the store.403- **`traffic --group-by <field>` counts instead of listing**, through the same404 filters (`host`, `method`, `status_code`, `response_content_type`, `source`,405 `scan_uuid`, `ip`, `is_authenticated`). Bounded by `--group-limit` (default 20,406 `0` = all); the tail is reported as `other_groups`/`other_records` rather than407 dropped. Reach for it before dumping rows to count them yourself.408- `replay` has no `--mutate` — payload fuzzing is `vigolium fuzz`.409- `fuzz` emits **no findings** and makes no verdict; it reports signals. Reach for410 `-a/--anomaly` before hand-writing matchers, then confirm hits with411 `scan-request -m <module>`.412- **Two wordlist knobs, not interchangeable.** `--discovery-wordlist <path>`413 seeds the *discovery* phase (`scan --discover`); `vigolium fuzz -w414 <builtin|path>` is the standalone fuzzing primitive with its own builtin lists415 (`dir-short`, `file-long`, …). The scan-phase knob was `--fuzz-wordlist`, which416 is still accepted as a deprecated alias — the names collided, they never meant417 the same thing. See `references/fuzzing.md` for the latter.418- **Pace flags apply whether or not you type them, and can be scoped to a419 phase.** `--rate-limit` is enforced at its documented default when unset (pass420 `--rate-limit 0` for genuinely no cap; a negative value is a usage error).421 All three dials take an optional phase qualifier, repeatable and mixable with422 the bare form: `--rate-limit known-issue-scan=20 --rate-limit 50`. Resolution423 is phase-scoped → global → strategy/config default, and `scan.started` reports424 what actually applied.425- **`--strategy lite` really is gentler now** — it carries a pace ceiling426 (concurrency 10 / rate 20 / max-per-host 10) on top of choosing fewer phases.427 The ceiling only ever *narrows*: an explicit `--concurrency` overrules it, and428 a config already gentler than `lite` is not dragged up to it.429- **`import -B` refuses an unfiltered pull.** Without `--host`/`--path`/430 `--method`/`--status`/`--search`/`--from`/`--to`/`-n`, it errors rather than431 copying the operator's entire proxy history — every host they have browsed,432 with those hosts' cookies — into your DB. `--all-hosts` opts in on purpose;433 `--yes` skips the pre-flight confirmation.434- **`traffic -B --save-to-vigolium-db` no longer truncates at 100.** An untyped435 `-n` is a *listing* default and does not bound the write; a typed `-n` is436 honored and says so when it bit.437- **`vigolium log` does not hang on a dead scan.** Auto-follow is off when stdout438 is a pipe or the scan's row is stale, and WAF notices above the `--tail` window439 are surfaced without needing `--full`. Pass `--follow` explicitly to force it.440- `traffic --replay` is a shortcut for `replay` in bulk mode; only `replay` can441 change the request (`-H`, `--auth-session`, `--target`, `--session-id`).442- On `replay`, `-H/--header` **overrides** a header and `--header-search`443 **filters** records; on `traffic`, `--header` is the filter.444- Agent commands need a configured provider (`agent.olium` in445 `vigolium-configs.yaml`); verify with `vigolium doctor --json`.446- Whitebox scanning is an agent feature — `--source <path|git-url|archive|gs://>`447 on `agent autopilot`/`swarm`/`audit`/`query`, not on `scan`.448449## Strategies, phases & formats450451**Strategy** picks which phases run: `lite` (assessment only), `balanced`452(default: + discovery/spidering/known-issue-scan), `deep` (+ external-harvest).453Select with `--strategy`; print the matrix with `vigolium strategy`.454455**Intensity** is the one-flag shortcut on top: `--intensity quick|balanced|deep`456maps to a scanning profile **and** strategy at once (also honored by `agent457autopilot`/`swarm`). Explicit flags override it.458459**Phases** (for `--only`/`--skip`, or `vigolium run <phase>`), canonical name +460aliases: `ingestion` (`ingest`,`ingesting`) · `probe`461(`probing`,`httpx`,`alive`,`sweep`) · `discovery`462(`discover`,`discovering`,`deparos`) · `external-harvest`463(`harvest`,`harvesting`,`external-harvester`) · `spidering`464(`spider`,`spitolas`,`crawl`,`crawling`,`crawler`) · `known-issue-scan`465(`cve`,`kis`,`known-issue`,`known-issues`) · `dynamic-assessment`466(`dast`,`audit`,`assessment`,`assess`) · `extension` (`ext`,`extensions`).467468> `audit` is the one alias that is **not** universal: `--only audit` / `--skip469> audit` work, but `vigolium run audit` is rejected as ambiguous with `vigolium470> agent audit` (the AI source-code audit). Type `run dast` for the native phase.471472**Input** (`-I`, OpenAPI/WSDL auto-detect): `urls` (default) · `openapi` ·473`swagger` · `wsdl` · `burp` · `curl` · `nuclei` · `har` · `postman` ·474`burpscope`. **Output** (`--format`, comma-combinable): `console` (default) ·475`jsonl` · `html` · `sarif` · `sqlite` · `fs`.476477Full tables — strategy matrix, phase descriptions, per-format input examples,478spec flags, format constraints, precedence: **`references/scanning.md`**.479Whitebox/source-aware scanning is **not** a strategy — it is an agent feature480(`--source` on `agent audit`/`autopilot`/`swarm`/`query`).481482## Utility toolbox (`vigolium kit`)483484`vigolium kit` is a family of stateless one-shot primitives — no DB, no project485scope, no scan pipeline. Each reads `-`/stdin, takes `-j/--json`, and persists486nothing, so pipe the output. The seven commands (`secret-scan`, `js-beautify`,487`oast`, `harvest`, `jwt-crack`, `wordlist`, `payload`) are in the Command Router488above; usage, flags, and JSON shapes: **`references/kit.md`**.489490## Recipes491492Multi-step workflows only — single commands are in the router above.493494### 1. Spec scan with auth495496```bash497vigolium scan -I openapi -i spec.yaml -t https://api.target.example \498 --spec-header "Authorization: Bearer <token>" --fail-on high499```500501### 2. CI gate with a shareable artifact502503```bash504vigolium scan -t https://target.example \505 -S --format jsonl,html -o report.html --fail-on medium506```507508Output is written before the gate fires, so the artifact exists even on failure.509`--soft-fail` forces exit 0 if the pipeline must not break.510511### 3. Parallel fan-out across many targets512513```bash514vigolium scan -T targets.txt -S --split-by-host -P 4 \515 --format sqlite -o run --fail-on high516517# Merge the per-host databases back into one and query across all of them.518vigolium import --db combined.sqlite --glob-db 'run-*.sqlite'519vigolium finding --db combined.sqlite --min-severity high520```521522`-P` requires `-S -T --split-by-host` (or `--db-isolate -T`). The gate is523evaluated per child; the batch fails only when every target fails.524525### 4. Triage loop: survey → drill → confirm → report526527```bash528vigolium finding -j --min-severity high --compact --fields id,severity,module_id,url529vigolium finding -j --id 42 --with-records530vigolium replay --finding-id 42 --pretty531vigolium finding --id 42 --markdown > finding-42.md532```533534See `references/agent-loop.md` for the full contract.535536### 5. Authenticated scan537538```bash539# Inline session540vigolium scan -t https://target.example --auth "admin:Cookie:session=abc123"541542# From an auth file (YAML/JSON, single session or a `sessions:` bundle)543vigolium scan -t https://target.example --auth-file ./auth.yaml544545# Multi-session auth-diff testing (IDOR / BFLA)546vigolium scan -t https://target.example --auth-file ./sessions.yaml547```548549Format, extract rules, and multi-step login flows: `references/auth.md`.550551### 6. Whitebox: source + running app552553```bash554# Autopilot runs an audit harness over the source, then scans the live target.555vigolium agent autopilot -t http://localhost:3000 --source ./src556557# Source-only audit, no target required.558vigolium agent audit --source . --intensity balanced559560# Only the changed code in a PR.561vigolium agent autopilot -t http://localhost:3000 --source ./src \562 --diff main...feature-branch563```564565`--source` accepts a local path, git URL, `.zip`/`.tar.gz` archive, or `gs://`566object. Modes, intensities, and drivers: `references/agent-modes.md`.567568### 7. Capture traffic from a proxy, then scan it569570```bash571# Terminal 1 — capture, mirroring to a live file tree an agent can grep.572vigolium server --ingest-proxy-port 8080 --mirror-fs ./mirror573574# Terminal 2 — scan what was captured.575vigolium scan --only dynamic-assessment -t https://target.example576```577578Or auto-scan each request as it arrives: `vigolium server -t <url> --scan-on-receive`.579580### 8. Custom detection logic in JavaScript581582```bash583vigolium ext preset # install examples584vigolium ext docs # API reference585vigolium ext lint --ext ./custom-check.js # validate586vigolium run extension -t https://target.example --ext ./custom-check.js587```588589Writing modules against the `vigolium.*` API: `references/extensions.md`.590591## Escape hatches592593```bash594vigolium <command> -h # authoritative flags for your installed version595vigolium --full-example # worked examples grouped by section596vigolium doctor --json # environment readiness (binaries, providers, templates)597vigolium module ls <keyword> # find a module by topic598vigolium skills get --full # print this skill and every reference599```600601## Resources602603- **Website**: [www.vigolium.com](https://www.vigolium.com/)604- **Documentation**: [docs.vigolium.com](https://docs.vigolium.com/)605- **GitHub**: [github.com/vigolium/vigolium](https://github.com/vigolium/vigolium)