Files
git.stella-ops.org/docs/modules/evidence-locker/export-format.md

10 KiB

Evidence Bundle Export Format Specification

Version: 1.0.0
Status: FINAL
Sprint Reference: SPRINT_20260106_003_003

Overview

This document specifies the standard export format for StellaOps evidence bundles. The export format enables offline verification of software supply chain artifacts including SBOMs, VEX statements, attestations, and policy verdicts.

Export Archive Format

Filename Convention

evidence-bundle-<bundle-id>.tar.gz

Where <bundle-id> follows the pattern: eb-<YYYY-MM-DD>-<unique-suffix>

Example: evidence-bundle-eb-2026-01-06-abc123.tar.gz

Compression

  • Format: gzip-compressed tar archive
  • Compression level: Configurable (1-9, default: 6)
  • Determinism: Fixed gzip header timestamp (2026-01-01T00:00:00Z)
  • Permissions: All files 0644, directories 0755, uid/gid 0:0

Directory Structure

evidence-bundle-<id>/
├── manifest.json                    # Bundle manifest with all artifact refs
├── metadata.json                    # Bundle metadata (provenance, timestamps)
├── README.md                        # Human-readable verification instructions
├── verify.sh                        # Bash verification script (POSIX-compliant)
├── verify.ps1                       # PowerShell verification script
├── checksums.sha256                 # BSD-format SHA256 checksums
├── keys/
│   ├── signing-key-001.pem          # Public key(s) for DSSE verification
│   ├── signing-key-002.pem          # Additional keys (multi-signature)
│   └── trust-bundle.pem             # CA chain (if applicable)
├── sboms/
│   ├── image.cdx.json               # Aggregated CycloneDX SBOM
│   ├── image.spdx.json              # Aggregated SPDX SBOM
│   └── layers/
│       ├── <layer-digest>.cdx.json  # Per-layer CycloneDX
│       └── <layer-digest>.spdx.json # Per-layer SPDX
├── vex/
│   ├── statements/
│   │   └── <statement-id>.openvex.json
│   └── consensus/
│       └── image-consensus.json     # VEX consensus result
├── attestations/
│   ├── sbom.dsse.json               # SBOM attestation envelope
│   ├── vex.dsse.json                # VEX attestation envelope
│   ├── policy.dsse.json             # Policy verdict attestation
│   └── rekor-proofs/
│       └── <uuid>.proof.json        # Rekor inclusion proofs
├── findings/
│   ├── scan-results.json            # Vulnerability findings
│   └── gate-results.json            # VEX gate decisions
└── audit/
    └── timeline.ndjson              # Audit event timeline

Core Artifacts

manifest.json

The manifest provides a complete inventory of all artifacts in the bundle.

{
  "manifestVersion": "1.0.0",
  "bundleId": "eb-2026-01-06-abc123",
  "createdAt": "2026-01-06T10:30:00.000000Z",
  "subject": {
    "type": "container-image",
    "digest": "sha256:abcdef1234567890...",
    "name": "registry.example.com/app:v1.2.3"
  },
  "artifacts": [
    {
      "path": "sboms/image.cdx.json",
      "type": "sbom",
      "format": "cyclonedx-1.7",
      "digest": "sha256:...",
      "size": 45678
    },
    {
      "path": "attestations/sbom.dsse.json",
      "type": "attestation",
      "format": "dsse-v1",
      "predicateType": "StellaOps.SBOMAttestation@1",
      "digest": "sha256:...",
      "size": 12345,
      "signedBy": ["sha256:keyabc..."]
    }
  ],
  "verification": {
    "merkleRoot": "sha256:...",
    "algorithm": "sha256",
    "checksumFile": "checksums.sha256"
  }
}

metadata.json

Provides provenance and chain information.

{
  "bundleId": "eb-2026-01-06-abc123",
  "exportedAt": "2026-01-06T10:35:00.000000Z",
  "exportedBy": "stella evidence export",
  "exportVersion": "2026.04",
  "provenance": {
    "tenantId": "tenant-xyz",
    "scanId": "scan-abc123",
    "pipelineId": "pipeline-def456",
    "sourceRepository": "https://github.com/example/app",
    "sourceCommit": "abc123def456..."
  },
  "chainInfo": {
    "previousBundleId": "eb-2026-01-05-xyz789",
    "sequenceNumber": 42
  },
  "transparency": {
    "rekorLogUrl": "https://rekor.sigstore.dev",
    "rekorEntryUuids": ["uuid1", "uuid2"]
  }
}

checksums.sha256

BSD-format SHA256 checksums for all artifacts:

SHA256 (manifest.json) = abc123...
SHA256 (metadata.json) = def456...
SHA256 (sboms/image.cdx.json) = 789abc...
SHA256 (attestations/sbom.dsse.json) = cde012...

Verification Scripts

verify.sh (Bash)

POSIX-compliant bash script for Unix/Linux/macOS verification:

#!/bin/bash
set -euo pipefail

BUNDLE_DIR="$(cd "$(dirname "$0")" && pwd)"
MANIFEST="$BUNDLE_DIR/manifest.json"
CHECKSUMS="$BUNDLE_DIR/checksums.sha256"

echo "=== StellaOps Evidence Bundle Verification ==="
echo "Bundle: $(basename "$BUNDLE_DIR")"
echo ""

# Step 1: Verify checksums
echo "[1/4] Verifying artifact checksums..."
cd "$BUNDLE_DIR"
sha256sum -c "$CHECKSUMS" --quiet
echo "  OK: All checksums match"

# Step 2: Verify Merkle root
echo "[2/4] Verifying Merkle root..."
COMPUTED_ROOT=$(compute-merkle-root "$CHECKSUMS")
EXPECTED_ROOT=$(jq -r '.verification.merkleRoot' "$MANIFEST")
if [ "$COMPUTED_ROOT" = "$EXPECTED_ROOT" ]; then
  echo "  OK: Merkle root verified"
else
  echo "  FAIL: Merkle root mismatch"
  exit 1
fi

# Step 3: Verify DSSE signatures
echo "[3/4] Verifying attestation signatures..."
for dsse in "$BUNDLE_DIR"/attestations/*.dsse.json; do
  verify-dsse "$dsse" --keys "$BUNDLE_DIR/keys/"
  echo "  OK: $(basename "$dsse")"
done

# Step 4: Verify Rekor proofs (if online)
echo "[4/4] Verifying Rekor proofs..."
if [ "${OFFLINE:-false}" = "true" ]; then
  echo "  SKIP: Offline mode, Rekor verification skipped"
else
  for proof in "$BUNDLE_DIR"/attestations/rekor-proofs/*.proof.json; do
    verify-rekor-proof "$proof"
    echo "  OK: $(basename "$proof")"
  done
fi

echo ""
echo "=== Verification Complete: PASSED ==="

verify.ps1 (PowerShell)

Windows PowerShell verification script:

#Requires -Version 5.1
$ErrorActionPreference = 'Stop'

$BundleDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$Manifest = Join-Path $BundleDir 'manifest.json'
$Checksums = Join-Path $BundleDir 'checksums.sha256'

Write-Host "=== StellaOps Evidence Bundle Verification ===" -ForegroundColor Cyan
Write-Host "Bundle: $(Split-Path -Leaf $BundleDir)"
Write-Host ""

# Step 1: Verify checksums
Write-Host "[1/4] Verifying artifact checksums..." -ForegroundColor Yellow
Push-Location $BundleDir
try {
    Get-Content $Checksums | ForEach-Object {
        if ($_ -match 'SHA256 \((.+)\) = (.+)') {
            $File = $Matches[1]
            $Expected = $Matches[2]
            $Actual = (Get-FileHash -Path $File -Algorithm SHA256).Hash.ToLower()
            if ($Actual -ne $Expected) {
                throw "Checksum mismatch: $File"
            }
        }
    }
    Write-Host "  OK: All checksums match" -ForegroundColor Green
} finally {
    Pop-Location
}

# Step 2-4: Continue verification...
Write-Host ""
Write-Host "=== Verification Complete: PASSED ===" -ForegroundColor Green

Determinism Requirements

Timestamp Handling

  • All timestamps MUST be UTC ISO-8601 with microsecond precision
  • Format: YYYY-MM-DDTHH:MM:SS.ffffffZ
  • Archive metadata timestamps are fixed for reproducibility

Ordering

  • Manifest artifacts: sorted lexicographically by path
  • Checksum entries: sorted lexicographically by filename
  • JSON object keys: sorted lexicographically (RFC 8785)
  • NDJSON records: sorted by primary key (e.g., observationId)

Hash Computation

  • Algorithm: SHA-256 (lowercase hex)
  • Input: raw file bytes (no BOM, LF line endings)
  • Merkle tree: RFC 6962 compliant binary Merkle tree

Artifact Types

SBOMs

Format File Extension MIME Type
CycloneDX 1.7 .cdx.json application/vnd.cyclonedx+json
SPDX 3.0.1 .spdx.json application/spdx+json

Attestations

Type Predicate Type File Pattern
SBOM StellaOps.SBOMAttestation@1 sbom.dsse.json
VEX StellaOps.VEXAttestation@1 vex.dsse.json
Policy StellaOps.PolicyEvaluation@1 policy.dsse.json
Gate StellaOps.VexGate@1 gate.dsse.json

VEX Statements

  • Format: OpenVEX 0.2.0+
  • File extension: .openvex.json
  • Location: vex/statements/

Export Options

CLI Command

# Basic export
stella evidence export --bundle <bundle-id> --output ./audit-bundle.tar.gz

# With options
stella evidence export --bundle <bundle-id> \
  --output ./bundle.tar.gz \
  --include-layers \
  --include-rekor-proofs \
  --compression 9

# Verify exported bundle
stella evidence verify ./audit-bundle.tar.gz

# Verify offline (skip Rekor)
stella evidence verify ./audit-bundle.tar.gz --offline

API Endpoint

POST /api/v1/bundles/{bundleId}/export
Content-Type: application/json

{
  "format": "tar.gz",
  "compression": "gzip",
  "compressionLevel": 6,
  "includeRekorProofs": true,
  "includeLayerSboms": true
}

Offline Verification

For air-gapped environments:

  1. Transfer evidence-bundle-<id>.tar.gz to isolated system
  2. Extract archive: tar -xzf evidence-bundle-<id>.tar.gz
  3. Run verification: ./verify.sh (Unix) or .\verify.ps1 (Windows)
  4. Pass OFFLINE=true to skip Rekor verification: OFFLINE=true ./verify.sh

Compatibility

  • StellaOps CLI: 2026.04+
  • Export Library: StellaOps.EvidenceLocker.Export 1.0.0+
  • Verify scripts: bash 4.0+ / PowerShell 5.1+

Change Log

Date Version Author Description
2026-01-07 1.0.0 StellaOps Initial specification for Sprint 003_003