Skip to content

Persistence

Vega stores two categories of data: structured domain records (projects, repositories, scans, findings, events, users, billing) and large objects (source code archives, scan reports, debug bundles). These use different storage systems.

Structured domain records

Structured records are stored either as JSON files (local development) or in Postgres (production).

All domain entities extend DomainModel (a Pydantic v2 base class). Records are stored as JSONB documents in Postgres — there is no ORM and no per-entity SQL schema beyond a common payload jsonb column. This is a document store pattern layered on top of a relational database:

-- Example: all scan records in one table
CREATE TABLE scans (
    record_key   TEXT PRIMARY KEY,
    payload      JSONB NOT NULL,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

JSON files under data/ — easy to inspect, easy to reset, no database required. Controlled by VEGA_PERSISTENCE_BACKEND=json.

Each entity type gets its own JSON file (e.g., data/scans.json). The JSON adapter (adapters/storage/json/) reads and writes these files with record-level locking.

A managed Postgres database via AWS RDS/Aurora. All records are stored as JSONB documents in typed tables. Controlled by VEGA_PERSISTENCE_BACKEND=postgres.

The Postgres adapter (adapters/storage/postgres/) uses psycopg directly (no ORM). It serializes DomainModel instances to payload JSON on write and deserializes them on read.

Storage ports

The application layer never calls storage directly. It uses storage ports (interfaces) that are implemented by the adapters:

Port Description
ProjectStore WorkspaceProject CRUD
RepositoryStore Repository CRUD
SnapshotStore SourceSnapshot CRUD
IngestJobStore IngestJob CRUD
ScanStore ScanRecord CRUD
FindingStore FindingRecord CRUD
ArtifactStore ArtifactRecord CRUD
EventStore Append-only DomainEvent log
GenericRecordStore Document store for auxiliary records (users, API keys, billing, etc.)

Large objects

Large files are stored either in local directories (development) or in S3 (production).

Files under subdirectories of data/ — simple, no AWS credentials required. Controlled by VEGA_FILE_STORAGE_BACKEND=local.

The local object storage adapter (adapters/objects/local/) reads and writes files in data/snapshots/ and data/artifacts/.

S3 object storage across multiple named buckets. Durable, scalable, and accessible from any ECS task. Controlled by VEGA_FILE_STORAGE_BACKEND=s3.

In production, scan runners use scan-scoped STS credentials (ScanScopedS3Credentials) to access S3. This limits each runner to its own scan's artifacts.

Domain events

Domain events use a separate EventStore port. In Postgres, events are stored in the domain_events table (not in the generic JSONB pattern) with a monotonically increasing sequence number per aggregate:

CREATE TABLE domain_events (
    event_id     UUID PRIMARY KEY,
    event_type   TEXT NOT NULL,
    aggregate_id TEXT NOT NULL,
    payload      JSONB NOT NULL,
    sequence     BIGINT NOT NULL,
    occurred_at  TIMESTAMPTZ NOT NULL,
    UNIQUE (aggregate_id, sequence)
);

This ordering enables the frontend to efficiently poll for new events since a known sequence position.

Key files

File What it does
app/domain/base.py DomainModel — Pydantic v2 base for all records
app/adapters/storage/postgres/ PostgresRecordStore — all Postgres JSONB operations
app/adapters/storage/json/ JsonRecordStore — local JSON file store
app/adapters/events/postgres/ PostgresEventStore — domain_events table
app/adapters/events/json/ JsonEventStore — local JSON event log
app/adapters/objects/s3/ S3ObjectStorage — S3 upload/download/presigned URLs
app/adapters/objects/local/ LocalObjectStorage — local file storage
app/storage/migrations/ SQL migration files (001–028)
scripts/run-db-migrations.py Applies pending migrations locally
scripts/aws/run-migrations.sh Runs migrations via the ECS maintenance task

Pages in this section

  • Postgres — JSONB schema, the GenericRecordStore, and debugging
  • S3 and Artifacts — buckets, object keys, and what gets stored
  • Migrations — how to apply schema changes and the full migration list