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
- 2. PostgreSQL Schema Ownership
- 3. Valkey Keyspace Reference
- 4. RustFS (S3) Path Conventions
- 5. Module-to-Storage Mapping
- 6. Cross-References
1. Storage Overview
StellaOps uses three primary storage systems:
| Storage | Purpose | Technology | Notes |
|---|---|---|---|
| PostgreSQL | Canonical persistent store | PostgreSQL v16+ | Per-module schema isolation |
| Valkey | Cache, queues, events | Valkey v8.0 (Redis-compatible) | DPoP nonces, task streams |
| RustFS | Object storage | S3-compatible API | Content-addressed blobs |
Storage Principles
- Schema Isolation: Each module owns its PostgreSQL schema
- Append-Only for Evidence: Advisory, VEX, and SBOM raw data is immutable (AOC)
- Content-Addressable: Blob storage uses SHA256 digest prefixes
- Deterministic Keys: Valkey keys follow predictable patterns
- TTL Management: Cache entries have explicit TTLs
2. PostgreSQL Schema Ownership
Schema-to-Module Mapping
| Schema | Owner Module | Purpose | Key Tables |
|---|---|---|---|
authority | Authority | Identity, clients, keys, auth audit | clients, keys, tokens, audit_trail |
scanner | Scanner | Scan manifests, triage, metadata | scans, artifacts, images, layers |
vuln | Concelier | Advisory raw documents, linksets | advisory_raw, linksets, observations |
vex | Excititor | VEX raw statements, consensus | vex_raw, consensus, issuer_trust |
policy | Policy | Policies, exceptions, findings | policies, exceptions, effective_finding_* |
scheduler | Scheduler (JobEngine) | Jobs, runs, schedules | schedules, runs, impact_snapshots |
notify | Notifier | Rules, channels, delivery history | rules, channels, templates, delivery_log |
orchestrator | ReleaseOrchestrator | Workflows, tasks | workflows, tasks, task_runs |
registry | Registry | Token service, image metadata | tokens, repositories |
symbols | Symbols | Symbol resolution | symbols, debug_info |
unknowns | Unknowns | Unknown components | unknown_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.
| Schema | Owner module | What it holds (shape) | Canonical migrations (src/) |
|---|---|---|---|
authority | Authority | OAuth clients, signing keys, tokens, auth audit trail | src/Authority/__Libraries/StellaOps.Authority.Persistence/Migrations/ |
scanner | Scanner | Scans, artifacts (sha256: keyed), images, layers, join tables | src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/ |
vuln | Concelier | Advisory raw documents (append-only, AOC), linksets, observations | src/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/ |
vex | Excititor | VEX raw statements (append-only, AOC), consensus, issuer trust | src/Concelier/__Libraries/StellaOps.Excititor.Persistence/Migrations/ |
policy | Policy | Policies + compiled IR, runs, exceptions, approval audit; dynamic per-policy effective_finding_{policyId}[_history] tables | src/Policy/__Libraries/StellaOps.Policy.Persistence/Migrations/ |
scheduler | Scheduler (JobEngine) | Schedules, runs, run summaries | src/JobEngine/**/Migrations/ |
notify | Notifier | Routing rules, channels, templates, delivery log | src/Notify/__Libraries/StellaOps.Notify.Persistence/Migrations/ |
orchestrator | ReleaseOrchestrator | Agent task / registration / capability stores | src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Agent/Migrations/ |
Invariants worth carrying (verify against the migrations, not this table):
- Append-only evidence (AOC):
vuln.advisory_rawandvex.vex_rawstore the original JSON as-received and are immutable; ingest must not mutate, only append revisions. - Content-addressed keys: scanner artifacts are keyed by
sha256:digest, not surrogate IDs. - Compiled-IR + hash:
policy.policiesstores both source YAML and a hashed compiled IR so evaluation is deterministic and replayable. - Dynamic finding tables: Policy creates
effective_finding_{policyId}(snapshot) andeffective_finding_{policyId}_history(audit) per policy rather than one wide table.
The shared migration runner (
AddStartupMigrations) lives insrc/__Libraries/StellaOps.Infrastructure.Postgres/Migrations/. See also the consolidated schema doc,../DATA_SCHEMAS.md.
3. Valkey Keyspace Reference
Key Patterns by Module
| Module | Key Pattern | Type | TTL | Purpose |
|---|---|---|---|---|
| Authority | dpop:{jti} | string | 5m | DPoP nonce cache (RFC 9449) |
| Scanner | scan:{digest} | string | Infinite | Last scan JSON result |
| Scanner | layers:{digest} | set | 90d | Layers with SBOMs (delta cache) |
| Scanner | locator:{imageDigest} | string | 30d | Image digest to sbomBlobId mapping |
| Policy | policy:active | string | Infinite | Active policy YAML/Rego |
| Policy | policy:history | list | Infinite | Change audit IDs |
| Concelier | feed:nvd:json | string | 24h | Normalized feed snapshot |
| General | quota:{token} | string | Until UTC midnight | Per-token scan counter |
| Scheduler | scheduler:jobs | stream | - | Job queue |
| Notifier | notify:delivery | stream | 7d | Delivery events |
| All | events:* | stream | 7d | Event streams |
| All | queue:* | stream | - | Task queues |
| Telemetry | metrics:* | various | - | Runtime metrics |
Stream Definitions
| Stream | Producers | Consumers | Events |
|---|---|---|---|
scanner:events | Scanner.Worker | Policy, Notify, TimelineIndexer, ExportCenter | scan.submitted, scan.completed, scan.failed |
concelier:drift | Concelier.Worker | Scheduler, Policy, Notify | advisory.new, advisory.updated |
policy:evaluated | Policy.Worker | Notify, Findings, ExportCenter | evaluation.completed, verdict.changed |
scheduler:jobs | Scheduler | Scanner, Policy | run.started, rescan.triggered |
notify:delivery | Notifier | Audit, TimelineIndexer | notification.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
| Extension | Content-Type | Description |
|---|---|---|
.json | application/json | JSON documents |
.cdx.json | application/vnd.cyclonedx+json | CycloneDX JSON |
.cdx.pb | application/vnd.cyclonedx+protobuf | CycloneDX Protobuf |
.spdx.json | application/spdx+json | SPDX JSON |
.dsse | application/vnd.dsse+json | DSSE envelope |
.bundle | application/zip | Evidence bundle |
5. Module-to-Storage Mapping
Complete Reference Table
| Module | PostgreSQL Schema | Valkey Keys | RustFS Paths |
|---|---|---|---|
| Authority | authority | dpop:{jti} | - |
| Gateway | - (stateless) | - | - |
| Router | - | connection state | - |
| Scanner | scanner | scan:{digest}, layers:{digest}, locator:{imageDigest} | blobs/, images/ |
| Concelier | vuln | feed:*, concelier:drift stream | - |
| Excititor | vex | - | - |
| VexLens | - (reads vex) | - | - |
| VexHub | vex (extension) | - | - |
| IssuerDirectory | - (reads vex.issuer_trust) | - | - |
| Policy | policy | policy:active, policy:history | - |
| RiskEngine | - (reads policy) | - | - |
| Scheduler | scheduler | scheduler:jobs stream | - |
| Notifier | notify | notify:delivery stream | - |
| Orchestrator | orchestrator | orchestrator:* streams | - |
| Attestor | - (uses scanner) | - | blobs/*/attestation.dsse |
| Signer | - (uses authority) | - | - |
| SbomService | - (reads scanner) | - | blobs/, images/ |
| EvidenceLocker | - | - | evidence/ |
| ExportCenter | - | - | evidence/, offline-kits/ |
| AirGap | - | - | offline-kits/ |
| Registry | registry | - | - |
| Symbols | symbols | - | - |
| Unknowns | unknowns | - | - |
| 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
| Storage | Backup Strategy | Retention |
|---|---|---|
| PostgreSQL | pg_dump + WAL archiving | 30 days |
| Valkey | RDB snapshots + AOF | 7 days |
| RustFS | Object versioning | 90 days |
Related Documentation
- Data Schemas - Detailed schema definitions
- Data Flows - How data moves through the system
- Data Isolation - Per-tenant isolation model
- Module Matrix - Complete module inventory
- Request Flows - HTTP/Binary protocol flows
