Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vigolium.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The storage API provides cloud object storage integration for source code upload/download and scan result archival. All objects are scoped to a project via the X-Project-UUID header and stored under <bucket>/<project-uuid>/. Storage is disabled by default. Enable it by setting storage.enabled: true in vigolium-configs.yaml. All endpoints return 503 Service Unavailable when storage is not configured.
EndpointMethodRoleDescription
/api/storage/upload-sourcePOSTOperatorUpload source code archive
/api/storage/source/:keyGETViewerDownload source code by key
/api/storage/results/:scan-uuidGETViewerDownload scan result bundle (.tar.gz)
/api/storage/presignPOSTOperatorGenerate presigned upload/download URL

Security

All storage operations enforce project-level isolation:
  • Every object path is prefixed with the authenticated project UUID (<bucket>/<project-uuid>/<key>). The project UUID comes from the X-Project-UUID header, set by server middleware — clients cannot override the prefix.
  • Keys and UUIDs are validated against path traversal (../, ..\\, ..) before any storage operation. Malicious keys are rejected with 400 Bad Request.
  • Presigned URLs are scoped to the requesting project — a presigned URL for project A cannot access objects in project B.

GCP Setup

Vigolium uses S3-compatible HMAC keys to talk to GCS. You need to create HMAC credentials from a service account, then configure them in vigolium-configs.yaml.

Step 1: Create HMAC Keys from a Service Account

If you have a service account JSON key (e.g. gcs-readwrite-key.json), activate it and create HMAC credentials:
# Authenticate with the service account
gcloud auth activate-service-account --key-file=/path/to/gcs-readwrite-key.json

# Get the service account email from the key file
SA_EMAIL=$(jq -r '.client_email' /path/to/gcs-readwrite-key.json)

# Create HMAC keys for the service account
gcloud storage hmac create "$SA_EMAIL"
This outputs an accessId and secret. Save both — the secret is only shown once.

Step 2: Create a GCS Bucket

# Create a bucket in your preferred region
gcloud storage buckets create gs://my-vigolium-bucket \
  --location=asia-southeast1 \
  --uniform-bucket-level-access

# Grant the service account read/write access
gcloud storage buckets add-iam-policy-binding gs://my-vigolium-bucket \
  --member="serviceAccount:$SA_EMAIL" \
  --role="roles/storage.objectAdmin"

Step 3: Configure Vigolium

Set the credentials and bucket name as environment variables:
export VIGOLIUM_STORAGE_ACCESS_KEY="GOOG1E..."        # accessId from step 1
export VIGOLIUM_STORAGE_SECRET_KEY="abc123..."          # secret from step 1
export VIGOLIUM_STORAGE_BUCKET_NAME="my-vigolium-bucket"
Then configure ~/.vigolium/vigolium-configs.yaml:
storage:
  enabled: true
  driver: gcs
  bucket: ${VIGOLIUM_STORAGE_BUCKET_NAME}
  region: asia-southeast1
  access_key: ${VIGOLIUM_STORAGE_ACCESS_KEY}
  secret_key: ${VIGOLIUM_STORAGE_SECRET_KEY}
  use_ssl: true

Step 4: Verify Connectivity

# Upload a test file
curl -s -X POST http://localhost:9002/api/storage/upload-source \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -F "[email protected]" | jq .

# Generate a presigned download URL
curl -s -X POST http://localhost:9002/api/storage/presign \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -d '{"key": "ugc/test-source.tar.gz", "method": "GET"}' | jq .url

AWS S3 / MinIO

For AWS S3, change the driver and region:
storage:
  enabled: true
  driver: s3
  bucket: ${VIGOLIUM_STORAGE_BUCKET_NAME}
  region: us-east-1
  access_key: ${AWS_ACCESS_KEY_ID}
  secret_key: ${AWS_SECRET_ACCESS_KEY}
For self-hosted MinIO, set the endpoint explicitly:
storage:
  enabled: true
  driver: minio
  endpoint: minio.internal:9000
  bucket: ${VIGOLIUM_STORAGE_BUCKET_NAME}
  access_key: ${MINIO_ACCESS_KEY}
  secret_key: ${MINIO_SECRET_KEY}
  use_ssl: false
  path_style: true

Storage Object Layout

All objects are prefixed with the project UUID for multi-tenant isolation:
<bucket>/
  <project-uuid>/
    ugc/                              # User-uploaded source code
      source-code.tar.gz
      my-app.zip
    native-scans/                     # Native scan result bundles
      <scan-uuid>/
        results.tar.gz                # Bundled findings JSONL, HTML report, stats
    agentic-scans/                    # Agentic scan result bundles
      <agentic-scan-uuid>/
        results.tar.gz                # Bundled session dir (output.md, extensions/, plan.json)
Result bundles use .tar.gz format (gzip-compressed tar), matching the vigolium db export --format bundle format.

CLI Usage

Source Download from Storage

Use gs:// URIs with --source to download and extract source code from cloud storage before scanning:
# Native scan with source from GCS
vigolium scan -t https://example.com \
  --source gs://<project-uuid>/ugc/source-code.tar.gz

# Agentic swarm with source from GCS
vigolium agent swarm -t https://example.com \
  --source gs://<project-uuid>/ugc/source-code.tar.gz

# Autopilot with source from GCS
vigolium agent autopilot -t https://example.com \
  --source gs://<project-uuid>/ugc/source-code.tar.gz
The archive is downloaded, extracted to a temp directory, and cleaned up after the scan completes. The source_type field in the DB records "gcs".

Result Upload to Storage

Add --upload-results to upload scan results to cloud storage after completion:
# Native scan — upload findings JSONL + HTML report as tar.gz bundle
vigolium scan -t https://example.com -o results --format jsonl,html --upload-results

# Agentic swarm — upload session dir as tar.gz bundle
vigolium agent swarm -t https://example.com --upload-results

# Autopilot — upload session artifacts as tar.gz bundle
vigolium agent autopilot -t https://example.com --source ./src --upload-results
Results are uploaded to:
  • Native scans: gs://<project-uuid>/native-scans/<scan-uuid>/results.tar.gz
  • Agentic scans: gs://<project-uuid>/agentic-scans/<run-uuid>/results.tar.gz
The storage_url field on the Scan / AgenticScan DB record is updated with the gs:// URL after upload.

POST /api/storage/upload-source

Uploads a source code archive to cloud storage, scoped to the project. Content-Type: multipart/form-data
FieldTypeRequiredDescription
filefileYesSource code archive to upload
curl -s -X POST http://localhost:9002/api/storage/upload-source \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -F "[email protected]" | jq .
Response (200):
{
  "storage_url": "gs://my-project-uuid/ugc/source-code.tar.gz",
  "key": "ugc/source-code.tar.gz",
  "filename": "source-code.tar.gz",
  "size": 1048576,
  "message": "source uploaded successfully"
}
The returned storage_url can be passed directly to --source (CLI) or the source field (API).

GET /api/storage/source/:key

Downloads a previously uploaded source file.
curl -s -o source.tar.gz \
  http://localhost:9002/api/storage/source/source-code.tar.gz \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid"
Returns the file as application/octet-stream with Content-Disposition: attachment. Returns 400 if the key contains path traversal sequences.

GET /api/storage/results/:scan-uuid

Downloads the result bundle for a native scan or agentic scan. Searches native-scans/<uuid>/results.tar.gz first, then agentic-scans/<uuid>/results.tar.gz.
curl -s -o results.tar.gz \
  http://localhost:9002/api/storage/results/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid"
Returns application/gzip with Content-Disposition: attachment. Returns 404 if no results have been uploaded for the given UUID. Extract the bundle:
tar xzf results.tar.gz

POST /api/storage/presign

Generates a presigned URL for direct upload or download, bypassing the API server. Useful for large files or client-side uploads. Request body:
FieldTypeRequiredDescription
keystringYesObject key (e.g. ugc/source-code.tar.gz)
methodstringNoGET (default) or PUT
expiry_secondsintNoURL expiry in seconds (default: 3600 / 1 hour)
# Generate a download URL
curl -s -X POST http://localhost:9002/api/storage/presign \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -d '{
    "key": "ugc/source-code.tar.gz",
    "method": "GET",
    "expiry_seconds": 3600
  }' | jq .

# Generate an upload URL (for client-side direct upload)
curl -s -X POST http://localhost:9002/api/storage/presign \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -d '{
    "key": "ugc/my-app.tar.gz",
    "method": "PUT"
  }' | jq .
Response (200):
{
  "url": "https://storage.googleapis.com/my-bucket/my-project-uuid/ugc/source-code.tar.gz?X-Goog-Algorithm=...",
  "key": "ugc/source-code.tar.gz",
  "method": "GET",
  "expiry_seconds": 3600
}
Keys are validated against path traversal — requests with ../ or similar sequences are rejected with 400.

Using Storage with Agentic Scans (API)

Upload Source, Then Run Agentic Scan

# 1. Upload source code
STORAGE_URL=$(curl -s -X POST http://localhost:9002/api/storage/upload-source \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -F "[email protected]" | jq -r '.storage_url')

echo "Uploaded to: $STORAGE_URL"

# 2. Run swarm with uploaded source + result upload
curl -s -X POST http://localhost:9002/api/agent/run/swarm \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -d "{
    \"input\": \"https://example.com/api/users?id=1\",
    \"source\": \"$STORAGE_URL\",
    \"upload_results\": true,
    \"triage\": true
  }" | jq .

# 3. After scan completes, download and extract results
curl -s -o results.tar.gz \
  http://localhost:9002/api/storage/results/<run-id> \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid"

tar xzf results.tar.gz

Run Autopilot with Local Source + Upload Results

curl -s -X POST http://localhost:9002/api/agent/run/autopilot \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -d '{
    "target": "http://localhost:3000",
    "source": "/home/user/src/my-app",
    "upload_results": true,
    "intensity": "balanced"
  }' | jq .

Run Swarm with GCS Source

curl -s -X POST http://localhost:9002/api/agent/run/swarm \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "X-Project-UUID: my-project-uuid" \
  -d '{
    "input": "https://example.com",
    "source": "gs://my-project-uuid/ugc/source-code.tar.gz",
    "upload_results": true,
    "code_audit": true,
    "triage": true
  }' | jq .

Storage URL in Scan Records

When upload_results is enabled, the storage_url field is populated on the Scan or AgenticScan record after upload completes. Native scan:
curl -s http://localhost:9002/api/scans/<scan-uuid> \
  -H "Authorization: Bearer <token>" | jq '.storage_url'
# "gs://my-project-uuid/native-scans/<scan-uuid>/results.tar.gz"
Agentic scan:
curl -s http://localhost:9002/api/agent/sessions/<run-id> \
  -H "Authorization: Bearer <token>" | jq '.storage_url'
# "gs://my-project-uuid/agentic-scans/<run-id>/results.tar.gz"