Files
git.stella-ops.org/docs/product-advisories/25-Dec-2025 - Building a Deterministic Verdict Engine.md
2025-12-25 20:15:19 +02:00

6.0 KiB
Raw Blame History

Heres a tight, practical blueprint for evolving StellaOpss policy engine into a fully deterministic verdict engine—so the same SBOM + VEX + reachability subgraph ⇒ the exact same, replayable verdict every time, with auditorgrade trails and signed “delta verdicts.”

Why this matters (quick)

  • Reproducibility: auditors can replay any scan and get identical results.
  • Trust & scale: crossagent consensus via contentaddressed inputs and signed outputs.
  • Operational clarity: diffs between builds become crisp, machineverifiable artifacts.

Core principles

  • Determinism-first: no wallclock time, no random iteration order, no network during evaluation.
  • Contentaddressing: hash every input (SBOM, VEX docs, reachability subgraph, policy set, rule versions, feed snapshots).
  • Declarative state: a compact Scan Manifest lists input hashes + policy bundle hash + engine version.
  • Pure evaluation: the verdict function is referentially transparent: Verdict = f(Manifest).

Data artifacts

  • Scan Manifest (manifest.jsonc)

    • sbom_sha256, vex_set_sha256[], reach_subgraph_sha256, feeds_snapshot_sha256, policy_bundle_sha256, engine_version, policy_semver, options_hash
  • Verdict (verdict.json)

    • canonical JSON (stable key order); includes:

      • risk_score, status (pass/warn/fail), unknowns_count
      • evidence_refs: content IDs for cited VEX statements, nodes/edges from reachability, CVE records, featureflags, envguards
      • explanations: stable, templatedriven strings (+ machine reasons)
  • Delta Verdict (delta.json)

    • computed between two manifests/verdicts:

      • added_findings[], removed_findings[], severity_shift[], unknowns_delta, policy_effects[]
    • signed (DSSE/COSE/JWS), timestamped, and linkable to both verdicts

Engine architecture (deterministic path)

  1. Normalize inputs

    • SBOM: sort by packageUrl/name@version; resolve aliases; freeze semver comparison rules.
    • VEX: normalize provider → vex_id, product_ref, status (affected, not_affected, …), with source trust score precomputed from a trust registry (strict, versioned).
    • Reachability: store subgraph as adjacency lists sorted by node ID; hash after topological stable ordering.
    • Feeds: lock to a snapshot (timestamp + commit/hash); no live calls.
  2. Policy bundle

    • Declarative rules (e.g., lattice/merge semantics), compiled to a canonical IR (e.g., OPARego → sorted DNF).
    • Merge precedence is explicit (e.g., vendor > distro > internal can be replaced by a latticemerge table).
    • Unknowns policy baked in: e.g., fail_if_unknowns > N in prod.
  3. Evaluation

    • Build a finding set: (component, vuln, context) tuples with deterministic IDs.
    • Apply latticebased VEX merge (proofcarrying): each suppression must carry an evidence pointer (feature flag off, code path unreachable, patchedbackport proof).
    • Compute final status and risk_score using fixedprecision math; round rules are part of the bundle.
  4. Emit

    • Canonicalize verdict JSON; attach evidence map (content IDs only).
    • Sign verdict; attach as OCI attestation to image/digest.

APIs (minimal but complete)

  • POST /evaluate → returns verdict.json + attestation
  • POST /delta with {base_verdict, head_verdict}delta.json (signed)
  • GET /replay?manifest_sha= → reexecutes using cached snapshot bundles, returns the same verdict_sha
  • GET /evidence/:cid → fetches immutable evidence blobs (offlineready)

Storage & indexing

  • CAS (contentaddressable store): /evidence/<sha256> for SBOM/VEX/graphs/feeds/policies.
  • Verdict registry: keyed by (image_digest, manifest_sha, engine_version).
  • Delta ledger: appendonly, signed; supports crossagent consensus (multiple engines can cosign identical deltas).

UI slices (where it lives)

  • Run details → “Verdict” tab: status, risk score, unknowns, top evidence links.
  • “Diff” tab: render Delta Verdict (added/removed/changed), with drilldown to proofs.
  • “Replay” button: shows the exact manifest & engine version; oneclick reevaluation (offline possible).
  • Audit export: zip of manifest.jsonc, verdict.json, delta.json (if any), attestation, and referenced evidence.

Testing & QA (musthave)

  • Golden tests: fixtures of manifests → frozen verdict JSONs (byteforbyte).
  • Chaos determinism tests: vary thread counts, env vars, map iteration seeds; assert identical verdicts.
  • Crossengine roundtrips: two independent builds of the engine produce the same verdict for the same manifest.
  • Timetravel tests: replay older feed snapshots to ensure stability.

Rollout plan

  1. Phase 1: Introduce Manifest + canonical verdict format alongside existing policy engine (shadow mode).
  2. Phase 2: Make verdicts the firstclass artifact (OCIattached); ship UI “Verdict/Diff”.
  3. Phase 3: Enforce deltagates in CI/CD (risk budgets + exception packs referenceable by content ID).
  4. Phase 4: Open consensus mode—accept externally signed identical delta verdicts to strengthen trust.

Notes for Stella modules

  • scanner.webservice: keep lattice algorithms here (per your standing rule). Concelier/Excitors “preserveprune source.”
  • Authority/Attestor: handle DSSE signing, key management, regional crypto profiles (eIDAS/FIPS/GOST/SM).
  • Feedser/Vexer: produce immutable snapshot bundles; never query live during evaluation.
  • Router/Scheduler: schedule replay jobs; cache manifests to speed up audits.
  • Db: Postgres as SoR; Valkey only for ephemeral queues/caches (per your BSDonly profile).

If you want, I can generate:

  • a sample Manifest + Verdict + Delta trio,
  • the canonical JSON schema,
  • and a .NET10 reference evaluator (deterministic LINQ pipeline + fixedprecision math) you can drop into scanner.webservice.