Data Schemas & Persistence Contracts

This reference describes the persistence contracts behind Stella Ops: the Valkey keyspace, the PostgreSQL tables, and the on-disk blob shapes that the scanner, policy, scheduler, and notify subsystems read and write.

Audience — backend developers, plug-in authors, and database administrators.

Altitude note. This document captures the shape, contracts, and invariants of each schema. Exhaustive field/enum/key lists live in source and are the authority — when this doc and the code disagree, the code wins. Each section points at the canonical model, JSON Schema, or sample fixture.


0 Document Conventions


1 SBOM Wrapper Envelope

Every SBOM blob (regardless of format) is stored on disk or in object storage with a sidecar JSON file that indexes it for the scanners.

1.1 JSON Shape

A wrapper records the digest of the SBOM file (id), the source imageDigest, created, the SBOM format (trivy-json-v2 | spdx-json | cyclonedx-json), the ordered layers[], a partial flag, and a planned provenanceId link to an SLSA attestation (⭑).

{
  "id": "sha256:417f…",          // digest of the SBOM file itself
  "imageDigest": "sha256:e2b9…", // digest of the original container image
  "format": "trivy-json-v2",     // trivy-json-v2 | spdx-json | cyclonedx-json
  "layers": ["sha256:d38b…"],    // ordered layer digests
  "partial": false               // true => delta SBOM (only some layers)
}

formatsupports multiple SBOM formats; partialis true when generated via the delta SBOM flow (§1.3).

1.2 File‑system Layout

blobs/
 └─ 417f…/             # digest prefix
     ├─ sbom.json      # payload (any format)
     └─ sbom.meta.json # wrapper (shape above)

Note – RustFS is the primary object store; S3/MinIO compatibility layer available for legacy deployments; driver plug‑ins support multiple backends.

1.3 Delta SBOM Extension

When partial: true, only the missing layers have been scanned. Merging logic inside the Scanner stitches new data onto the cached full SBOM in Valkey, keyed off layers:* (§2).


2 Valkey Keyspace

Valkey (Redis-compatible) provides cache, DPoP nonces, event streams, and queues for real-time messaging and rate limiting.

Key patternTypeTTLPurpose
scan:<digest>stringLast scan JSON result (as returned by /scan)
layers:<digest>set90dLayers already possessing SBOMs (delta cache)
policy:activestringYAML or Rego ruleset
quota:<token>stringuntil next UTC midnightPer‑token scan counter for Free tier ({{quota_token}} scans).
policy:historylistChange audit IDs (see PostgreSQL)
feed:nvd:jsonstring24hNormalised feed snapshot
locator:<imageDigest>string30dMaps image digest → sbomBlobId
dpop:<jti>string5mDPoP nonce cache (RFC 9449) for sender-constrained tokens
events:*stream7dEvent streams for Scheduler/Notify (Valkey Streams)
queue:*streamTask queues (Scanner jobs, Notify deliveries)
metrics:…variousProm / OTLP runtime metrics

Delta SBOM uses layers:* to skip work in <20 ms. Quota enforcement increments quota:<token> atomically; once the counter reaches the {{quota_token}}-scan ceiling the API returns 429. DPoP & Events: Valkey Streams support high-throughput, ordered event delivery for re-evaluation and notification triggers. Alternative: NATS JetStream can replace Valkey for queues (opt-in only; requires explicit configuration).


3 PostgreSQL Tables

PostgreSQL is the canonical persistent store for long-term audit and history.

TableShape (summary)Indexes
sbom_historyWrapper JSON + replace_ts on overwrite(image_digest) (created)
policy_versions{id, yaml, rego, author_id, created, comment}(created)
attestationsSLSA provenance doc + Rekor log pointer(image_digest)
audit_logFully rendered RFC 5424 entries (UI & CLI actions)(user_id) (ts)

policy_versions stores the YAML ruleset plus an optional rego field (filled only when Rego is uploaded), the authorId, created, and an import comment.

3.1 Scheduler Artifacts

Tables. schedules, runs, impact_snapshots, audit, run_summaries (module-local). All rows use the canonical JSON emitted by StellaOps.Scheduler.Models so agents and fixtures remain deterministic.

Canonical source — models: src/JobEngine/StellaOps.Scheduler.__Libraries/StellaOps.Scheduler.Models/ (Schedule.cs, Run.cs, ImpactSet.cs, AuditRecord.cs, RunSummary.cs, CanonicalJsonSerializer.cs, Validation.cs). Canonical source — fixtures: samples/api/scheduler/ (schedule.json, run.json, impact-set.json, audit.json, run-summary.json). Use these for the exhaustive field-by-field shape.

The shapes and their load-bearing invariants:


4 Policy Schema (YAML v1.0)

Minimal viable grammar (subset of OSV‑SCHEMA ideas) — a version plus a list of rules, each with a name, severity[], an action (block | ignore | escalate), and optional environments, sources, and expires.

version: "1.0"
rules:
  - name: Block Critical
    severity: [Critical]
    action: block
  - name: Ignore Low Dev
    severity: [Low, None]
    environments: [dev, staging]
    action: ignore
    expires: "2026-01-01"

Canonical schema: src/Policy/__Libraries/StellaOps.Policy/Schemas/policy-schema@1.json (embedded into StellaOps.Policy). Reusable validator: src/Policy/__Libraries/StellaOps.Policy/PolicyValidationCli.cs (PolicyValidationCli.RunAsync(PolicyValidationCliOptions{ Inputs, Strict })) — the command handler the main CLI wires up.

4.1 Rego Variant (Advanced ⭑)

Accepted but stored as-is in the rego field. Planned for evaluation via an internal OPA side-car once the feature ships.

4.2 Policy Scoring Config (JSON)

Configures how findings are scored: version, a severityWeights map, action penalties (quietPenalty / warnPenalty / ignorePenalty), trustOverrides, reachabilityBuckets (numeric multipliers, default ≤1.0), and an unknownConfidence block (initial / decayPerDay / floor plus ordered confidence bands).

Schema id: https://schemas.stella-ops.org/policy/policy-scoring-schema@1.json Source: src/Policy/__Libraries/StellaOps.Policy/Schemas/policy-scoring-schema@1.json; default fixture …/Schemas/policy-scoring-default.json (both embedded in StellaOps.Policy). The exhaustive default weights/keys live there.

Validation runs alongside policy binding (PolicyScoringConfigBinder), producing deterministic digests via PolicyScoringConfigDigest. Bands are ordered descending by min so consumers resolve confidence tiers deterministically. Reachability bucket keys are case-insensitive.

Runtime usage (contract)

Validate the sample payloads with Ajv (--spec=draft2020 -c ajv-formats) against their published schemas before changing them; see the Docs CI validation loop in §6.


5 SLSA Attestation Schema ⭑

Planned for Q1‑2026 (kept here for early plug‑in authors). A provenance doc carries id, imageDigest, buildType, a builder, build metadata (invocation parameters + timestamps + completeness), materials[], and a rekorLogIndex into the local Rekor mirror — following the SLSA provenance v1 layout.


6 Notify Foundations (Rule · Channel · Template · Event)

Canonically describes the Notify data shapes that UI, workers, and storage consume. JSON Schemas live under docs/modules/notify/resources/schemas/; deterministic fixtures under docs/modules/notify/resources/samples/.

ArtifactSchemaSample
Rule (catalogued routing logic)…/schemas/notify-rule@1.json…/samples/notify-rule@1.sample.json
Channel (delivery endpoint definition)…/schemas/notify-channel@1.json…/samples/notify-channel@1.sample.json
Template (rendering payload)…/schemas/notify-template@1.json…/samples/notify-template@1.sample.json
Event envelope (Notify ingest surface)…/schemas/notify-event@1.json…/samples/notify-event@1.sample.json

Cross-cutting invariants (all four artifacts):

Per-artifact notes (shape is in the schema; these are the contracts that aren’t):

Validation loop (matches Docs CI): compile every …/schemas/*.json with npx ajv compile -c ajv-formats, then validate each *.sample.json against its sibling schema with npx ajv validate. Integration tests embed the sample fixtures to guarantee deterministic serialisation from the StellaOps.Notify.Models DTOs.


7 Validator Contracts


8 Migration Notes

  1. Add format column to existing SBOM wrappers; default to trivy-json-v2.
  2. Populate layers & partialvia backfill script (ships with stellopsctl migrate wizard).
  3. Policy YAML previously stored in Valkey → copy to PostgreSQL if persistence enabled.
  4. Prepare attestations table (empty) – safe to create in advance.

9 Open Questions / Future Work


10 Change Log

DateNote
2025‑07‑14Added: format, partial, delta cache keys, YAML policy schema v1.0.
2025‑07‑12Initial public draft – SBOM wrapper, Valkey keyspace, audit collections.