# Proof Spine Assembly Algorithm > **Sprint:** SPRINT_0501_0004_0001 > **Module:** Attestor / ProofChain ## Overview The Proof Spine is the cryptographic backbone of StellaOps' proof chain. It aggregates evidence, reasoning, and VEX statements into a single merkle-rooted bundle that can be verified independently. ## Architecture ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ PROOF SPINE STRUCTURE │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ SBOMEntryID │ │ EvidenceID[] │ │ ReasoningID │ │ VEXVerdictID │ │ │ │ (leaf 0) │ │ (leaves 1-N) │ │ (leaf N+1) │ │ (leaf N+2) │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ │ └─────────────────┴─────────────────┴─────────────────┘ │ │ │ │ │ ▼ │ │ ┌───────────────────────────────┐ │ │ │ MERKLE TREE BUILDER │ │ │ │ - SHA-256 hash function │ │ │ │ - Lexicographic sorting │ │ │ │ - Power-of-2 padding │ │ │ └───────────────┬───────────────┘ │ │ │ │ │ ▼ │ │ ┌───────────────────────────────┐ │ │ │ ProofBundleID (Root) │ │ │ │ sha256:<64-hex-chars> │ │ │ └───────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ## Algorithm Specification ### Input | Parameter | Type | Description | |-----------|------|-------------| | `sbomEntryId` | string | Content-addressed ID of the SBOM entry | | `evidenceIds` | string[] | Array of evidence statement IDs | | `reasoningId` | string | ID of the reasoning/policy match statement | | `vexVerdictId` | string | ID of the VEX verdict statement | ### Output | Parameter | Type | Description | |-----------|------|-------------| | `proofBundleId` | string | Merkle root in format `sha256:<64-hex>` | ### Pseudocode ``` FUNCTION BuildProofBundleMerkle(sbomEntryId, evidenceIds[], reasoningId, vexVerdictId): // Step 1: Prepare leaves in deterministic order leaves = [] leaves.append(SHA256(UTF8.GetBytes(sbomEntryId))) // Step 2: Sort evidence IDs lexicographically sortedEvidenceIds = evidenceIds.Sort(StringComparer.Ordinal) FOR EACH evidenceId IN sortedEvidenceIds: leaves.append(SHA256(UTF8.GetBytes(evidenceId))) leaves.append(SHA256(UTF8.GetBytes(reasoningId))) leaves.append(SHA256(UTF8.GetBytes(vexVerdictId))) // Step 3: Pad to power of 2 (duplicate last leaf) WHILE NOT IsPowerOfTwo(leaves.Length): leaves.append(leaves[leaves.Length - 1]) // Step 4: Build tree bottom-up currentLevel = leaves WHILE currentLevel.Length > 1: nextLevel = [] FOR i = 0 TO currentLevel.Length STEP 2: left = currentLevel[i] right = currentLevel[i + 1] parent = SHA256(left || right) // Concatenate then hash nextLevel.append(parent) currentLevel = nextLevel // Step 5: Return root as formatted ID RETURN "sha256:" + HexEncode(currentLevel[0]) ``` ## Determinism Invariants | Invariant | Rule | Rationale | |-----------|------|-----------| | Evidence Ordering | Lexicographic (byte comparison) | Reproducible across platforms | | Hash Function | SHA-256 only | No algorithm negotiation | | Padding | Duplicate last leaf | Not zeros, preserves tree structure | | Concatenation | Left `\|\|` Right | Consistent ordering | | String Encoding | UTF-8 | Cross-platform compatibility | | ID Format | `sha256:` | Canonical representation | ## Example ### Input ```json { "sbomEntryId": "sha256:abc123...", "evidenceIds": [ "sha256:evidence-cve-2024-0001...", "sha256:evidence-reachability...", "sha256:evidence-sbom-component..." ], "reasoningId": "sha256:reasoning-policy...", "vexVerdictId": "sha256:vex-not-affected..." } ``` ### Processing 1. **Leaf 0**: `SHA256("sha256:abc123...")` → SBOM 2. **Leaf 1**: `SHA256("sha256:evidence-cve-2024-0001...")` → Evidence (sorted first) 3. **Leaf 2**: `SHA256("sha256:evidence-reachability...")` → Evidence 4. **Leaf 3**: `SHA256("sha256:evidence-sbom-component...")` → Evidence 5. **Leaf 4**: `SHA256("sha256:reasoning-policy...")` → Reasoning 6. **Leaf 5**: `SHA256("sha256:vex-not-affected...")` → VEX 7. **Padding**: Duplicate leaf 5 to get 8 leaves (power of 2) ### Tree Structure ``` ROOT / \ H1 H2 / \ / \ H3 H4 H5 H6 / \ / \ / \ / \ L0 L1 L2 L3 L4 L5 L5 L5 (padded) ``` ### Output ``` sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069 ``` ## Cross-Platform Verification ### Test Vector For cross-platform compatibility testing, use this known test vector: **Input:** ```json { "sbomEntryId": "sha256:0000000000000000000000000000000000000000000000000000000000000001", "evidenceIds": [ "sha256:0000000000000000000000000000000000000000000000000000000000000002", "sha256:0000000000000000000000000000000000000000000000000000000000000003" ], "reasoningId": "sha256:0000000000000000000000000000000000000000000000000000000000000004", "vexVerdictId": "sha256:0000000000000000000000000000000000000000000000000000000000000005" } ``` All implementations (C#, Go, Rust, TypeScript) must produce the same root hash. ## Verification To verify a proof bundle: 1. Obtain all constituent statements (SBOM, Evidence, Reasoning, VEX) 2. Extract their content-addressed IDs 3. Re-compute the merkle root using the algorithm above 4. Compare with the claimed `proofBundleId` If the roots match, the bundle is valid and all statements are bound to this proof. ## API ### C# Interface ```csharp public interface IProofSpineAssembler { /// /// Assembles a proof spine from its constituent statements. /// ProofSpineResult Assemble(ProofSpineInput input); } public record ProofSpineInput { public required string SbomEntryId { get; init; } public required IReadOnlyList EvidenceIds { get; init; } public required string ReasoningId { get; init; } public required string VexVerdictId { get; init; } } public record ProofSpineResult { public required string ProofBundleId { get; init; } public required byte[] MerkleRoot { get; init; } public required IReadOnlyList LeafHashes { get; init; } } ``` ## Related Documentation - [Proof and Evidence Chain Technical Reference](../product-advisories/14-Dec-2025%20-%20Proof%20and%20Evidence%20Chain%20Technical%20Reference.md) - §2.4, §4.2, §9 - [Content-Addressed IDs](./content-addressed-ids.md) - [DSSE Predicates](./dsse-predicates.md)