# Evidence Bundle Export Format Specification > **Version:** 1.0.0 > **Status:** FINAL > **Sprint Reference:** [SPRINT_20260106_003_003](../../../docs/implplan/SPRINT_20260106_003_003_EVIDENCE_export_bundle.md) ## 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-.tar.gz ``` Where `` follows the pattern: `eb--` 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-/ ├── 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/ │ ├── .cdx.json # Per-layer CycloneDX │ └── .spdx.json # Per-layer SPDX ├── vex/ │ ├── statements/ │ │ └── .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/ │ └── .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. ```json { "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. ```json { "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: ```bash #!/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: ```powershell #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 ```bash # Basic export stella evidence export --bundle --output ./audit-bundle.tar.gz # With options stella evidence export --bundle \ --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 ```http 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-.tar.gz` to isolated system 2. Extract archive: `tar -xzf evidence-bundle-.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+ ## Related Documentation - [Bundle Packaging](bundle-packaging.md) - Internal bundle structure - [Evidence Bundle v1](evidence-bundle-v1.md) - Core bundle contract - [Verify Offline](verify-offline.md) - Offline verification procedures - [Attestation Contract](attestation-contract.md) - DSSE envelope format ## Change Log | Date | Version | Author | Description | |------|---------|--------|-------------| | 2026-01-07 | 1.0.0 | StellaOps | Initial specification for Sprint 003_003 |