Vigolium API Reference — Scan with Storage¶
End-to-end guide for running scans (native + agentic) and shipping results to a cloud storage bucket. Covers every endpoint that supports upload_results, the on-bucket layout, how to retrieve bundles afterwards, and the matching CLI flags.
For storage configuration (GCS / S3 / MinIO setup, presigned URLs, source uploads), see storage.md. For the agent endpoints themselves, see agent.md. For native scan options, see scan.md.
At a Glance¶
Every scan mode supports an opt-in result-upload flow. When upload_results: true is sent, the server tar.gzs the relevant artifacts after the scan completes successfully and uploads them to a project-scoped object key. The DB row's storage_url column is populated so the bundle can be retrieved later.
| Mode | 端点 | CLI flag | Bundle key |
|---|---|---|---|
| Native scan | POST /api/scans/run |
--upload-results |
<project>/native-scans/<scan-uuid>/results.tar.gz |
| Agent query | POST /api/agent/run/query |
--upload-results |
<project>/agentic-scans/<agentic-scan-uuid>/results.tar.gz |
| Agent autopilot | POST /api/agent/run/autopilot |
--upload-results |
<project>/agentic-scans/<agentic-scan-uuid>/results.tar.gz |
| Agent swarm | POST /api/agent/run/swarm |
--upload-results |
<project>/agentic-scans/<agentic-scan-uuid>/results.tar.gz |
| Agent audit | POST /api/agent/run/audit |
--upload-results |
<project>/agentic-scans/<agentic-scan-uuid>/results.tar.gz |
All bundles live under <bucket>/<project-uuid>/...; the project UUID comes from the X-Project-UUID header (or the request body's project_uuid) — see storage.md → Security.
Every endpoint above also accepts an optional scan_uuid field (CLI flag --scan-uuid). When supplied, the server pins the scan/run UUID to that value 而不是 minting a fresh one — this is what makes cross-node sync work, see Pinning Scan UUIDs below.
Prerequisites¶
1. Configure storage¶
存储 is disabled 默认情况下. Without it, upload_results: true is a silent no-op (a warning is logged). Enable it in ~/.vigolium/vigolium-configs.yaml:
storage:
enabled: true
driver: gcs # or s3, minio
bucket: ${VIGOLIUM_STORAGE_BUCKET_NAME}
region: asia-southeast1
access_key: ${VIGOLIUM_STORAGE_ACCESS_KEY}
secret_key: ${VIGOLIUM_STORAGE_SECRET_KEY}
use_ssl: true
Full setup steps (HMAC keys, bucket creation, IAM bindings) are in storage.md → GCP Setup.
2. Start the server¶
3. Verify storage is reachable¶
curl -s -X POST http://localhost:9002/api/storage/upload-source \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-F "file=@README.md" | jq .
A storage_url in the response confirms credentials are working.
Native Scan¶
Run with upload¶
curl -s -X POST http://localhost:9002/api/scans/run \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d '{
"targets": ["https://example.com"],
"strategy": "balanced",
"upload_results": true,
"output_formats": ["jsonl", "html"],
"scan_uuid": "550e8400-e29b-41d4-a716-446655440000"
}' | jq .
scan_uuid is optional. Omit it and the server mints a fresh one. Supply it (CLI: --scan-uuid) when a different node already created the scan record and this run should attach to it — see Pinning Scan UUIDs.
Response (202):
{
"project_uuid": "<project-uuid>",
"scan_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"message": "scan started"
}
What gets uploaded¶
By default the bundle contains the 每-scan runtime.log only. Pass output_formats (currently jsonl and/or html) on the request to also materialize and bundle the rendered findings — these land at <sessions_dir>/<scan-uuid>/output.{jsonl,html} on the server and are added to the tar.gz alongside runtime.log. CLI runs that pass --output and --format get 相同 shape.
<bucket>/<project-uuid>/native-scans/<scan-uuid>/results.tar.gz
├── runtime.log
├── output.jsonl # only when output_formats includes "jsonl"
└── output.html # only when output_formats includes "html"
CLI equivalent¶
Agent Query¶
Single-shot prompt execution. Useful for code review, secret discovery, endpoint extraction. The bundle captures the agent session directory (runtime.log, output.md, any extracted artifacts).
curl -s -X POST http://localhost:9002/api/agent/run/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d '{
"prompt_template": "security-code-review",
"source": "/path/to/repo",
"scan_uuid": "550e8400-e29b-41d4-a716-446655440000",
"upload_results": true
}' | jq .
scan_uuid is optional — supply it to attach this query run to a UUID a different node already created (the response's agentic_scan_uuid will echo back 相同 value). Omit it for a freshly minted UUID.
CLI equivalent¶
vigolium agent query --prompt-template security-code-review \
--source /path/to/repo \
--scan-uuid 550e8400-e29b-41d4-a716-446655440000 \
--upload-results
Agent Autopilot¶
Autonomous agentic scan. Useful for blackbox + greybox testing where the agent drives the whole pipeline.
curl -s -X POST http://localhost:9002/api/agent/run/autopilot \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d '{
"target": "http://localhost:3000",
"source": "/home/user/src/my-app",
"intensity": "balanced",
"scan_uuid": "550e8400-e29b-41d4-a716-446655440000",
"upload_results": true
}' | jq .
For agentic endpoints, scan_uuid pins AgenticScan.uuid (the run identity) — the response surfaces it as agentic_scan_uuid, and any native scans the autopilot spawns inherit 相同 value so findings, http_records, and the uploaded bundle all key off one UUID.
CLI equivalent¶
vigolium agent autopilot \
--target http://localhost:3000 \
--source /home/user/src/my-app \
--intensity balanced \
--scan-uuid 550e8400-e29b-41d4-a716-446655440000 \
--upload-results
The bundle is the full session directory: runtime.log, vigolium-results/ artifacts, operator transcripts, frozen context, and any findings emitted by the agent.
Agent Swarm¶
AI-guided targeted swarm scan. Good for focused 每-vulnerability runs.
curl -s -X POST http://localhost:9002/api/agent/run/swarm \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d '{
"input": "https://example.com/api/users?id=1",
"source": "/path/to/repo",
"code_audit": true,
"triage": true,
"scan_uuid": "550e8400-e29b-41d4-a716-446655440000",
"upload_results": true
}' | jq .
A pinned scan_uuid lets a dispatcher pre-create the agentic_scans row (e.g. 通过 a 1-record dry_run scan or a direct DB insert) and have the swarm worker attach to it. If the UUID already exists under a different project, the API returns 409 Conflict 而不是 corrupting tenant isolation — see Pinning Scan UUIDs.
CLI equivalent¶
vigolium agent swarm \
--input "https://example.com/api/users?id=1" \
--source /path/to/repo \
--code-audit --triage \
--scan-uuid 550e8400-e29b-41d4-a716-446655440000 \
--upload-results
Agent Audit¶
Foreground vigolium-audit (multi-phase source code security audit).
curl -s -X POST http://localhost:9002/api/agent/run/audit \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d '{
"source": "https://github.com/example/myapp",
"intensity": "deep",
"platform": "claude",
"scan_uuid": "550e8400-e29b-41d4-a716-446655440000",
"upload_results": true
}' | jq .
审计 runs are typically long-lived (deep mode can run for hours), so pinning scan_uuid is especially useful when a dispatcher wants to surface the run's UUID to end users before the audit starts — 相同 UUID will be returned as agentic_scan_uuid and used as the bundle key under <project-uuid>/agentic-scans/<scan_uuid>/results.tar.gz.
CLI equivalent¶
The bundle contains runtime.log, audit-stream.jsonl, vigolium-audit-output.md, and the synced vigolium-results/ artifact tree (每-phase reports, advisory matrices, audit-state.json).
On-Bucket Layout¶
<bucket>/
<project-uuid>/
ugc/ # uploaded source archives (input)
my-app.tar.gz
native-scans/
<scan-uuid>/
results.tar.gz # runtime.log + output.{jsonl,html} when output_formats requested
agentic-scans/
<agentic-scan-uuid>/
results.tar.gz # full session dir for query/autopilot/swarm/audit
Both native and agentic scan trees are flat: one directory 每 UUID, one bundle 每 scan.
Retrieving Results¶
Via the API¶
GET /api/storage/results/:scan-uuid searches native-scans/ first, then agentic-scans/:
curl -s -o results.tar.gz \
http://localhost:9002/api/storage/results/<uuid> \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>"
tar xzf results.tar.gz
Via the CLI¶
Via presigned URL (large files / external clients)¶
curl -s -X POST http://localhost:9002/api/storage/presign \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d '{
"key": "agentic-scans/<agentic-scan-uuid>/results.tar.gz",
"method": "GET",
"expiry_seconds": 3600
}' | jq -r .url
The returned URL is a direct GCS/S3 download that bypasses the API server.
From the DB row¶
When upload_results succeeds, the scan's storage_url column is set:
# Native scan
curl -s http://localhost:9002/api/scans/<scan-uuid> \
-H "Authorization: Bearer <token>" | jq .storage_url
# "gs://<project-uuid>/native-scans/<scan-uuid>/results.tar.gz"
# Agentic scan (query / autopilot / swarm / audit)
curl -s http://localhost:9002/api/agent/sessions/<agentic-scan-uuid> \
-H "Authorization: Bearer <token>" | jq .storage_url
# "gs://<project-uuid>/agentic-scans/<agentic-scan-uuid>/results.tar.gz"
Pinning Scan UUIDs (Cross-Node Sync)¶
Every native and agentic endpoint accepts a scan_uuid request field (CLI: --scan-uuid). When supplied, the server uses it as the scan/run identity 而不是 generating one. This unlocks the remote-create + remote-run pattern: one node creates a placeholder scan record, another node runs the actual work and pushes results under 相同 UUID — useful for serverless dispatch, queue-based fan-out, or any setup where the dispatcher and the runner are different processes.
Same field, both endpoint families¶
| 端点 family | Where the pinned UUID lands | Response field |
|---|---|---|
| Native scan | Scan.uuid (the scan record's PK) |
scan_uuid |
| Agentic scan | AgenticScan.uuid (the run record's PK) |
agentic_scan_uuid |
In other words, request.scan_uuid becomes response.scan_uuid for native and response.agentic_scan_uuid for agentic. Findings, http_records, and OAST(带外交互) interactions written by either run carry that same value in their scan_uuid foreign-key column, so a join across nodes always lines up.
Get-or-create semantics¶
When the supplied scan_uuid already exists in the database:
- Same
project_uuid— the existing row is reused; no duplicate is inserted. The runner proceeds and writes its results against the existing record. 这是 the cross-node attach case. - Different
project_uuid— the API returns HTTP 409 Conflict witherror: "scan UUID exists under a different project: ...". This guard prevents serverless nodes from accidentally corrupting another tenant's data when UUIDs collide.
Per-tenant UUID collisions across unrelated scans are statistically negligible (UUID v4), so the 409 path should only ever fire on a real mistake.
Worked example: dispatch on Node A, run on Node B¶
Node A — dispatcher creates the placeholder 通过 the API:
SCAN_UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')
curl -s -X POST http://node-a:9002/api/scans/run \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d "{
\"targets\": [\"https://example.com\"],
\"scan_uuid\": \"$SCAN_UUID\",
\"dry_run\": true
}"
# Returns 200 with status=dry_run; scan record is now persisted.
# Hand off SCAN_UUID via your queue / message bus
publish-job --scan-uuid "$SCAN_UUID" --target https://example.com
Node B — worker picks up the job and runs the scan against 相同 UUID:
vigolium scan -t https://example.com \
--project-uuid "<project-uuid>" \
--scan-uuid "$SCAN_UUID" \
--upload-results
Findings and http_records emitted by Node B carry scan_uuid = $SCAN_UUID — exactly what Node A's UI is already querying for. The bundle uploaded by Node B lands at <project-uuid>/native-scans/$SCAN_UUID/results.tar.gz, 相同 key any consumer would expect from a single-node run.
Agentic equivalent¶
The same pattern applies to vigolium agent autopilot|swarm|query|audit and POST /api/agent/run/* — --scan-uuid (or the request body's scan_uuid field) pins AgenticScan.uuid 而不是 Scan.uuid. The response surfaces the pinned value as agentic_scan_uuid.
Node A — dispatcher pre-creates an autopilot run (e.g. so a UI can show the run UUID before the worker picks it up). Pre-creation is most ergonomic 通过 the agentic API itself with dry_run: true (avoids spending agent budget on an empty run):
RUN_UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')
curl -s -X POST http://node-a:9002/api/agent/run/autopilot \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d "{
\"target\": \"https://example.com\",
\"source\": \"/work/my-app\",
\"scan_uuid\": \"$AGENTIC_SCAN_UUID\",
\"dry_run\": true
}"
publish-job --run-uuid "$AGENTIC_SCAN_UUID" --target https://example.com --source /work/my-app
Node B — worker runs the actual autopilot pipeline against 相同 UUID:
vigolium agent autopilot \
--target https://example.com \
--source /work/my-app \
--project-uuid "<project-uuid>" \
--scan-uuid "$AGENTIC_SCAN_UUID" \
--upload-results
Or, equivalently against a remote server:
curl -s -X POST http://node-b:9002/api/agent/run/autopilot \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-H "X-Project-UUID: <project-uuid>" \
-d "{
\"target\": \"https://example.com\",
\"source\": \"/work/my-app\",
\"scan_uuid\": \"$AGENTIC_SCAN_UUID\",
\"upload_results\": true
}"
# Response: { "agentic_scan_uuid": "$AGENTIC_SCAN_UUID", "status": "running", ... }
Both nodes' findings, http_records, and OAST(带外交互) interactions carry scan_uuid = $AGENTIC_SCAN_UUID. Node A's GET /api/agent/sessions/$AGENTIC_SCAN_UUID returns the in-progress status (Node B updates 相同 row), and the uploaded bundle ends up at <project-uuid>/agentic-scans/$AGENTIC_SCAN_UUID/results.tar.gz.
The same flow works for swarm, query, and audit — substitute the endpoint and the relevant request fields. For audit, pinning is especially useful because deep-mode runs can take hours and the dispatcher often wants to surface the run UUID immediately.
End-to-End Example: Upload Source → Scan → Download Bundle¶
PROJECT="<project-uuid>"
TOKEN="<api-token>"
BASE="http://localhost:9002"
# 1. Upload local source to storage (one-time)
SRC_URL=$(curl -s -X POST "$BASE/api/storage/upload-source" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Project-UUID: $PROJECT" \
-F "file=@my-app.tar.gz" | jq -r .storage_url)
echo "Source uploaded: $SRC_URL"
# 2. Launch swarm scan against the uploaded source
RUN_ID=$(curl -s -X POST "$BASE/api/agent/run/swarm" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Project-UUID: $PROJECT" \
-d "{
\"input\": \"https://example.com\",
\"source\": \"$SRC_URL\",
\"upload_results\": true,
\"triage\": true
}" | jq -r .agentic_scan_uuid)
echo "Run started: $RUN_ID"
# 3. Poll until completed
while true; do
STATUS=$(curl -s "$BASE/api/agent/status/$RUN_ID" \
-H "Authorization: Bearer $TOKEN" | jq -r .status)
echo "Status: $STATUS"
[[ "$STATUS" == "completed" || "$STATUS" == "failed" ]] && break
sleep 10
done
# 4. Download and extract the bundle
curl -s -o results.tar.gz "$BASE/api/storage/results/$RUN_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Project-UUID: $PROJECT"
tar xzf results.tar.gz
ls -la
Behavior Notes¶
- 存储 disabled — When
storage.enabledisfalse,upload_results: trueis a silent no-op. The server logs"upload_results requested but storage is not enabled"at warn level. Scans still complete normally. - 扫描 failure — Bundles are uploaded only on successful completion. Failed/cancelled scans skip the upload (the runtime.log is still on disk under the session/scan-logs directory).
- No output to upload — If a native scan has neither output files nor a runtime.log on disk (e.g.
persist_logsdisabled and no--output), the upload step logs"storage: no native scan files to upload"and exits cleanly. - Bundle size — Agentic bundles include the full session dir, which can grow large (vigolium-audit deep mode produces 10s of MB of phase reports). Use presigned URLs for large downloads.
- 项目 scoping — All keys are prefixed with the project UUID. A bundle uploaded under project A is not visible to project B even if the run UUID is known.
- Idempotency — Re-running a scan with 相同 UUID overwrites the previous bundle. UUIDs are server-generated 默认情况下; clients deliberately pin them 通过
scan_uuid/--scan-uuidfor cross-node sync, in which case attaching to an existing record under 相同 project is the intended path (see Pinning Scan UUIDs). A pinned UUID that exists under a different project returns HTTP 409 而不是 overwriting.
Related¶
- storage.md — full storage API reference (upload-source, presign, GCS/S3/MinIO setup)
- agent.md — agent run endpoints (query, autopilot, swarm, audit, status, sessions, logs)
- scan.md — native scan run/list/status endpoints
- projects.md — project scoping and multi-tenancy
- Run
make sanity-checkto exercise this entire flow end-to-end against real targets (ginandjuice.shop / VAmPI / juice-shop).