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 start from an image reference (writes a minimal manifest that names the image)
stella replay export --image myregistry/app:v1.0.0 --output replay.json

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

# Verify with drift failure (for CI)
stella replay export 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; writes a minimal manifest that names the image
--manifest <path>One ofExisting replay manifest to convert to the export format
--output <path>NoOutput path (default: replay.json)
--prettyNoPretty-print the JSON
--include-feedsNoInclude feed snapshot information
--include-reachabilityNoInclude reachability data

stella replay export verify

Verify a replay manifest against its expected hashes.

stella replay export verify [OPTIONS]
OptionRequiredDescription
--manifest <path>YesPath to replay manifest
--strict-modeNoFail on any drift (strict verification)
--fail-on-driftNoExit code 1 if drift is detected
--verboseNoPrint the result, the hash comparisons and each detected drift

Two related commands work on other inputs: stella replay verify --manifest <run-manifest> replays a run manifest twice to check determinism, and stella replay diff --a <verdict.json> --b <verdict.json> compares two verdict files.

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 }} .

      # SCAN_ID is the id your scan submission returned for this image.
      - name: Export replay manifest
        run: stella replay export --scan-id "$SCAN_ID" --output replay.json

      - name: Verify determinism
        run: |
          stella replay export 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'

      # SCAN_ID is the id your scan submission returned for this image.
      - name: Export replay manifest
        run: stella replay export --scan-id "$SCAN_ID" --output replay.json

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

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

GitLab CI

sbom-replay:
  stage: security
  image: stellaops/cli:latest
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    # SCAN_ID is the id your scan submission returned for this image.
    - stella replay export --scan-id "$SCAN_ID" --output replay.json
    - stella replay export 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. List the drifting fields:

    stella replay export verify --manifest replay.json --verbose
    
  2. Compare two verdicts:

    stella replay diff --a old-verdict.json --b new-verdict.json
    
  3. Record feed snapshots in the manifest:

    stella replay export --scan-id <scan-uuid> --include-feeds --output replay.json
    
  4. Verify toolchain:

    stella --version
    

Feed Snapshot Pinning

A scan replay pins its inputs by hash: the feed snapshot, the policy ruleset and the run manifest are all required.

stella scan replay --artifact sha256:<digest> \
  --manifest <run-manifest-hash> \
  --feeds <feed-snapshot-hash> \
  --policy <policy-ruleset-hash>

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

Replay against a fixed feed snapshot hash with stella scan replay --feeds <feed-snapshot-hash> (see Feed Snapshot Pinning).

3. Version Policy Bundles

Keep policy bundles in version control, and replay against a fixed ruleset hash with stella scan replay --policy <policy-ruleset-hash>.

4. Use Strict Mode in CI

# Always use strict mode in CI pipelines
stella replay export 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