10 KiB
Stella Ops vNext - SBOM Spine and Deterministic Evidence
Status: IMPLEMENTED — Architecture overview document Date: 2025-12-26 Updated: 2025-12-26 Type: Vision Document / Architecture Summary Implementation: 100% complete in existing infrastructure
Implementation Status
This advisory describes the existing StellaOps architecture. All proposed features are implemented:
Core Infrastructure
| Component | Implementation | Location |
|---|---|---|
| SBOM-first canonical graph | SbomService module |
src/SbomService/ |
| CycloneDX 1.6 + SPDX 3.x | SbomNormalizationService |
src/SbomService/.../Services/ |
| in-toto + DSSE attestations | Attestor module (6+ predicate types) |
src/Attestor/ |
| OCI referrers | OciReferrerPushClient, OciReferrerDiscovery |
src/ExportCenter/.../Oci/ |
| Cosign/Sigstore signatures | Signer module |
src/Signer/ |
| Regional crypto (eIDAS/FIPS/GOST/SM) | Cryptography module |
src/Cryptography/ |
| Evidence graph | ProofChain library |
src/Attestor/__Libraries/StellaOps.Attestor.ProofChain/ |
| Smart-diff algorithm | DeltaComputationEngine |
src/__Libraries/StellaOps.DeltaVerdict/ |
| Signed delta verdicts | DeltaSigningService |
src/__Libraries/StellaOps.DeltaVerdict/Signing/ |
| Content-addressed IDs | IContentAddressedIdGenerator |
src/Attestor/__Libraries/.../Identifiers/ |
| Decision digest (Provcache) | DecisionDigest, VeriKey |
src/__Libraries/StellaOps.Provcache/ |
| Merkle proof verification | MerkleProofVerifier |
src/Attestor/__Libraries/.../Merkle/ |
| Deterministic replay | ReplaySeed, FrozenEpoch |
src/__Libraries/StellaOps.Provcache/ |
| PostgreSQL + Valkey | All modules | Per-module storage |
Predicate Types (All Implemented)
| Predicate | Type URI | Implementation |
|---|---|---|
| Build Provenance | StellaOps.BuildProvenance@1 |
Attestor.ProofChain |
| SBOM Attestation | StellaOps.SBOMAttestation@1 |
Attestor.ProofChain |
| Scan Results | StellaOps.ScanResults@1 |
Attestor.ProofChain |
| Policy Evaluation | StellaOps.PolicyEvaluation@1 |
Attestor.ProofChain |
| VEX Attestation | StellaOps.VEXAttestation@1 |
Attestor.ProofChain |
| Risk Profile Evidence | StellaOps.RiskProfileEvidence@1 |
Attestor.ProofChain |
Implementation Checklist (All Complete)
Pipelines:
- Build: emit SBOM (CDX + SPDX), SLSA provenance (in-toto/DSSE), sign all
- Scan: OS + language + config; one attestation per tool
- Policy: evaluate rules -> signed verdict attestation; include unknowns count
- Publish: push all as OCI referrers; enable verification gate
Schema & IDs:
- Normalize component IDs (PURL/CPE) + strong hashes
- Evidence graph store: Postgres (authoritative) + cache (Valkey)
- Index by image digest; maintain as-of snapshots
Determinism:
- Lock feeds, rule versions, tool versions; record all input digests
- Provide replay manifest capturing inputs -> expected verdict hash
Security & Sovereignty:
- Pluggable crypto: eIDAS/FIPS/GOST/SM; offline bundle export/import
- Air-gapped profile: Postgres-only with documented trade-offs
Documentation References
- SbomService Architecture:
docs/modules/sbomservice/architecture.md - Attestor Architecture:
docs/modules/attestor/architecture.md - Signer Keyless Guide:
docs/modules/signer/guides/keyless-signing.md - ProofChain Specification:
docs/modules/attestor/proof-chain-specification.md - Determinism Gates:
docs/testing/determinism-gates.md
Advisory Content (Original Vision)
Here's a simple, practical way to think about a SBOM-first, VEX-ready supply-chain spine and the evidence graph + smart-diff you can build on top of it—starting from zero and ending with reproducible, signed decisions.
SBOM-first spine (VEX-ready)
Goal: make the SBOM the canonical graph of "what's inside," then layer signed evidence (build, scans, policy) so every verdict is portable, replayable, and auditable across registries.
Core choices:
- Canonical graph: treat CycloneDX 1.6 and SPDX 3.x as first-class. Keep both in sync; normalize component IDs (PURL/CPE), hashes, licenses, and relationships.
- Attestations: use in-toto + DSSE for all lifecycle facts:
- build (SLSA provenance),
- scan results (vuln, secrets, IaC, reachability),
- policy evaluation (allow/deny, risk budgets, exceptions).
- Storage/transport: publish everything as OCI-attached artifacts via OCI Referrers:
image:tag-> SBOM (spdx/cdx), VEX, SARIF, provenance, policy verdicts, exception notes—each a referrer with media type + signature.
- Signatures: cosign/sigstore (or your regional crypto: eIDAS/FIPS/GOST/SM) for content-addressed blobs.
Minimum viable workflow:
-
Build step
- Produce identical SBOMs in CycloneDX and SPDX.
- Emit SLSA-style provenance attestation.
-
Scan step(s)
- OS + language deps + container layers; add reachability proofs where possible.
- Emit one scan attestation per tool (don't conflate).
-
Policy step
- Evaluate policies (e.g., OPA/Rego or your lattice rules) against the SBOM graph + scan evidence.
- Emit a signed policy verdict attestation (pass/fail + reasons + unknowns count).
-
Publish
- Push image, then push SBOMs, VEX, scan attestations, policy verdicts as OCI referrers.
-
Verify / consume
- Pull the image's referrer set; verify signatures; reconstruct graph locally; replay the policy evaluation deterministically.
Data model tips:
- Stable identifiers: PURLs for packages, digests for layers, Build-ID for binaries.
- Edges:
component->dependsOn,component->vulnerability,component->evidence(attestation),component->policyClaim. - Keep time (as-of) and source on every node/edge for replay.
Evidence graph + smart-diff
Goal: persist an explainability graph (findings <-> components <-> provenance <-> policies) and compute signed delta-verdicts on diffs to drive precise impact analysis and quiet noise.
What to store:
- Provenance: who built it, from what, when (commit, builder, materials).
- Findings: CVEs, misconfigs, secrets, license flags, each with source tool, version, rule, confidence, timestamp.
- Policies & verdicts: rule set version, inputs' hashes, outcome, rationale.
- Reachability subgraphs: the minimal path proving exploitability (e.g., symbol -> function -> package -> process start).
Smart-diff algorithm (high level):
- Compare two images (or SBOM graphs) by component identity + version + hash.
- For each change class:
- Added/removed/changed component
- New/cleared/changed finding
- Changed reachability path
- Changed policy version/inputs
- Re-evaluate only affected subgraph; produce a Delta Verdict:
status: safer / risk-equal / risk-higherwhy: list of net-new reachable vulns, removed reachable vulns, policy/exception impactsevidenceRefs: hashes of attestations used
- Sign the delta verdict (DSSE) and publish it as an OCI referrer too.
UX essentials:
- Artifact page shows: "Evidence Stack" (SBOM, scans, VEX, policy, provenance) with green checks for signatures.
- Smart-diff view: left vs right image -> "net-new reachable CVEs (+3)", "downgraded risk (-1)" with drill-downs to the exact path/evidence.
- Explain button: expands to show why a CVE is (not) applicable (feature flag off, code path unreachable, kernel mitigation present, etc.).
- Replay badge: "Deterministic ✓" (inputs' hashes match; verdict reproducible).
APIs & types (implemented media types)
application/vnd.cyclonedx+jsonapplication/spdx+jsonapplication/vnd.in-toto+json; statement=provenance|scan|policyapplication/vnd.stella.verdict+json(signed verdict/delta)
Existing Implementation Examples
Scan Attestation (StellaOps.ScanResults@1):
{
"type": "https://in-toto.io/Statement/v1",
"predicateType": "https://stella.dev/scan/v1",
"subject": [{"name": "registry/app@sha256:...", "digest": {"sha256": "..."}}],
"predicate": {
"tool": {"name": "scannerX", "version": "1.4.2"},
"inputs": {"sbom": "sha256:...", "db": "sha256:..."},
"findings": [{"id": "CVE-2025-1234", "component": "pkg:pypi/xyz@1.2.3", "severity": "HIGH"}]
}
}
Policy Verdict (StellaOps.PolicyEvaluation@1):
{
"type": "https://in-toto.io/Statement/v1",
"predicateType": "https://stella.dev/verdict/v1",
"subject": [{"name": "registry/app@sha256:..."}],
"predicate": {
"policy": {"id": "prod.v1.7", "hash": "sha256:..."},
"inputs": {"sbom": "sha256:...", "scans": ["sha256:...","sha256:..."]},
"unknowns": 2,
"decision": "allow",
"reasons": [
"CVE-2025-1234 not reachable (path pruned)",
"License policy ok"
]
}
}
Delta Verdict (stella.dev/delta-verdict/v1):
{
"predicateType": "https://stella.dev/delta-verdict/v1",
"predicate": {
"from": "sha256:old", "to": "sha256:new",
"impact": "risk-higher",
"changes": {
"componentsAdded": ["pkg:apk/openssl@3.2.1-r1"],
"reachableVulnsAdded": ["CVE-2025-2222"]
},
"evidenceRefs": ["sha256:scanA", "sha256:policyV1"]
}
}
Operating rules (adopted)
- Everything is evidence. If it influenced a decision, it's an attestation you can sign and attach.
- Same inputs -> same verdict. If not, treat it as a bug.
- Unknowns budgeted by policy. E.g., "fail prod if unknowns > 0; warn in dev."
- Diffs decide deployments. Gate on the delta verdict, not raw CVE counts.
- Portable by default. If you move registries, your decisions move with the image via referrers.
Related Implementation
| Module | Purpose | Documentation |
|---|---|---|
SbomService |
SBOM ledger, lineage, versioning | docs/modules/sbomservice/ |
Attestor |
DSSE, in-toto, Rekor integration | docs/modules/attestor/ |
Signer |
Keyless + KMS signing | docs/modules/signer/ |
Provcache |
Decision digest, VeriKey | src/__Libraries/StellaOps.Provcache/ |
DeltaVerdict |
Smart-diff engine | src/__Libraries/StellaOps.DeltaVerdict/ |
ExportCenter |
OCI referrer publishing | src/ExportCenter/ |
Policy |
Lattice evaluation, verdicts | src/Policy/ |