Skip to main content
Use stateless scanning for CI/CD pipelines, scripting, AI-agent integration, and quick ad-hoc checks. For the conceptual deep-dive see Scanning Modes Overview; for the full strategy reference see Strategies.

Stateless at a glance

CommandPersists to DB?PhasesUse it for
scan-urlNonone, direct module runOne URL, fast
scan-requestNonone, direct module runA raw HTTP request / curl
scan --statelessNo (temp DB, discarded)full pipelineOne-shot full scan
scanYes (~/.vigolium/...sqlite)full pipelinePersistent projects
scan-url and scan-request never touch a database. scan --stateless creates a temporary SQLite database, runs every requested phase, exports results, and deletes the database on exit.
Pass -o/--output (with --format) when using --stateless, otherwise results are discarded along with the temporary database. Vigolium prints a warning if you forget. --stateless and --db are mutually exclusive.

Scan a single URL, scan-url

# Simplest possible scan
vigolium scan-url https://example.com/api/users?id=1

# JSON output for scripting
vigolium scan-url -j https://example.com/api/users?id=1
POST with a body and headers:
vigolium scan-url \
  --method POST \
  --body '{"user":"admin","pass":"secret"}' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer tok123' \
  https://example.com/api/login
Scope the modules and skip work you don’t need:
# Only injection-class modules (fuzzy match on ID/name)
vigolium scan-url -m sqli -m xss "https://example.com/search?q=test"

# Filter by tag
vigolium scan-url --module-tag injection https://example.com/api/data

# Skip passive analysis and insertion-point fuzzing for the fastest result
vigolium scan-url --no-passive --no-insertion-points https://example.com/api/data
Run a discovery/spider phase before the scan (these promote scan-url to the full pipeline and require a database, pass --db):
vigolium scan-url --discover --db /tmp/scan.sqlite https://example.com

Scan a raw HTTP request, scan-request

# From a file containing a raw HTTP request
vigolium scan-request -i request.txt

# From stdin
printf 'GET /api/users?id=1 HTTP/1.1\r\nHost: example.com\r\n\r\n' \
  | vigolium scan-request

# From a curl command (auto-detected)
echo "curl -X POST -d 'user=admin' https://example.com/login" \
  | vigolium scan-request
Override the host when the request file has only a path:
vigolium scan-request -i request.txt --target https://staging.example.com

Piping from stdin

Both scan-url and scan-request auto-detect the stdin format, plain URL, curl command, or raw HTTP request:
# Plain URL
echo 'https://example.com/search?q=test' | vigolium scan-url

# Curl command
echo "curl -H 'Content-Type: application/json' -d '{\"id\":1}' https://example.com/api" \
  | vigolium scan-url

# Raw HTTP request
printf 'POST /api/login HTTP/1.1\r\nHost: example.com\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\nuser=admin&pass=secret' \
  | vigolium scan-request

# Scan whatever is on your clipboard (macOS)
pbpaste | vigolium scan-url -j

Full stateless pipeline, scan --stateless

Run discovery, spidering, and dynamic-assessment with no persistent state. --stateless works on both scan and run:
# Full pipeline, JSONL out, nothing left behind
vigolium scan --stateless -t https://example.com --format jsonl -o results

# Add content discovery, write both JSONL and HTML
vigolium scan --stateless -t https://example.com \
  --discover --format jsonl,html -o scan-output

# A single phase, statelessly
vigolium run dynamic-assessment --stateless -t https://example.com \
  --format jsonl -o results
Multiple targets from a file scan into one shared temporary database and export to a single unified output file by default:
vigolium scan --stateless -T targets.txt --format jsonl -o results
# -> results.jsonl (all targets combined)
Add --split-by-host to instead scan each target in its own isolated temporary database and write a separate per-host output file (the filename is suffixed with the host so results don’t overwrite):
vigolium scan --stateless -T targets.txt --split-by-host --format jsonl -o results
# -> results-example.com.jsonl, results-test.example.com.jsonl, ...

⚡Scanning a huge list of targets in parallel

When you point Vigolium at a large target list, use -P/--parallel N to scan several hosts at once. Each target runs in its own isolated child process — so there is no cross-contamination between workers — and each child keeps its own --concurrency, meaning real in-flight requests are roughly N × --concurrency. -P requires one of two output strategies so results never collide:
  • --stateless --split-by-host — each target runs against its own temporary database and writes a separate per-host output file (base-<host>.<ext>). Nothing is persisted. Best for stateless, fire-and-forget batches.
  • --db-isolate — each worker scans into a private temporary SQLite database, then merges its results into the shared --db (or the default DB) at the end. This lets many parallel scans share one database without write contention, and you export one unified report from the merged DB afterward.
# Stateless fan-out: per-host JSONL + HTML files, 3 targets at a time
vigolium scan -T list-of-targets.txt -P 3 \
  --stateless --split-by-host \
  --format jsonl,html --output prefix-output \
  --fuzz-wordlist ~/Tools/contents/fast.txt

# Shared-DB fan-out: 4 targets at a time merged into one local.db, one unified output
vigolium scan -T list-of-targets.txt -P 4 \
  --db-isolate --db local.db \
  --format jsonl,html --output report \
  --fuzz-wordlist ~/Tools/contents/fast.txt
--db-isolate is SQLite-only and cannot be combined with --stateless (they are two different ways to avoid write contention). Pressing Ctrl-C during a -P batch is treated as an operator stop: un-started and cut-short targets are reported as “not scanned” rather than failures.

Resuming an interrupted fan-out

A stateless parallel fan-out (-S -T --split-by-host -P) writes a tiny line-cursor manifest, <output>.progress.json, tracking the targets that completed cleanly. If the batch is interrupted (Ctrl-C, a crash, a CI timeout), re-run it with --resume to skip the finished targets and scan only the remainder — Vigolium also prints a copy-pasteable resume command on Ctrl-C/failure:
# Original run
vigolium scan -T targets.txt -P 4 --stateless --split-by-host --format jsonl -o results

# Resume only the targets that didn't finish
vigolium scan -T targets.txt -P 4 --stateless --split-by-host --format jsonl -o results --resume
Run vigolium scan --resume bare — with no other flags — and it auto-discovers the *.progress.json in the current directory and relaunches the saved run from it (pass -o <prefix> to disambiguate when several manifests exist).
--resume currently applies only to the parallel fan-out (-S -T --split-by-host -P > 1). Resuming a plain sequential scan re-runs it in full.

Stateless scans from other input sources

# OpenAPI / Swagger spec
vigolium scan --stateless -i api.yaml -I openapi \
  -t https://api.example.com --format jsonl -o results

# Postman collection
vigolium scan --stateless -i collection.json -I postman \
  -t https://api.example.com --format jsonl -o results

# Burp Suite XML export
vigolium scan --stateless -i export.xml -I burpxml --format jsonl -o results

# HAR capture
vigolium scan --stateless -i traffic.har -I har --format jsonl -o results

# Nuclei JSONL
vigolium scan --stateless -i nuclei.jsonl -I nuclei --format jsonl -o results

Tuning a scan

# Speed knobs — defaults: -c 50, -r 100 req/s, --max-per-host 30, --timeout 15s
vigolium scan --stateless -t https://example.com \
  -c 100 -r 200 --max-per-host 10 --timeout 30s \
  --format jsonl -o results

# Strategy presets trade depth for speed
vigolium scan --stateless -t https://example.com --strategy lite -o r --format jsonl
vigolium scan --stateless -t https://example.com --strategy deep -o r --format jsonl

# Route everything through a proxy
vigolium scan --stateless -t https://example.com --proxy http://127.0.0.1:8080 \
  --format jsonl -o results

# Constrain how broadly scope is interpreted
vigolium scan --stateless -t https://example.com --scope-origin strict \
  --format jsonl -o results

# Include the full HTTP response body in findings (scan / run only)
vigolium scan --stateless -t https://example.com --include-response \
  --format jsonl -o results

Authenticated stateless scans

Pass an inline session or a session file, both work in stateless mode:
# Inline session: name:Header:value
vigolium scan --stateless -t https://example.com \
  --auth "admin:Cookie:session_id=abc123" \
  --format jsonl -o results

# Single session or multi-session bundle file (YAML or JSON)
vigolium scan --stateless -t https://example.com \
  --auth-file ./admin-session.yaml \
  --format jsonl -o results

# A static header is often enough for token auth
vigolium scan-url -H 'Authorization: Bearer token123' \
  https://example.com/api/me
See Authenticated Scanning for login flows, token extraction, and multi-session IDOR/BOLA testing.

CI/CD integration

--ci-output-format forces clean JSONL with no banners or color codes, ideal for parsing in a pipeline:
vigolium scan --stateless -t https://example.com \
  --ci-output-format -o findings
A minimal gate that fails the build when any finding is reported:
vigolium scan --stateless -t "$TARGET" --ci-output-format -o findings
test ! -s findings.jsonl || { echo "Vulnerabilities found"; exit 1; }
See CI/CD Integration for full pipeline examples.

Output formats recap

--formatOutputNotes
consoleTerminal (default)Colored, human-readable
jsonl<o>.jsonlOne JSON object per line; -j is shorthand
html<o>.htmlInteractive ag-grid report; requires -o
sqlite<o>.sqliteStandalone DB dump (VACUUM INTO); requires -S + -o. Aliases sqlite3, db
console,jsonl,htmlAll of the aboveComma-separate to combine
For stateless runs, -o is the base path, Vigolium appends the correct extension per format and materializes every requested format from the temporary database before tearing it down.

Next steps

Oh dear, you actually read to the end. Here’s the secret the config file kept nudging you toward: by default every Vigolium request announces itself with a Vigolium/<version> User-Agent, so a friendly blue team can spot your authorized scan in their logs in seconds. Go full ninja only when you mean to be sneaky:
# Persist it in your config
vigolium config set scanning_strategy.http.user_agent random

# Or a one-off via env var (overrides the config value for that run)
export VIGOLIUM_DEFAULT_UA=random