Server Mode: Ingesting Data via the API¶
This guide covers how to start Vigolium in server mode and ingest HTTP traffic into the database using the REST API and CLI.
Starting the Server¶
# Start with an API key
export VIGOLIUM_API_KEY=my-secret-key
vigolium server
# Custom host and port
export VIGOLIUM_API_KEY=my-secret-key
vigolium server --host 127.0.0.1 --service-port 9002
# With transparent HTTP proxy for recording traffic
export VIGOLIUM_API_KEY=my-secret-key
vigolium server --ingest-proxy-port 9003
# Without authentication (development only)
vigolium server -A
The server listens on 0.0.0.0:9002 默认情况下.
CORS Configuration¶
The server's CORS behavior is controlled by cors_allowed_origins in ~/.vigolium/vigolium-configs.yaml:
| Value | Behavior |
|---|---|
reflect-origin (default) |
Echoes the requesting Origin header back. 允许 credentials. |
* |
允许 all origins without credentials (standard wildcard). |
| (empty string) | 禁用 CORS middleware entirely. |
https://app.example.com, https://admin.example.com |
Comma-separated allowlist. 允许 credentials. |
Project Scoping¶
All server operations are scoped to a project 通过 the X-Project-UUID request header. If omitted, 默认 project is used.
# Ingest into a specific project
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "X-Project-UUID: a1b2c3d4-..." \
-H "Content-Type: application/json" \
-d '{"input_mode": "url", "content": "https://example.com"}'
All queries (findings, HTTP records, stats, scans) return data scoped to the project specified in the header. See Projects for the full multi-tenancy reference.
Authentication¶
All API requests (except /health) require a Bearer token:
API key resolution order: VIGOLIUM_API_KEY env var > server.auth_api_key in config file.
API Endpoints¶
| Method | Path | Description |
|---|---|---|
| GET | / |
App info (no auth required) |
| GET | /health |
Health check (no auth required) |
| GET | /metrics |
Prometheus metrics (no auth required) |
| GET | /swagger/* |
Swagger UI and OpenAPI spec (no auth required) |
| GET | /server-info |
Server status, queue depth, record/finding counts |
| GET | /api/modules |
List available scanner modules |
| GET | /api/http-records |
查询 stored HTTP records |
| GET | /api/findings |
查询 scan findings |
| POST | /api/ingest-http |
Ingest HTTP traffic into the database |
| GET | /api/stats |
Aggregated scan statistics |
| GET | /api/scope |
View scope configuration |
| POST | /api/scope |
Update scope configuration |
| GET | /api/config |
View server configuration |
| POST | /api/config |
Update server configuration |
| POST | /api/scans/run |
Trigger a target-based background scan |
| GET | /api/scan/status |
Check scan status |
| POST | /api/scans/:uuid/stop |
Stop a running scan |
| POST | /api/agent/run/query |
Single-shot agent prompt execution |
| POST | /api/agent/run/autopilot |
Autonomous AI-driven scanning session |
| POST | /api/agent/run/swarm |
AI-guided targeted vulnerability swarm |
| GET | /api/agent/status/list |
List agent runs |
| GET | /api/agent/status/:id |
Get agent run status (includes full result when completed) |
Ingesting Data via API¶
The /api/ingest-http endpoint accepts multiple input modes. All requests use POST with a JSON body.
Ingest a Single URL¶
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"input_mode": "url",
"content": "https://example.com/api/users?id=1"
}'
Ingest Multiple URLs (url_file mode)¶
Pass a newline-separated list of URLs. Lines starting with # are treated as comments.
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"input_mode": "url_file",
"content": "https://example.com/api/users?id=1\nhttps://example.com/api/posts?page=2\nhttps://example.com/login"
}'
Ingest a curl Command¶
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"input_mode": "curl",
"content": "curl -X POST https://example.com/api/login -H \"Content-Type: application/json\" -d \"{\\\"username\\\":\\\"admin\\\",\\\"password\\\":\\\"test\\\"}\""
}'
Using content_base64 to avoid JSON escaping issues:
# Encode the curl command
ENCODED=$(echo -n 'curl -X POST https://example.com/api/login -H "Content-Type: application/json" -d "{\"username\":\"admin\",\"password\":\"test\"}"' | base64)
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d "{
\"input_mode\": \"curl\",
\"content_base64\": \"$ENCODED\"
}"
Ingest a Raw HTTP Request (Burp-style)¶
Send a base64-encoded raw HTTP request, optionally with its response:
# Encode raw request
RAW_REQ=$(printf 'GET /api/users?id=1 HTTP/1.1\r\nHost: example.com\r\nCookie: session=abc123\r\n\r\n' | base64)
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d "{
\"input_mode\": \"burp_base64\",
\"http_request_base64\": \"$RAW_REQ\"
}"
With both request and response:
RAW_REQ=$(printf 'POST /api/login HTTP/1.1\r\nHost: example.com\r\nContent-Type: application/json\r\n\r\n{"username":"admin","password":"test"}' | base64)
RAW_RESP=$(printf 'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"token":"eyJhbGciOiJIUzI1NiJ9..."}' | base64)
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d "{
\"input_mode\": \"burp_base64\",
\"http_request_base64\": \"$RAW_REQ\",
\"http_response_base64\": \"$RAW_RESP\"
}"
Ingest a Raw HTTP Request with a URL Hint¶
Raw HTTP requests don't contain the scheme (https vs http), and the Host header may not match the public hostname (e.g. behind a load balancer). Use the url field to provide the correct scheme and host:
RAW_REQ=$(printf 'POST /api/login HTTP/1.1\r\nHost: internal-lb\r\nContent-Type: application/json\r\n\r\n{"user":"admin"}' | base64)
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d "{
\"input_mode\": \"burp_base64\",
\"url\": \"https://app.example.com\",
\"http_request_base64\": \"$RAW_REQ\"
}"
Ingest an OpenAPI / Swagger Spec¶
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"input_mode": "openapi",
"content": "{\"openapi\":\"3.0.0\",\"info\":{\"title\":\"Example\",\"version\":\"1.0\"},\"servers\":[{\"url\":\"https://api.example.com\"}],\"paths\":{\"/users\":{\"get\":{\"summary\":\"List users\"}},\"/users/{id}\":{\"get\":{\"summary\":\"Get user\",\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"integer\"}}]}}}}"
}'
Using base64 for larger specs:
SPEC=$(base64 < openapi.yaml)
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d "{
\"input_mode\": \"openapi\",
\"content_base64\": \"$SPEC\"
}"
Ingest a Postman Collection¶
COLLECTION=$(base64 < collection.json)
curl -X POST http://localhost:9002/api/ingest-http \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d "{
\"input_mode\": \"postman_collection\",
\"content_base64\": \"$COLLECTION\"
}"
Ingesting Data via CLI¶
The vigolium ingest command supports both remote (server) and local (direct-to-database) modes.
Remote Ingestion (to a running server)¶
export VIGOLIUM_API_KEY=my-secret-key
# Pipe URLs from stdin
cat urls.txt | vigolium ingest -s http://localhost:9002
# From a file
vigolium ingest -s http://localhost:9002 --input targets.txt
# OpenAPI spec with a base URL
vigolium ingest -s http://localhost:9002 \
--input api.yaml -I openapi -t https://api.example.com
# Control submission rate
vigolium ingest -s http://localhost:9002 \
--input urls.txt --concurrency 20 -r 200
Local Ingestion (direct to database)¶
When --server is omitted, requests are fetched and stored directly in the local database:
# Ingest URLs (fetches each and stores request + response)
cat urls.txt | vigolium ingest
# From an OpenAPI spec
vigolium ingest --input api.yaml -I openapi -t https://api.example.com
# With a custom scan ID for tagging
vigolium ingest --input urls.txt --scan-uuid recon-2026-02
# Use a specific database file
vigolium ingest --input urls.txt --db ./project.db
# Ingest into a specific project
vigolium ingest --input urls.txt --project-uuid a1b2c3d4-...
Ingesting via Transparent Proxy¶
Start the server with a proxy port to passively record HTTP traffic:
Then route your tools through the proxy:
# curl through the proxy
curl -x http://localhost:9003 https://example.com/api/users
# httpx through the proxy
echo "https://example.com" | httpx -proxy http://localhost:9003
# nuclei through the proxy
nuclei -u https://example.com -proxy http://localhost:9003
All proxied HTTP traffic is automatically recorded in the database. HTTPS CONNECT tunneling is passed through without recording.
Querying Ingested Data¶
List HTTP Records¶
# All records (paginated, default limit=50)
curl -s http://localhost:9002/api/http-records \
-H "Authorization: Bearer my-secret-key" | jq .
# Filter by domain
curl -s "http://localhost:9002/api/http-records?domain=example.com" \
-H "Authorization: Bearer my-secret-key" | jq .
# Filter by status code and method
curl -s "http://localhost:9002/api/http-records?status_code=200,302&method=GET,POST" \
-H "Authorization: Bearer my-secret-key" | jq .
# Search across URLs and headers
curl -s "http://localhost:9002/api/http-records?search=admin&limit=10" \
-H "Authorization: Bearer my-secret-key" | jq .
# Pagination
curl -s "http://localhost:9002/api/http-records?limit=20&offset=40" \
-H "Authorization: Bearer my-secret-key" | jq .
List Findings¶
# All findings
curl -s http://localhost:9002/api/findings \
-H "Authorization: Bearer my-secret-key" | jq .
# Filter by severity
curl -s "http://localhost:9002/api/findings?severity=high,critical" \
-H "Authorization: Bearer my-secret-key" | jq .
# Filter by module
curl -s "http://localhost:9002/api/findings?module_name=xss-reflected" \
-H "Authorization: Bearer my-secret-key" | jq .
# Filter by domain
curl -s "http://localhost:9002/api/findings?domain=example.com" \
-H "Authorization: Bearer my-secret-key" | jq .
Server Info¶
Response:
{
"version": "0.1.0",
"uptime": "2h15m30s",
"service_addr": "0.0.0.0:9002",
"proxy_addr": "0.0.0.0:9003",
"queue_depth": 0,
"total_records": 1542,
"total_findings": 23
}
Scan Management via API¶
After ingesting HTTP records, trigger a vulnerability scan 通过 the API.
Trigger a Scan¶
curl -s -X POST http://localhost:9002/api/scan \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{}' | jq .
Force re-scan with specific modules:
curl -s -X POST http://localhost:9002/api/scan \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"force": true,
"enable_modules": ["xss-scanner", "sqli-error-based"]
}' | jq .
返回 202 Accepted on success, 409 Conflict if a scan is already running.
Check Scan Status¶
Stop a Running Scan¶
curl -s -X POST http://localhost:9002/api/scans/<scan-uuid>/stop \
-H "Authorization: Bearer my-secret-key" | jq .
See the API Reference for full request/response details.
Running AI Agents via API¶
The agent API provides three run modes that mirror the vigolium agent CLI subcommands. Only one agent run can be active at a time (returns 409 Conflict if busy).
Query — Single-Shot Agent Run¶
curl -s -X POST http://localhost:9002/api/agent/run/query \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"agent": "claude",
"prompt_template": "code-review",
"source": "/home/user/src/my-app"
}' | jq .
At least one of prompt_template, prompt_file, or prompt is required. 返回 202 Accepted on success. Set "stream": true for real-time SSE(服务器推送事件) output.
Autopilot — Autonomous Scanning¶
curl -s -X POST http://localhost:9002/api/agent/run/autopilot \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"target": "https://example.com",
"focus": "API injection",
"stream": true
}'
Swarm — AI-Guided Scanning¶
curl -s -X POST http://localhost:9002/api/agent/run/swarm \
-H "Authorization: Bearer my-secret-key" \
-H "Content-Type: application/json" \
-d '{
"input": "https://example.com",
"discover": true,
"profile": "thorough",
"stream": true
}'
SSE(服务器推送事件) events are data: lines with JSON payloads: {"type":"chunk","text":"..."} for real-time output, {"type":"phase","phase":"..."} for swarm phase transitions, {"type":"done","result":{...}} on completion, or {"type":"error","error":"..."} on failure.
List All Agent Runs¶
curl -s http://localhost:9002/api/agent/status/list \
-H "Authorization: Bearer my-secret-key" | jq .
Check Agent Run Status¶
curl -s http://localhost:9002/api/agent/status/agt-550e8400... \
-H "Authorization: Bearer my-secret-key" | jq .
Once the run completes, the response includes a result field with the full agent output (raw text, findings, HTTP records).
See Agent Mode for the full agent documentation (including autopilot, swarm, context enrichment, and prompt templates) and the API Reference for request/response details.
Input Modes Reference¶
| Mode | Content Field | Description |
|---|---|---|
url |
content |
A single URL |
url_file |
content |
Newline-separated list of URLs |
curl |
content or content_base64 |
A curl command string |
burp_base64 |
http_request_base64 |
Base64-encoded raw HTTP request |
openapi / swagger |
content or content_base64 |
OpenAPI/Swagger spec (JSON or YAML) |
postman_collection |
content or content_base64 |
Postman Collection (JSON) |
For burp_base64 mode, you can also include http_response_base64 to store the response alongside the request.
For modes that accept large payloads, prefer content_base64 to avoid JSON escaping issues.