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.
| Field | Type | Description |
|---|---|---|
id | string | Unique snapshot ID (snapshot:<sha256>) |
createdAt | ISO 8601 | UTC timestamp when scan completed |
artifact | object | Reference 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.
| Field | Type | Description |
|---|---|---|
scannerVersion | string | StellaOps Scanner version |
policyEngineVersion | string | Policy Engine version |
platform | string | Platform identifier (e.g., linux-x64) |
sbomerVersion | string | SBOM generator version |
vexerVersion | string | VEX 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.
| Field | Type | Description |
|---|---|---|
sboms | array | SBOM inputs (if layered) |
vex | array | VEX documents used |
feeds | array | Vulnerability feed snapshots |
policies | object | Policy 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.
| Field | Type | Description |
|---|---|---|
verdictDigest | string | SHA256 of verdict JSON |
decision | enum | allow, deny, or review |
sbomDigest | string | SHA256 of generated SBOM |
findingsDigest | string | SHA256 of findings JSON |
verification Object
Helper commands and expected hashes for verification.
| Field | Type | Description |
|---|---|---|
command | string | CLI command to reproduce scan |
expectedSbomHash | string | Expected SBOM content hash |
expectedVerdictHash | string | Expected verdict content hash |
CLI Commands
stella replay export
Export a replay manifest from a completed scan.
stella replay export [OPTIONS]
| Option | Required | Description |
|---|---|---|
--scan-id <uuid> | One of | Scan ID to export |
--image <ref> | One of | Image reference; writes a minimal manifest that names the image |
--manifest <path> | One of | Existing replay manifest to convert to the export format |
--output <path> | No | Output path (default: replay.json) |
--pretty | No | Pretty-print the JSON |
--include-feeds | No | Include feed snapshot information |
--include-reachability | No | Include reachability data |
stella replay export verify
Verify a replay manifest against its expected hashes.
stella replay export verify [OPTIONS]
| Option | Required | Description |
|---|---|---|
--manifest <path> | Yes | Path to replay manifest |
--strict-mode | No | Fail on any drift (strict verification) |
--fail-on-drift | No | Exit code 1 if drift is detected |
--verbose | No | Print 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
| Code | Meaning |
|---|---|
0 | Verification passed, outputs match |
1 | Drift detected, outputs differ |
2 | Verification 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
| Cause | Symptom | Fix |
|---|---|---|
| Feed update | findingsDigest differs | Pin feed snapshot version |
| Policy change | verdictDigest differs | Version policy bundles |
| Tool upgrade | All digests differ | Lock toolchain versions |
| Non-deterministic SBOM | sbomDigest differs | Enable deterministic mode |
| Timezone issues | Timestamps drift | Ensure UTC everywhere |
Debugging Steps
List the drifting fields:
stella replay export verify --manifest replay.json --verboseCompare two verdicts:
stella replay diff --a old-verdict.json --b new-verdict.jsonRecord feed snapshots in the manifest:
stella replay export --scan-id <scan-uuid> --include-feeds --output replay.jsonVerify 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; }
}
Related Documentation
- Deterministic Replay - Core concepts and architecture
- Developer Guide: Replay - Implementation details
- Replay Manifest v2 Acceptance - Schema evolution
- Test Strategy - Replay testing approach
Changelog
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2025-12-28 | Initial schema and CLI commands |
