Stella Ops — Deterministic Replay Specification
Version: 1.0
Status: Draft / Internal Technical Reference
Audience: Core developers, module maintainers, and audit engineers.
This specification defines the on-disk manifest, execution rules, and verification flow for byte-for-byte replay. For the module surface (service, API, persistence) see the Replay module overview and architecture; for the platform-wide invariant, see the Determinism Specification.
1. Purpose
Deterministic Replay allows any completed Stella Ops scan to be reproduced byte-for-byte with full cryptographic validation.
It guarantees that SBOMs, Findings, and VEX evaluations can be re-executed later to:
- prove historical compliance decisions,
- attribute changes precisely to feeds, rules, or tools,
- support dual-signing (FIPS + regional crypto),
- and anchor cryptographic evidence in offline or public ledgers.
Replay requires that all inputs and environmental conditions are captured, hashed, and sealed at scan time.
2. Architecture Overview
graph TD A[Scanner.WebService] --> B[Replay Manifest] A --> C[InputBundle] A --> D[OutputBundle] B --> E[DSSE Envelope] C --> F[Concelier Snapshot Export] C --> G[Policy/Lattice Bundle] D --> H[DSSE Outputs (SBOM, Findings, VEX)] E --> I[PostgreSQL: replay_runs] C --> J[Blob Store: Input/Output Bundles]
Core Artifacts
| Artifact | Description | Format |
|---|---|---|
| Replay Manifest | Immutable JSON describing all scan inputs and outputs. | JSON (canonicalized) |
| InputBundle | Feeds, rules, policies, tool binaries (hashed). | .tar.zst |
| OutputBundle | SBOM, Findings, VEX, logs. | .tar.zst |
| DSSE Envelope | Signed metadata for each artifact. | JSON / JWS |
| Merkle Map | Layer and feed chunk trees. | JSON (embedded or sidecar) |
3. Replay Manifest Schema (v1)
3.1 Top-level Layout
{
"schemaVersion": "1.0",
"scan": {
"id": "uuid",
"time": "2025-10-29T13:05:33Z",
"mode": "record",
"scannerVersion": "10.1.3",
"cryptoProfile": "FIPS-140-3+GOST-R-34.10-2012"
},
"subject": {
"ociDigest": "sha256:abcd...",
"layers": [
{ "layerDigest": "...", "merkleRoot": "...", "leafCount": 144 }
]
},
"inputs": {
"feeds": [
{
"name": "nvd",
"snapshotHash": "sha256:...",
"snapshotTime": "2025-10-29T12:00:00Z",
"merkleRoot": "..."
}
],
"rulesBundleHash": "sha256:...",
"tools": [
{ "name": "sbomer", "version": "10.1.3", "sha256": "..." },
{ "name": "scanner", "version": "10.1.3", "sha256": "..." },
{ "name": "vexer", "version": "10.1.3", "sha256": "..." }
],
"env": {
"os": "linux",
"arch": "x64",
"locale": "en_US.UTF-8",
"tz": "UTC",
"seed": "H(scan.id||merkleRootAllLayers)",
"flags": ["offline"]
}
},
"policy": {
"latticeHash": "sha256:...",
"mutes": [
{ "id": "MUTE-1234", "reason": "vendor ack", "approvedBy": "authority@example.com", "approvedAt": "2025-10-29T12:55Z" }
],
"trustProfile": "sha256:..."
},
"outputs": {
"sbomHash": "sha256:...",
"findingsHash": "sha256:...",
"vexHash": "sha256:...",
"logHash": "sha256:..."
},
"reachability": {
"graphs": [
{
"kind": "static",
"analyzer": "scanner/java@sha256:...",
"casUri": "cas://replay/scan-123/reachability/static-graph.tar.zst",
"sha256": "abc123"
},
{
"kind": "framework",
"analyzer": "scanner/framework@sha256:...",
"casUri": "cas://replay/scan-123/reachability/framework-graph.tar.zst",
"sha256": "def456"
}
],
"runtimeTraces": [
{
"source": "zastava",
"casUri": "cas://replay/scan-123/reachability/runtime-trace.ndjson.zst",
"sha256": "feedface",
"recordedAt": "2025-11-07T11:10:00Z"
}
]
},
"provenance": {
"signer": "scanner.authority",
"dsseEnvelopeHash": "sha256:...",
"rekorEntry": "optional"
}
}
3.2 Reachability Section
The optional reachability block captures the inputs needed to replay explainability decisions:
| Field | Description |
|---|---|
reachability.graphs[] | References to static/framework callgraph bundles. Each entry records the producing analyzer (analyzer/version), the CAS URI under cas://replay/<scan-id>/reachability/graphs/, an algorithm-prefixed hash and the matching hashAlg label. |
reachability.runtimeTraces[] | References to runtime observation bundles (e.g., Zastava ND-JSON traces). Each item stores the emitting source, CAS URI (typically cas://replay/<scan-id>/reachability/traces/), algorithm-prefixed hash, hashAlg, and capture timestamp. |
Replay manifests use schema version 2.0. Producers compute content digests through the configured ICryptoHash content purpose and carry the resulting prefix and algorithm label. The bare sha256 property is not part of either reference. Current contract sources are ReplayReachabilityGraphReference and ReplayReachabilityTraceReference in src/Replay/__Libraries/StellaOps.Replay.Contracts/; see the counted PCD-364 verification and rerun command in Replay architecture.
Replay engines MUST verify every referenced artifact hash before re-evaluating reachability. Missing graphs downgrade affected signals to reachability:unknown and should raise policy warnings.
Producer note: default clock values in StellaOps.Replay.Core are UnixEpoch to avoid hidden time drift; producers MUST set scan.time and reachability.runtimeTraces[].recordedAt explicitly.
4. Deterministic Execution Rules
4.1 Environment Normalization
Clock: frozen to
scan.timeunless a rule explicitly requires “now”.Replay engine timestamps: success and failure outputs must use the injected
TimeProviderto keep replay timestamps deterministic.Random seed: derived as
H(scan.id || MerkleRootAllLayers).Locale/TZ: enforced per manifest; deviations cause validation error.
Filesystem normalization:
- Normalize perms to 0644/0755.
- Path separators =
/. - Newlines = LF.
- JSON key order = lexical.
4.2 Concurrency & I/O
- File traversal: stable lexicographic order.
- Parallel jobs: ordered reduction by subject path.
- Temporary directories: ephemeral but deterministic hash seeds.
4.3 Feeds & Policies
- All network I/O disabled; feeds must be read from snapshot bundles.
- Policies and suppressions must resolve by hash, not name.
4.4 Library hooks (StellaOps.Replay.Core)
Use the shared helpers in src/__Libraries/StellaOps.Replay.Core to keep outputs deterministic:
CanonicalJson.Serialize(...)→ lexicographic key ordering with relaxed escaping, arrays preserved as-is.DeterministicHash.Sha256Hex(...)andDeterministicHash.MerkleRootHex(...)→ lowercase digests and stable Merkle roots for bundle manifests.DssePayloadBuilder.BuildUnsigned(...)→ DSSE payloads for replay manifests using payload typeapplication/vnd.stellaops.replay+json.ReplayManifestExtensions.ComputeCanonicalSha256()→ convenience for CAS naming of manifest blobs.
5. DSSE and Signing
5.1 Envelope Structure
{
"payloadType": "application/vnd.stellaops.replay+json",
"payload": "<base64-encoded canonical JSON>",
"signatures": [
{ "keyid": "authority-root-fips", "sig": "..." },
{ "keyid": "authority-root-gost", "sig": "..." }
]
}
5.2 Verification Steps
- Decode payload → verify canonical form.
- Verify each signature chain against RootPack (offline trust anchors).
- Recompute hash and compare to
dsseEnvelopeHashin manifest. - Optionally verify Rekor inclusion proof.
5.3 Default payload type
Replay DSSE envelopes emitted by DssePayloadBuilder use payload type application/vnd.stellaops.replay+json. Consumers should treat this as canonical unless a future manifest revision increments the schema and payload type together.
6. CLI Interface
6.1 Recording a Scan
stella scan image:tag --record ./out/
Produces:
out/
├─ manifest.json
├─ manifest.dsse.json
├─ inputbundle.tar.zst
├─ outputbundle.tar.zst
└─ signatures/
6.2 Verifying
stella verify manifest.json
Checks all hashes and DSSE envelopes.
Prints summary:
✅ Verified: SBOM, Findings, VEX, Tools, Feeds, Policy
6.3 Replaying
stella replay manifest.json --strict
stella replay manifest.json --what-if --vary=feeds
--strict: all inputs locked; identical result expected.--what-if: varies only specified dimension(s).
6.4 Diffing
stella diff manifestA.json manifestB.json
Shows field-level differences (feed snapshot, tool, or policy hash).
7. PostgreSQL Schema
7.1 replay_runs
{
"_id": "uuid",
"manifestHash": "sha256:...",
"status": "verified|failed|replayed",
"createdAt": "...",
"updatedAt": "...",
"signatures": [{ "profile": "FIPS", "verified": true }],
"outputs": {
"sbom": "sha256:...",
"findings": "sha256:..."
}
}
7.2 bundles
{
"_id": "sha256:...",
"type": "input|output|rootpack",
"size": 4123123,
"location": "/var/lib/stella/bundles/<sha>.tar.zst"
}
7.3 subjects
{
"ociDigest": "sha256:abcd...",
"layers": [
{ "layerDigest": "...", "merkleRoot": "...", "leafCount": 120 }
]
}
8. Layer Merkle Implementation
8.1 Algorithm
static string ComputeMerkleRoot(string layerTarPath)
{
const int ChunkSize = 4 * 1024 * 1024;
var hashes = new List<byte[]>();
using var fs = File.OpenRead(layerTarPath);
var buffer = new byte[ChunkSize];
int read;
using var sha = SHA256.Create();
while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
hashes.Add(sha.ComputeHash(buffer, 0, read));
while (hashes.Count > 1)
hashes = hashes
.Select((h, i) => (h, i))
.GroupBy(x => x.i / 2)
.Select(g => sha.ComputeHash(g.SelectMany(x => x.h).ToArray()))
.ToList();
return Convert.ToHexString(hashes.Single());
}
8.2 Stored Values
{
"layerDigest": "sha256:...",
"merkleRoot": "b81f...",
"leafCount": 240,
"leavesHash": "sha256:..."
}
9. Replay Engine Implementation Notes (.NET 10)
9.1 Manifest Parsing
Use System.Text.Json with deterministic ordering:
var options = new JsonSerializerOptions {
WriteIndented = false,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
TypeInfoResolverChain = { new OrderedResolver() }
};
9.2 Stable Output
Normalize SBOM/Findings/VEX JSON:
string Canonicalize(string json) =>
JsonSerializer.Serialize(
JsonSerializer.Deserialize<JsonDocument>(json),
options);
9.3 Verification Flow
var manifest = Manifest.Load("manifest.json");
VerifySignatures(manifest);
VerifyHashes(manifest);
if (mode == Strict) RunPipeline(manifest);
else RunPipelineWithVariation(manifest, vary);
9.4 Failure Modes
| Condition | Action |
|---|---|
| Missing snapshot or bundle | Error: InputBundleMissing |
| Feed hash mismatch | Error: FeedSnapshotDrift |
| Tool binary hash mismatch | Reject replay |
| Output hash drift in strict mode | Mark as failed, emit diff log |
| Invalid signature | Reject manifest |
10. Crypto Profiles and RootPack
10.1 Example Profiles
| Profile | Algorithms | Notes |
|---|---|---|
| FIPS-140-3 | ECDSA-P256 / SHA-256 / AES-GCM | Default for US/EU |
| GOST | GOST R 34.10-2012 / GOST R 34.11-2012 | Russia |
| SM | SM2 / SM3 / SM4 | China |
| eIDAS | RSA-PSS / SHA-256 | EU qualified signatures |
10.2 Dual-Signing Example
stella sign manifest.json --profiles=FIPS,GOST
Produces:
signatures/
├─ manifest.dsse.fips.json
└─ manifest.dsse.gost.json
11. Test Strategy
| Test | Description | Expected Result |
|---|---|---|
| Golden Replay | Repeat identical scan → same outputs | ✅ identical hashes |
| Feed Drift Test | Replay with updated feeds | Only inputs.feeds changes |
| Tool Upgrade Test | Replay with new scanner version | Reject or diff by tools |
| Policy Change Test | Different lattice/mutes | Diff by policy section |
| Cross-Arch Test | x64 vs arm64 | Identical outputs |
| Corrupted Bundle | Tamper bundle | Verification fails |
12. Example Verification Output
$ stella verify manifest.json
[✓] Manifest integrity: OK
[✓] DSSE signatures (FIPS,GOST): OK
[✓] Feeds snapshot hash: OK
[✓] Policy + mutes hash: OK
[✓] Toolchain hash: OK
[✓] SBOM/VEX outputs: OK
Result: VERIFIED
13. Future Extensions
- Support SPDX 3.0.1 alongside CycloneDX 1.6.
- Add per-file Merkle proofs for local scans.
- Ledger anchoring (Rekor, distributed Proof-Market).
- Post-quantum signatures (Dilithium/Falcon).
- Replay orchestration API (
/api/replay/:id).
14. Summary
Deterministic Replay freezes every element of a scan:
image → feeds → policy → toolchain → environment → outputs → signatures.
By enforcing canonical input/output states and verifiable cryptographic bindings, Stella Ops achieves regulatory-grade replayability, regional crypto compliance, and immutable provenance across all scans.
See also
- Replay module overview — service, API surface, and persistence.
- Replay architecture — internal design and component layout.
- Replay manifest guide — authoring and reading manifests.
- Determinism Specification — the platform-wide determinism invariant.
- Runtime facts — how runtime traces (
runtime_tracesnamespace) are captured for replay.
