Replay Manifest Guide

Audience: Stella Ops users and CI engineers exporting, verifying, and pinning replay manifests. Scope: The complete reference for Replay Manifest export, verification, and CI integration — schema, CLI commands, exit codes, drift troubleshooting, and the supporting API.

Overview

The Replay Manifest is a self-contained JSON document that captures everything needed to reproduce a scan: inputs, toolchain versions, policies, and expected outputs. When verified, it provides cryptographic proof that a scan is deterministic and reproducible.

Quick Start

# Export replay manifest after scanning
stella replay export --scan-id <scan-uuid> --output replay.json

# Or export for a specific image
stella replay export --image myregistry/app:v1.0.0 --output replay.json

# Verify determinism (strict mode)
stella replay verify --manifest replay.json --strict-mode

# Verify with drift failure (for CI)
stella replay verify --manifest replay.json --fail-on-drift

Schema Reference

Schema Version

Current version: 1.0.0

Schema location: src/__Libraries/StellaOps.Replay.Core/Schemas/replay-export.schema.json

Top-Level Structure

{
  "version": "1.0.0",
  "snapshot": { ... },
  "toolchain": { ... },
  "inputs": { ... },
  "outputs": { ... },
  "verification": { ... }
}

snapshot Object

Identifies the scan snapshot this manifest represents.

FieldTypeDescription
idstringUnique snapshot ID (snapshot:<sha256>)
createdAtISO 8601UTC timestamp when scan completed
artifactobjectReference to scanned artifact (digest, repository, tag)

Example:

{
  "id": "snapshot:a1b2c3d4e5f6...",
  "createdAt": "2025-12-28T14:30:00Z",
  "artifact": {
    "digest": "sha256:abc123...",
    "repository": "myregistry/app",
    "tag": "v1.0.0"
  }
}

toolchain Object

Captures exact versions of all tools used during the scan.

FieldTypeDescription
scannerVersionstringStellaOps Scanner version
policyEngineVersionstringPolicy Engine version
platformstringPlatform identifier (e.g., linux-x64)
sbomerVersionstringSBOM generator version
vexerVersionstringVEX processor version

Example:

{
  "scannerVersion": "0.42.0",
  "policyEngineVersion": "0.42.0",
  "platform": "linux-x64",
  "sbomerVersion": "0.42.0",
  "vexerVersion": "0.42.0"
}

inputs Object

All inputs consumed during the scan, with content hashes.

FieldTypeDescription
sbomsarraySBOM inputs (if layered)
vexarrayVEX documents used
feedsarrayVulnerability feed snapshots
policiesobjectPolicy bundle reference

Feed snapshot example:

{
  "feeds": [
    {
      "name": "nvd",
      "snapshotId": "nvd:2025-12-28T00:00:00Z",
      "digest": "sha256:def456...",
      "recordCount": 245678
    }
  ]
}

outputs Object

Expected outputs from the scan, used for verification.

FieldTypeDescription
verdictDigeststringSHA256 of verdict JSON
decisionenumallow, deny, or review
sbomDigeststringSHA256 of generated SBOM
findingsDigeststringSHA256 of findings JSON

verification Object

Helper commands and expected hashes for verification.

FieldTypeDescription
commandstringCLI command to reproduce scan
expectedSbomHashstringExpected SBOM content hash
expectedVerdictHashstringExpected verdict content hash

CLI Commands

stella replay export

Export a replay manifest from a completed scan.

stella replay export [OPTIONS]
OptionRequiredDescription
--scan-id <uuid>One ofScan ID to export
--image <ref>One ofImage reference (uses latest scan)
--output <path>NoOutput path (default: replay.json)
--include-feed-snapshotsNoInclude full feed snapshot refs
--no-verification-scriptNoSkip verification command generation

stella replay verify

Verify a replay manifest by re-executing the scan and comparing outputs.

stella replay verify [OPTIONS]
OptionRequiredDescription
--manifest <path>YesPath to replay manifest
--strict-modeNoRequire bit-for-bit identical outputs
--fail-on-driftNoExit code 1 on any drift
--output-diff <path>NoWrite diff report to file

Exit Codes

CodeMeaning
0Verification passed, outputs match
1Drift detected, outputs differ
2Verification error (missing inputs, invalid manifest, etc.)

CI Integration

Gitea Actions

name: SBOM Replay Verification

on:
  push:
    branches: [main]
  pull_request:

jobs:
  verify-determinism:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t ${{ github.repository }}:${{ github.sha }} .

      - name: Scan with replay export
        run: |
          stella scan \
            --image ${{ github.repository }}:${{ github.sha }} \
            --output-sbom sbom.json \
            --output-replay replay.json

      - name: Verify determinism
        run: |
          stella replay verify \
            --manifest replay.json \
            --fail-on-drift \
            --strict-mode

      - name: Upload replay manifest
        uses: actions/upload-artifact@v4
        with:
          name: replay-manifest
          path: replay.json
          retention-days: 90

GitHub Actions

name: SBOM Replay Verification

on:
  push:
    branches: [main]
  pull_request:

jobs:
  verify-determinism:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up StellaOps
        uses: stellaops/setup-stella@v1
        with:
          version: '0.42.0'

      - name: Build and scan
        run: |
          docker build -t myapp:${{ github.sha }} .
          stella scan --image myapp:${{ github.sha }} \
            --output-sbom sbom.json \
            --output-replay replay.json

      - name: Verify replay
        run: stella replay verify --manifest replay.json --fail-on-drift

      - name: Upload attestations
        uses: actions/upload-artifact@v4
        with:
          name: sbom-attestations
          path: |
            sbom.json
            replay.json

GitLab CI

sbom-replay:
  stage: security
  image: stellaops/cli:latest
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - stella scan --image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --output-replay replay.json
    - stella replay verify --manifest replay.json --fail-on-drift
  artifacts:
    paths:
      - replay.json
    expire_in: 90 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

Troubleshooting Drift Detection

Common Drift Causes

CauseSymptomFix
Feed updatefindingsDigest differsPin feed snapshot version
Policy changeverdictDigest differsVersion policy bundles
Tool upgradeAll digests differLock toolchain versions
Non-deterministic SBOMsbomDigest differsEnable deterministic mode
Timezone issuesTimestamps driftEnsure UTC everywhere

Debugging Steps

  1. Export diff report:

    stella replay verify --manifest replay.json --output-diff drift-report.json
    
  2. Compare inputs:

    stella replay diff --manifest-a old.json --manifest-b new.json --show-inputs
    
  3. Check feed versions:

    stella feeds list --show-snapshots
    
  4. Verify toolchain:

    stella version --all
    

Feed Snapshot Pinning

For reproducible CI, pin feed snapshots:

# List available snapshots
stella feeds snapshots --feed nvd

# Pin specific snapshot
stella scan --image myapp:v1.0.0 \
  --feed-snapshot nvd:2025-12-28T00:00:00Z \
  --output-replay replay.json

Best Practices for Deterministic Builds

1. Lock All Dependencies

# In CI, always specify exact versions
stellaops/cli:0.42.0  # Not :latest

2. Pin Feed Snapshots

# Export current snapshot ID
stella feeds export-snapshot --output feeds-snapshot.json

# Use in subsequent scans
stella scan --feed-snapshot-file feeds-snapshot.json

3. Version Policy Bundles

# Store policies in version control
git add policies/
git commit -m "Policy bundle v2.3.0"

# Reference by commit in manifest
stella scan --policy-ref policies@abc123

4. Use Strict Mode in CI

# Always use strict mode in CI pipelines
stella replay verify --manifest replay.json --strict-mode --fail-on-drift

5. Archive Replay Manifests

Store replay manifests alongside release artifacts for audit trail:

# Archive with release
cp replay.json releases/v1.0.0/replay.json

API Reference

IReplayManifestExporter

public interface IReplayManifestExporter
{
    /// <summary>
    /// Exports a replay manifest for a completed scan.
    /// </summary>
    Task<ReplayExportResult> ExportAsync(
        string scanId,
        ReplayExportOptions options,
        CancellationToken ct = default);
}

ReplayExportOptions

public sealed record ReplayExportOptions
{
    /// <summary>Include exact toolchain versions.</summary>
    public bool IncludeToolchainVersions { get; init; } = true;

    /// <summary>Include feed snapshot references.</summary>
    public bool IncludeFeedSnapshots { get; init; } = true;

    /// <summary>Generate verification shell command.</summary>
    public bool GenerateVerificationScript { get; init; } = true;

    /// <summary>Output file path.</summary>
    public string OutputPath { get; init; } = "replay.json";
}

ReplayExportResult

public sealed record ReplayExportResult
{
    /// <summary>Path to exported manifest.</summary>
    public required string ManifestPath { get; init; }

    /// <summary>SHA256 digest of manifest content.</summary>
    public required string ManifestDigest { get; init; }

    /// <summary>Path to verification script (if generated).</summary>
    public string? VerificationScriptPath { get; init; }
}


Changelog

VersionDateChanges
1.0.02025-12-28Initial schema and CLI commands