SBOM ledger retention policy

Purpose

Retention keeps ledger history bounded while preserving audit trails for compliance.

Configuration

Settings are bound from SbomService:Ledger (env prefix SBOM_SbomService__Ledger__):

Operations

Guarantees

Version-stream outbox retention

Ledger-version retention above and P6 envelope retention are separate bounds. The host declares sbom.versions as RemotelyConsumed only after SbomService:VersionStream:Enabled=true, because Findings checkpoints in its own physical database:

Consumers below retentionHorizonSeq receive requiresBootstrap=true and restart from the bounded live-version snapshot before resuming ordered catch-up. See version stream.

Activation validates the complete setup-owned P7 admission/retention contract before registering the producer or pruner. Missing/invalid values stop startup; no numeric deployment value is supplied by source. Enabled upload/retirement writes pause with typed 503 reasons before the configured row, WAL, or database-capacity threshold. See version stream.

Registry source run retention

sbom.registry_source_runs holds one row per registry discovery run. A run moves from Queued or Running to Completed, Failed or Cancelled. Every reader returns at most the newest SbomService:RegistrySources:MaxRunHistoryLimit runs of one source, and that option is limited to 500. A terminal run ranked past 500 in its source can never be read again.

The host reclaims those runs. A background sweep runs shortly after startup and then hourly. It deletes terminal runs ranked past the newest 500 of their source, in bounded batches. Queued and Running runs are never deleted, whatever their rank. The last-run summary on sbom.registry_sources is stored separately and is not affected.

Settings bind from SbomService:RegistrySourceRunRetention:

Doctor check doctor.sbomservice.retention.registry-run-backlog reports the total, active and past-window run counts. It is unhealthy when the sweep is disabled, when any terminal run is past the window, or when the table exceeds MaximumRunRows. The check counts rows with the same database function the sweep uses, so both use one predicate.

The routines are sbom.registry_source_run_retention_candidates(keep) and sbom.prune_registry_source_runs(keep, batch). They are created by startup migration 011_sbomservice_registry_run_retention.sql. Re-verify with the RegistrySourceRunRetentionTests class in StellaOps.SbomService.Persistence.Tests.

Retention classes

Every declared sbom table states its class in a forward COMMENT ON TABLE. Migration 010_sbomservice_retention_classes.sql covers 23 tables and migration 011_sbomservice_registry_run_retention.sql covers registry_source_runs. Each class was decided from the table’s writers and readers in the durable runtime, not from its name.

ClassTablesWhy
source-of-truthledger_versions, ledger_chains, ledger_audit, sbom_documents, analysis_jobs, lineage_edges, artifact_subjects, artifact_links, registry_sources, entrypoints, orchestrator_sources, orchestrator_control, watermarksThe platform writes these as the record and cannot rebuild them. Only the ledger has a reclamation policy: the version-count and optional age policy above. Audit rows, chain cursors and raw documents outlive pruned versions.
source-of-truthprojections, catalog, component_lookups, inventory_events, resolver_candidatesNo in-platform writer exists in the durable runtime. Rows arrive out-of-band and nothing can regenerate them, so they are retained as supplied. See the open gaps in the architecture dossier.
derived-rebuildabledependency_graphs, dependency_nodes, dependency_edgesExtracted from the retained raw document. The dependency-path sidecar backfill rebuilds a missing graph, and the rows follow their ledger version by cascade.
derived-rebuildableversion_events, asset_eventsDerived from stored projections. POST /internal/sbom/events/backfill replays version events, and reading a projection republishes its asset event.
operational-stateregistry_source_runsA run lifecycle with a terminal-row sweep, a size budget and a Doctor check. See the section above.

sbom.verdict_links is retired by migration 012_sbomservice_retire_verdict_links.sql. No deployable host ever registered its writer or reader. The migration drops the table only while it is empty; a populated table stops startup with SQLSTATE 55000 and its rows are kept.

Verification: retention classification receipt.