Schema Mapping Reference

This document maps StellaOps data storage across its three backing stores — PostgreSQL, Valkey, and RustFS (S3-compatible object storage) — organized by the module that owns each schema, keyspace, or path prefix. It is written for operators and service authors who need to know which module owns which data, where it lives, and how it is keyed.

Altitude note. This is an ownership-and-shape map, not a DDL reference. Exact column lists, indexes, and Valkey TTLs live in each service’s embedded migrations and configuration and drift over time; the tables below capture the durable ownership and intent. The authoritative, forward-only migration files in src/ are the source of truth — see the database auto-migration requirement (§2.7). When this page and the migrations disagree, the migrations win.

Table of Contents


1. Storage Overview

StellaOps uses three primary storage systems:

StoragePurposeTechnologyNotes
PostgreSQLCanonical persistent storePostgreSQL v16+Per-module schema isolation
ValkeyCache, queues, eventsValkey v8.0 (Redis-compatible)DPoP nonces, task streams
RustFSObject storageS3-compatible APIContent-addressed blobs

Storage Principles

  1. Schema Isolation: Each module owns its PostgreSQL schema
  2. Append-Only for Evidence: Advisory, VEX, and SBOM raw data is immutable (AOC)
  3. Content-Addressable: Blob storage uses SHA256 digest prefixes
  4. Deterministic Keys: Valkey keys follow predictable patterns
  5. TTL Management: Cache entries have explicit TTLs

2. PostgreSQL Schema Ownership

Schema-to-Module Mapping

SchemaOwner ModulePurposeKey Tables
authorityAuthorityIdentity, clients, keys, auth auditclients, keys, tokens, audit_trail
scannerScannerScan manifests, triage, metadatascans, artifacts, images, layers
vulnConcelierAdvisory raw documents, linksetsadvisory_raw, linksets, observations
vexExcititorVEX raw statements, consensusvex_raw, consensus, issuer_trust
policyPolicyPolicies, exceptions, findingspolicies, exceptions, effective_finding_*
schedulerScheduler (JobEngine)Jobs, runs, schedulesschedules, runs, impact_snapshots
notifyNotifierRules, channels, delivery historyrules, channels, templates, delivery_log
orchestratorReleaseOrchestratorWorkflows, tasksworkflows, tasks, task_runs
registryRegistryToken service, image metadatatokens, repositories
symbolsSymbolsSymbol resolutionsymbols, debug_info
unknownsUnknownsUnknown componentsunknown_components, tracking

Detailed Schema Definitions (authoritative source)

The exact column definitions for every schema are not reproduced here — they drift. Each service owns its DDL as embedded SQL migrations that auto-apply on startup (see database auto-migration requirement, §2.7). The canonical, forward-only migration files are the source of truth; the summaries below capture only the shape and intent.

SchemaOwner moduleWhat it holds (shape)Canonical migrations (src/)
authorityAuthorityOAuth clients, signing keys, tokens, auth audit trailsrc/Authority/__Libraries/StellaOps.Authority.Persistence/Migrations/
scannerScannerScans, artifacts (sha256: keyed), images, layers, join tablessrc/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/
vulnConcelierAdvisory raw documents (append-only, AOC), linksets, observationssrc/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/
vexExcititorVEX raw statements (append-only, AOC), consensus, issuer trustsrc/Concelier/__Libraries/StellaOps.Excititor.Persistence/Migrations/
policyPolicyPolicies + compiled IR, runs, exceptions, approval audit; dynamic per-policy effective_finding_{policyId}[_history] tablessrc/Policy/__Libraries/StellaOps.Policy.Persistence/Migrations/
schedulerScheduler (JobEngine)Schedules, runs, run summariessrc/JobEngine/**/Migrations/
notifyNotifierRouting rules, channels, templates, delivery logsrc/Notify/__Libraries/StellaOps.Notify.Persistence/Migrations/
orchestratorReleaseOrchestratorAgent task / registration / capability storessrc/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Agent/Migrations/

Invariants worth carrying (verify against the migrations, not this table):

The shared migration runner (AddStartupMigrations) lives in src/__Libraries/StellaOps.Infrastructure.Postgres/Migrations/. See also the consolidated schema doc, ../DATA_SCHEMAS.md.


3. Valkey Keyspace Reference

Key Patterns by Module

ModuleKey PatternTypeTTLPurpose
Authoritydpop:{jti}string5mDPoP nonce cache (RFC 9449)
Scannerscan:{digest}stringInfiniteLast scan JSON result
Scannerlayers:{digest}set90dLayers with SBOMs (delta cache)
Scannerlocator:{imageDigest}string30dImage digest to sbomBlobId mapping
Policypolicy:activestringInfiniteActive policy YAML/Rego
Policypolicy:historylistInfiniteChange audit IDs
Concelierfeed:nvd:jsonstring24hNormalized feed snapshot
Generalquota:{token}stringUntil UTC midnightPer-token scan counter
Schedulerscheduler:jobsstream-Job queue
Notifiernotify:deliverystream7dDelivery events
Allevents:*stream7dEvent streams
Allqueue:*stream-Task queues
Telemetrymetrics:*various-Runtime metrics

Stream Definitions

StreamProducersConsumersEvents
scanner:eventsScanner.WorkerPolicy, Notify, TimelineIndexer, ExportCenterscan.submitted, scan.completed, scan.failed
concelier:driftConcelier.WorkerScheduler, Policy, Notifyadvisory.new, advisory.updated
policy:evaluatedPolicy.WorkerNotify, Findings, ExportCenterevaluation.completed, verdict.changed
scheduler:jobsSchedulerScanner, Policyrun.started, rescan.triggered
notify:deliveryNotifierAudit, TimelineIndexernotification.sent, notification.failed

Valkey Configuration

Streams use Redis consumer groups (e.g. scanner-workers on scanner:jobs, notify-workers on notify:delivery) with an LRU eviction policy. Concrete maxmemory, eviction, and consumer-group tuning are deployment concerns — see the compose/Valkey configuration under devops/compose/ rather than pinning values here.


4. RustFS (S3) Path Conventions

Blob Storage Layout

blobs/
+-- {sha256_prefix}/               # First 4 chars of digest
    +-- {full_digest}/
        +-- sbom.json              # SBOM payload (any format)
        +-- sbom.meta.json         # Wrapper envelope
        +-- sbom.cdx.pb            # CycloneDX Protobuf (compact)
        +-- attestation.dsse       # DSSE envelope
        +-- evidence.bundle        # Evidence package

images/
+-- {imageDigest}/
    +-- inventory.cdx.json         # Inventory SBOM
    +-- inventory.cdx.pb           # Inventory (Protobuf)
    +-- usage.cdx.json             # Usage SBOM (entrypoint closure)
    +-- usage.cdx.pb               # Usage (Protobuf)
    +-- call-graph.json            # Call graph data
    +-- reachability.json          # Reachability analysis

evidence/
+-- {bundleId}/
    +-- manifest.json              # Bundle manifest
    +-- manifest.dsse              # Signed manifest
    +-- sboms/                     # SBOM files
    +-- attestations/              # Attestation files
    +-- proofs/                    # Verification proofs

offline-kits/
+-- {kitId}/
    +-- feeds/                     # Advisory snapshots
    +-- images/                    # Container images
    +-- signatures/                # DSSE signatures
    +-- trust-roots/               # CA certificates
    +-- manifest.json              # Kit manifest

SBOM Wrapper Envelope

{
  "id": "sha256:417f...",
  "imageDigest": "sha256:e2b9...",
  "created": "2025-01-02T15:30:00Z",
  "format": "cdx-json",
  "layers": [
    "sha256:d38b...",
    "sha256:af45..."
  ],
  "partial": false,
  "provenanceId": "prov_0291"
}

Content Types

ExtensionContent-TypeDescription
.jsonapplication/jsonJSON documents
.cdx.jsonapplication/vnd.cyclonedx+jsonCycloneDX JSON
.cdx.pbapplication/vnd.cyclonedx+protobufCycloneDX Protobuf
.spdx.jsonapplication/spdx+jsonSPDX JSON
.dsseapplication/vnd.dsse+jsonDSSE envelope
.bundleapplication/zipEvidence bundle

5. Module-to-Storage Mapping

Complete Reference Table

ModulePostgreSQL SchemaValkey KeysRustFS Paths
Authorityauthoritydpop:{jti}-
Gateway- (stateless)--
Router-connection state-
Scannerscannerscan:{digest}, layers:{digest}, locator:{imageDigest}blobs/, images/
Conceliervulnfeed:*, concelier:drift stream-
Excititorvex--
VexLens- (reads vex)--
VexHubvex (extension)--
IssuerDirectory- (reads vex.issuer_trust)--
Policypolicypolicy:active, policy:history-
RiskEngine- (reads policy)--
Schedulerschedulerscheduler:jobs stream-
Notifiernotifynotify:delivery stream-
Orchestratororchestratororchestrator:* streams-
Attestor- (uses scanner)-blobs/*/attestation.dsse
Signer- (uses authority)--
SbomService- (reads scanner)-blobs/, images/
EvidenceLocker--evidence/
ExportCenter--evidence/, offline-kits/
AirGap--offline-kits/
Registryregistry--
Symbolssymbols--
Unknownsunknowns--
TimelineIndexer- (writes to scanner, etc.)--

6. Cross-References

Data Flow Dependencies

+---------+     +-----------+     +--------+     +--------+
| Scanner |---->| SbomService|---->| Policy |---->| Notify |
+---------+     +-----------+     +--------+     +--------+
     |                |                |              |
     v                v                v              v
+----------+    +-----------+    +-----------+   +---------+
| scanner  |    |  blobs/   |    |  policy   |   | notify  |
| (PG)     |    |  (RustFS) |    |  (PG)     |   | (PG)    |
+----------+    +-----------+    +-----------+   +---------+

+-----------+     +----------+
| Concelier |---->|  Policy  |
+-----------+     +----------+
     |                 ^
     v                 |
+----------+     +-----------+
|   vuln   |     | Excititor |
|  (PG)    |     +-----------+
+----------+           |
                       v
                 +----------+
                 |   vex    |
                 |  (PG)    |
                 +----------+

Schema Version Tracking

All schemas support versioning:

-- Every schema has a version tracking table
schema_migrations (
  version INTEGER PRIMARY KEY,
  description TEXT,
  applied_at TIMESTAMPTZ,
  checksum VARCHAR(64)
);

Backup Considerations

StorageBackup StrategyRetention
PostgreSQLpg_dump + WAL archiving30 days
ValkeyRDB snapshots + AOF7 days
RustFSObject versioning90 days