Resolved Execution Graph (REG) Evidence Architecture

Status: Proposed Sprint Series: 8100.0012.* Last Updated: 2025-12-24

Overview

This document describes the architectural enhancements to StellaOps’ evidence and attestation subsystems based on the Merkle-Hash REG (Resolved Execution Graph) pattern. The core insight: when every node in an execution graph is identified by a Merkle hash of its normalized content, evidence can be attached to content itself rather than brittle positional indices.

Motivation

Problem Statement

StellaOps currently has robust foundations for content-addressed identifiers, Merkle trees, and attestations. However, three gaps limit the system’s verifiability:

  1. Canonicalizer versioning: Hash collisions possible if canonicalization logic changes
  2. Fragmented evidence models: Different modules use different evidence structures
  3. Implicit graph roots: Merkle roots are computed but not independently attested

Target Benefits

BenefitDescription
Reproducible proofsVerifiers re-hash node content and check against stored hashes—no fragile indices
Dedup & reuseIdentical content across pipelines collapses to one ID; one piece of evidence justifies many occurrences
Audits + time travelSnapshot the graph’s Merkle root; any replay matching the root guarantees identical nodes & evidence
Offline verificationAttestations are self-contained; no network required to verify
Exception stabilityExceptions bind to content hashes, not “row 42”; stable across rebuilds

Architecture

Component Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                              StellaOps REG Architecture                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐     │
│  │  StellaOps.      │     │  StellaOps.      │     │  StellaOps.      │     │
│  │  Canonical.Json  │────▶│  Evidence.Core   │◀────│  Attestor.       │     │
│  │                  │     │                  │     │  GraphRoot       │     │
│  │  • CanonVersion  │     │  • IEvidence     │     │                  │     │
│  │  • CanonJson     │     │  • EvidenceRecord│     │  • IGraphRoot-   │     │
│  │  • Versioned     │     │  • IEvidenceStore│     │    Attestor      │     │
│  │    Hashing       │     │  • Adapters      │     │  • Verification  │     │
│  └──────────────────┘     └──────────────────┘     └──────────────────┘     │
│           │                        │                        │               │
│           └────────────────────────┼────────────────────────┘               │
│                                    │                                        │
│                                    ▼                                        │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                        Content-Addressed Store                        │   │
│  │                                                                       │   │
│  │   Evidence by subject_node_id    │    Graph roots by root_hash       │   │
│  │   ────────────────────────────   │    ──────────────────────────     │   │
│  │   sha256:abc... → [evidence]     │    sha256:xyz... → attestation    │   │
│  │   sha256:def... → [evidence]     │    sha256:uvw... → attestation    │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  ┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐     │
│  │  Scanner         │     │  Policy          │     │  Excititor       │     │
│  │  ──────────────  │     │  ──────────────  │     │  ──────────────  │     │
│  │  • EvidenceBundle│────▶│  • Exception     │────▶│  • VexObservation│     │
│  │  • ProofSpine    │     │    Application   │     │  • VexLinkset    │     │
│  │  • RichGraph     │     │  • PolicyVerdict │     │                  │     │
│  └──────────────────┘     └──────────────────┘     └──────────────────┘     │
│           │                        │                        │               │
│           └────────────────────────┼────────────────────────┘               │
│                                    │                                        │
│                                    ▼                                        │
│                         ┌──────────────────┐                                │
│                         │  Unified IEvidence│                               │
│                         │  via Adapters     │                               │
│                         └──────────────────┘                                │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Data Flow

1. Content Creation
   ─────────────────
   Component/Node → Canonicalize(content, version) → SHA-256 → node_id

2. Evidence Attachment
   ────────────────────
   Analysis Result → IEvidence { subject_node_id, type, payload, provenance }
                  → EvidenceId = hash(subject + type + payload + provenance)
                  → Store(evidence)

3. Graph Construction
   ───────────────────
   Nodes + Edges → Sort(node_ids) + Sort(edge_ids) → Merkle Tree → root_hash

4. Graph Attestation
   ──────────────────
   GraphRootAttestationRequest → GraphRootAttestor
                              → In-Toto Statement { subject: [root, artifact] }
                              → DSSE Sign
                              → Optional: Rekor Publish

5. Verification
   ─────────────
   Download attestation → Verify signature
                       → Fetch nodes/edges by ID
                       → Recompute Merkle root
                       → Compare with attested root

Implementation Sprints

SprintTitleDependencyKey Deliverables
8100.0012.0001Canonicalizer VersioningNoneCanonVersion, CanonicalizeVersioned(), backward compatibility
8100.0012.0002Unified Evidence Model0001IEvidence, EvidenceRecord, IEvidenceStore, adapters
8100.0012.0003Graph Root Attestation0001, 0002IGraphRootAttestor, in-toto statements, Rekor integration

Sprint Sequence Diagram

Week 1-2          Week 3-4          Week 5-6          Week 7-8
────────          ────────          ────────          ────────
   │                 │                 │                 │
   │  0001: Canon    │                 │                 │
   │  Versioning     │                 │                 │
   │  ┌─────────┐    │                 │                 │
   │  │Wave 0-1 │────┼─▶  0002: Unified│                 │
   │  │Wave 2-3 │    │    Evidence     │                 │
   │  │Wave 4   │    │    ┌─────────┐  │                 │
   │  └─────────┘    │    │Wave 0-1 │──┼─▶  0003: Graph  │
   │                 │    │Wave 2-3 │  │    Root Attest  │
   │                 │    │Wave 4   │  │    ┌─────────┐  │
   │                 │    └─────────┘  │    │Wave 0-1 │  │
   │                 │                 │    │Wave 2-4 │  │
   │                 │                 │    │Wave 5   │  │
   │                 │                 │    └─────────┘  │
   ▼                 ▼                 ▼                 ▼

Technical Specifications

Canonicalization Version Marker

{
  "_canonVersion": "stella:canon:v1",
  "evidenceSource": "stellaops/scanner/reachability",
  "sbomEntryId": "sha256:abc123...:pkg:npm/lodash@4.17.21",
  ...
}

The _canonVersion field:

Unified Evidence Record

interface IEvidence {
  // Content-addressed binding
  subjectNodeId: string;      // "sha256:{hex}" - what this evidence is about
  evidenceId: string;         // "sha256:{hex}" - ID of this evidence record

  // Type and payload
  evidenceType: EvidenceType; // reachability, scan, policy, vex, etc.
  payload: Uint8Array;        // Canonical JSON bytes
  payloadSchemaVersion: string;

  // Attestation
  signatures: EvidenceSignature[];

  // Provenance
  provenance: {
    generatorId: string;
    generatorVersion: string;
    generatedAt: DateTimeOffset;
    inputsDigest?: string;
    correlationId?: string;
  };

  // External storage
  externalPayloadCid?: string;
}

Graph Root Attestation (In-Toto)

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "name": "sha256:abc123...",
      "digest": { "sha256": "abc123..." }
    },
    {
      "name": "sha256:def456...",
      "digest": { "sha256": "def456..." }
    }
  ],
  "predicateType": "https://stella-ops.org/attestation/graph-root/v1",
  "predicate": {
    "graphType": "ResolvedExecutionGraph",
    "rootHash": "sha256:abc123...",
    "nodeCount": 1247,
    "edgeCount": 3891,
    "nodeIds": ["sha256:...", ...],
    "edgeIds": ["sha256:...", ...],
    "inputs": {
      "policyDigest": "sha256:...",
      "feedsDigest": "sha256:...",
      "toolchainDigest": "sha256:...",
      "paramsDigest": "sha256:..."
    },
    "evidenceIds": ["sha256:...", ...],
    "canonVersion": "stella:canon:v1",
    "computedAt": "2025-12-24T10:30:00Z",
    "computedBy": "stellaops/attestor/graph-root",
    "computedByVersion": "1.0.0"
  }
}

Verification Guarantees

What Can Be Verified

ClaimVerification Method
“This graph contains exactly these nodes”Recompute Merkle root from node IDs
“This evidence applies to node X”Check evidence.subjectNodeId == node.id
“This attestation was signed by key K”Verify DSSE envelope signature
“This graph was published to transparency log”Check Rekor inclusion proof
“These inputs produced this graph”Check inputs.* digests match expectations

What Cannot Be Verified (Without Additional Trust)

ClaimReasonMitigation
“The analysis was performed correctly”Semantic, not structuralTrust the generator; audit toolchain
“No evidence was omitted”Attester controls contentRequire known evidence types; policy enforcement
“The inputs are authentic”External dependencyChain attestations; verify feed signatures

Migration Path

Phase 1: Parallel Operation (Sprints 0001-0003)

Phase 2: Gradual Adoption (Post-Sprints)

Phase 3: Deprecation (Future)

Compatibility Considerations

Existing Attestations

Attestations generated before canonicalizer versioning remain valid:

Existing Evidence

Existing evidence types (EvidenceBundle, EvidenceStatement, etc.) continue working:

API Stability

Public APIs remain backward compatible:

Performance Considerations

OperationImpactMitigation
Version field injection~100 bytes per hashNegligible
Merkle tree computationO(n log n) for n nodesExisting algorithm; no change
Graph root attestation+1 DSSE sign per graphBatching; async
Evidence store queriesIndex on subject_node_idComposite index; pagination
Rekor publishingNetwork latencyOptional; async; batching

Security Considerations

Threat Model

ThreatMitigation
Hash collision attacksSHA-256 with 256-bit security; version namespacing
Signature forgeryDSSE with ECDSA/EdDSA; key rotation
Evidence tamperingContent-addressed storage; Merkle verification
Replay attacksTimestamp in provenance; Rekor log index
Canonicalization attacksRFC 8785 compliance; explicit versioning

Key Management

Graph root attestations use the existing Signer module:

References

Appendix A: File Locations

ComponentPath
CanonVersionsrc/__Libraries/StellaOps.Canonical.Json/CanonVersion.cs
CanonJson (versioned)src/__Libraries/StellaOps.Canonical.Json/CanonJson.cs
IEvidencesrc/__Libraries/StellaOps.Evidence.Core/IEvidence.cs
EvidenceRecordsrc/__Libraries/StellaOps.Evidence.Core/EvidenceRecord.cs
IEvidenceStoresrc/__Libraries/StellaOps.Evidence.Core/IEvidenceStore.cs
Adapterssrc/__Libraries/StellaOps.Evidence.Core/Adapters/
IGraphRootAttestorsrc/Attestor/__Libraries/StellaOps.Attestor.GraphRoot/IGraphRootAttestor.cs
GraphRootAttestationsrc/Attestor/__Libraries/StellaOps.Attestor.GraphRoot/Models/

Appendix B: Example Evidence Queries

// Get all reachability evidence for a package
var evidence = await evidenceStore.GetBySubjectAsync(
    subjectNodeId: "sha256:abc123...pkg:npm/lodash@4.17.21",
    typeFilter: EvidenceType.Reachability);

// Verify a graph root attestation
var result = await graphRootAttestor.VerifyAsync(
    envelope: downloadedEnvelope,
    nodes: fetchedNodes,
    edges: fetchedEdges);

if (!result.IsValid)
    throw new VerificationException(result.FailureReason);

// Check if evidence exists before creating duplicate
if (!await evidenceStore.ExistsAsync(subjectNodeId, EvidenceType.Scan))
{
    await evidenceStore.StoreAsync(newEvidence);
}