Proof Verification Operations Runbook

Version: 1.2.0
Sprint: 3500.0004.0004
Last Updated: 2026-05-31

Audience: operators and auditors verifying Stella Ops evidence — attestation bundles and proof spines — on the CLI or via the Scanner API.

This runbook covers operational procedures for proof verification across the two proof surfaces that exist in the codebase today:

  1. Attestation bundle verification via the stella proof verify CLI (StellaOps.Cli, ProofCommandGroupAttestationBundleVerifier). This verifies a .tar.gz bundle exported from the Export Center, containing a DSSE envelope wrapping an in-toto statement.
  2. Proof Spine inspection via the Scanner WebService HTTP API (ProofSpineEndpointsProofSpineVerifier). A proof spine is an ordered, hash-chained sequence of typed evidence segments (SBOM slice → match → reachability → policy eval …) for a single artifact/vulnerability/profile tuple.

Source-of-truth note. Several procedures in earlier drafts described a 4-leaf Merkle tree of sbom/rules/policy/feed hashes, X.509 certificate-chain validation, and a POST .../proofs/{rootHash}/verify endpoint. None of those exist in src/. This revision reconciles the runbook against the actual code: src/Cli/StellaOps.Cli/Commands/Proof/, src/Cli/StellaOps.Cli/Services/AttestationBundleVerifier.cs, src/Scanner/StellaOps.Scanner.WebService/Endpoints/ProofSpineEndpoints.cs, src/Scanner/__Libraries/StellaOps.Scanner.ProofSpine/, and src/Scanner/__Libraries/StellaOps.Scanner.Core/ProofBundleWriter.cs.


Table of Contents

  1. Overview
  2. Attestation Bundle Verification (CLI)
  3. Proof Spine Verification (API)
  4. Offline Verification
  5. Transparency Log Integration
  6. Troubleshooting
  7. Monitoring & Alerting
  8. Escalation Procedures

1. Overview

What is Proof Verification?

Proof verification cryptographically validates that StellaOps evidence has not been tampered with. Two independent surfaces implement it:

Attestation bundle (CLI)AttestationBundleVerifier checks, in order:

Proof Spine (API)ProofSpineVerifier checks each segment and the spine:

Verification Components

ComponentWhereVerification
Attestation DSSE envelope (attestation.dsse.json)CLI bundlePayload ↔ statement _type; signature presence
in-toto statement (statement.json)CLI bundleSubjects + predicate type extraction
checksums.txt / <bundle>.sha256CLI bundleSHA-256 integrity
transparency.ndjsonCLI bundlePresence (online); content not re-verified by the CLI
Proof spine segmentsScanner APIDSSE (HMAC) + hash-chain + id recomputation
Spine root hashScanner APIRecompute from segment result hashes

Trust Model (as implemented)

in-toto Statement (statement.json)
   └── DSSE Envelope (attestation.dsse.json)   ← payload _type must match statement
         └── Attestation bundle (.tar.gz)        ← checksums.txt + optional .sha256
               └── transparency.ndjson (optional Rekor entries)

Proof Spine (Scanner)
   Segment[0] SBOM_SLICE  ──prevHash=null
   Segment[1] MATCH       ──prevHash=Segment[0].resultHash
   Segment[2] REACHABILITY──prevHash=Segment[1].resultHash
   ...                                     each DSSE-signed (HMAC)
   rootHash = hash( join(":", segment.resultHash[]) )
   spineId  = hash( artifactId:vulnId:profileId:rootHash )[0..32]

Note on “Merkle”. ProofSpineModels describes RootHash as a “Merkle root hash”, but ProofSpineVerifier.ComputeRootHash is a flat hash of the colon-joined segment result hashes — not a binary Merkle tree with inclusion proofs. There is no X.509 root/intermediate CA hierarchy in the proof code; DSSE segments are signed with an HMAC key profile (HmacDsseSigningService).


2. Attestation Bundle Verification (CLI)

2.1 Basic Bundle Verification

The stella proof verify command operates on an attestation bundle .tar.gz (see §2.4 for the layout). Source: ProofCommandGroup.BuildVerifyCommandAttestationBundleVerifier.VerifyAsync.

# Verify an attestation bundle (.tar.gz)
stella proof verify --bundle bundle.tar.gz

# Short flag form
stella proof verify -b bundle.tar.gz

# Offline mode (skips Rekor/transparency *and* the crypto signature check)
stella proof verify --bundle bundle.tar.gz --offline

# Machine-readable output
stella proof verify --bundle bundle.tar.gz --output json

Flags implemented by the command:

FlagAliasEffect
--bundle-bPath to the attestation bundle .tar.gz (required).
--offlineSkip transparency check and crypto signature verification.
--output-otext (default) or json.
--verboseEnables debug logging only; does not add output sections.

Not implemented. --check-rekor, --skip-rekor, and --crl are not options of this command. Online verification implies transparency-presence checking; --offline is the only modifier. There is no separate stella proof compute-root, stella proof rekor-entry, stella proof show, or stella proof offline-kit verb (see §5 and the CLI quick reference).

Expected Output (Success — text)

PrintTextResult emits:

Proof Verification Result
========================================
Status:         PASS
Bundle:         bundle.tar.gz
Root Hash:      sha256:abc123...
Attestation ID: <id from metadata.json>
Export ID:      <export id from metadata.json>
Predicate:      <predicateType from statement.json>
Subjects:       1
  - my-image@sha256:...

Verification Checks:
----------------------------------------
  [PASS] File integrity
  [PASS] DSSE envelope format
  [PASS] Signature validation
  [PASS] Transparency log          (or [SKIP] Transparency log (offline mode))

Expected Output (Success — json)

{
  "valid": true,
  "status": "verified",
  "bundlePath": "bundle.tar.gz",
  "rootHash": "sha256:abc123...",
  "attestationId": "...",
  "exportId": "...",
  "subjects": ["my-image@sha256:..."],
  "predicateType": "...",
  "checks": [
    {"check": "file_integrity", "status": "pass", "details": "Bundle checksums verified"},
    {"check": "dsse_signature", "status": "pass", "details": "DSSE envelope signature valid"},
    {"check": "transparency_log", "status": "pass", "details": "Transparency entry verified or skipped (offline)"}
  ]
}

The check names emitted by BuildVerificationChecks are exactly file_integrity, dsse_signature, and transparency_log. There are no merkle_root, certificate_chain, or not_expired checks.

Expected Output (Failure)

On failure the text renderer prints Status: FAIL and a single [FAIL] <error message> line; the json renderer sets valid: false and a status of failed (or error for missing file / exceptions). The process exit code is set from AttestationBundleExitCodes (see §2.3).

2.2 Verification Check Order

AttestationBundleVerifier.VerifyAsync runs these steps in order; the first failure short-circuits with the corresponding exit code:

  1. Bundle file existsFileNotFound (6).
  2. Outer SHA-256 against a co-located <bundle>.tar.gz.sha256 sidecar if present (skipped silently when absent) → ChecksumMismatch (2).
  3. Extract the gzip+tar archive → FormatError (5) on archive/JSON errors.
  4. Internal checksums from checksums.txt (transparency.ndjson treated as optional) → ChecksumMismatch (2).
  5. DSSE signature — decode the base64 payload and require its in-toto _type to equal statement.json’s _type; online mode additionally requires ≥1 signature entry → SignatureFailure (3).
  6. Transparency presence — when not offline, require transparency.ndjsonMissingTransparency (4).

2.3 Exit Codes

AttestationBundleExitCodes (src/Cli/StellaOps.Cli/Services/Models/AttestationBundleModels.cs):

CodeConstantMeaning
0SuccessBundle verified.
1GeneralFailureGeneral failure.
2ChecksumMismatchOuter or internal checksum mismatch.
3SignatureFailureDSSE payload/statement mismatch or missing signature.
4MissingTransparencytransparency.ndjson missing (online mode).
5FormatErrorArchive or file-format error.
6FileNotFoundBundle file not found.
7ImportFailed(Import path only.)

The unrelated ProofExitCodes class (Success/PolicyViolation/SystemError/…) is used for the command-level wrapper and error paths in ProofCommandGroup; the bundle-verification result returns AttestationBundleExitCodes.

2.4 Attestation Bundle Layout

The .tar.gz produced by the Export Center and consumed by the CLI contains these entries (AttestationBundleVerifier constants):

bundle.tar.gz
├── attestation.dsse.json   # DSSE envelope { payloadType, payload(b64), signatures[] }
├── statement.json          # in-toto statement (_type, predicateType, subject[])
├── transparency.ndjson     # optional Rekor entries (one JSON object per line)
├── metadata.json           # exportId, attestationId, tenantId, rootHash, subjectDigests[]
└── checksums.txt           # "<sha256>  <filename>" lines (optional, '#'-comments allowed)

bundle.tar.gz.sha256        # optional co-located outer-checksum sidecar

This is not the manifest.json/certificate.pem/dsse-envelope.json layout described in earlier drafts. There is no certificate.pem in the bundle; trust is anchored via the DSSE envelope and (optionally) transparency entries, not an embedded X.509 chain.

2.5 Inspecting a Bundle Manually

# List bundle contents
tar -tzf bundle.tar.gz

# Inspect the DSSE envelope
tar -xzOf bundle.tar.gz attestation.dsse.json | jq .

# Decode the DSSE payload (the in-toto statement)
tar -xzOf bundle.tar.gz attestation.dsse.json | jq -r '.payload' | base64 -d | jq .

# Confirm payload _type matches the statement
tar -xzOf bundle.tar.gz statement.json | jq -r '._type'

# Verify the outer checksum (if a .sha256 sidecar exists)
sha256sum -c bundle.tar.gz.sha256

3. Proof Spine Verification (API)

A proof spine is the Scanner’s hash-chained evidence record for one artifact/vulnerability/policy-profile tuple. Endpoints are mapped by ProofSpineEndpoints.MapProofSpineEndpoints (read-only; both require the scanner.scans.read scope) and verified by ProofSpineVerifier.

3.1 Endpoints

MethodPath (default segments)ReturnsScope
GET…/spines/{spineId}Full spine + per-segment + spine-level verificationscanner.scans.read
GET…/scans/{scanId}/spinesSummaries of spines for a scan runscanner.scans.read

Path prefixes come from ScannerWebServiceOptions.Api (SpinesSegment default spines, ScansSegment default scans) under the configured API group. Both endpoints also support CBOR via Accept: application/cbor. There is no POST .../proofs/{rootHash}/verify endpoint — verification runs server-side on GET .../spines/{spineId} and is returned inline.

Scope note (flag). scanner.scans.read is declared locally in the Scanner WebService (ScannerAuthorityScopes/ScannerPolicies) and is not present in the canonical Authority scope catalog (StellaOps.Auth.Abstractions/StellaOpsScopes.cs, which uses dotted scopes like scanner:read/scanner:export). Verify the token actually carries scanner.scans.read (and that it is provisioned in the client’s allowed_scopes) before relying on these endpoints in a given environment.

# Fetch and verify a single spine
curl "https://scanner.stellaops.local/<api>/spines/$SPINE_ID" \
  -H "Authorization: Bearer $TOKEN"

# List spines for a scan run
curl "https://scanner.stellaops.local/<api>/scans/$SCAN_ID/spines" \
  -H "Authorization: Bearer $TOKEN"

3.2 Spine Response Shape

The single-spine response (ProofSpineResponseDto) includes the segments and an inline verification block. Each segment carries its own DSSE envelope, the hash-chain fields, and (on failure) verificationErrors:

{
  "spineId": "…",
  "artifactId": "…",
  "vulnerabilityId": "…",
  "policyProfileId": "…",
  "verdict": "…",
  "verdictReason": "…",
  "rootHash": "…",
  "scanRunId": "…",
  "supersededBySpineId": null,
  "segments": [
    {
      "segmentId": "…",
      "segmentType": "SBOM_SLICE",
      "index": 0,
      "inputHash": "…",
      "resultHash": "…",
      "prevSegmentHash": null,
      "envelope": { "payloadType": "application/vnd.stellaops.proofspine.segment+json",
                    "payload": "<b64>", "signatures": [{ "keyid": "…", "sig": "…" }] },
      "toolId": "…",
      "toolVersion": "…",
      "status": "verified",
      "createdAt": "…",
      "verificationErrors": null
    }
  ],
  "verification": { "isValid": true, "errors": [] }
}

Segment segmentType wire values: SBOM_SLICE, MATCH, REACHABILITY, GUARD_ANALYSIS, RUNTIME_OBSERVATION, POLICY_EVAL.

Per-segment status (lowercased from ProofSegmentStatus): verified, untrusted (signature valid but key not trusted), or invalid.

3.3 Spine-Level Verification Errors

ProofSpineVerifier records these spine-level error codes in verification.errors (and per-segment in verificationErrors):

Error codeMeaning
root_hash_mismatchRecomputed spine root hash ≠ stored rootHash.
spine_id_mismatchRecomputed spineId ≠ stored spineId.
segment_index_mismatch:<a>-><b>Segment index not equal to its position.
prev_hash_expected_nullFirst segment has a non-null prevSegmentHash.
prev_hash_mismatchSegment prev hash ≠ previous segment resultHash.
segment_id_mismatchRecomputed segment id ≠ stored segmentId.
signed_segment_type_mismatch / signed_index_mismatch / signed_fields_mismatchSigned payload disagrees with the segment record.
dsse_payload_not_base64 / signed_payload_json_invalidMalformed signed payload.
<dsse failure reason>Whatever HmacDsseSigningService.VerifyAsync reports (e.g. dsse_key_not_trusted).

A spine is isValid: true only when there are no spine-level errors and every segment status is verified or untrusted.

Defense in depth. HandleGetSpineAsync re-validates the assembled DTO through ProofSegmentValidator and records structural drift to the scanner_proofspine_validation_failures_total counter. This validation does not gate the HTTP response.

3.4 CLI Spine Command (partial)

stella proof spine show <bundle-id> exists but is not yet implemented — it prints a placeholder (“Spine display not yet implemented”) and directs the user to stella proof verify. There is no stella proof spine --bundle form. Use the HTTP API above for spine inspection until the CLI verb lands.


4. Offline Verification

4.1 When to Use Offline Verification

4.2 Prerequisites

For attestation-bundle verification, the only required input is the bundle .tar.gz itself; an optional co-located <bundle>.tar.gz.sha256 sidecar lets the verifier check the outer checksum. The DSSE envelope, in-toto statement, internal checksums, and (optional) transparency entries are all carried inside the bundle, so no external CA bundle or trust-root file is consumed by the CLI verifier today.

Not implemented. There is no stella proof offline-kit create command and no verify.sh / ca-bundle.pem / trust-roots.json kit generator. The --offline-kit flag that exists in the CLI belongs to the stella sbomer drift analyze command (alias diff), not to proof verification. (The stella offline kit … command tree is a separate pull/import/status surface for offline kits, unrelated to proof verification.)

4.3 Running Offline Verification

# Offline bundle verification
stella proof verify --bundle bundle.tar.gz --offline

4.4 Offline Verification Checks (as implemented)

CheckOnlineOfflineNotes
Bundle existsLocal file check.
Outer SHA-256 (.sha256 sidecar)Only if sidecar present.
Internal checksums (checksums.txt)Per-file SHA-256.
DSSE payload ↔ statement _typeLocal JSON comparison.
DSSE cryptographic signaturepartial¹Skipped entirely offline.
Transparency (transparency.ndjson) presenceRequired only online.

¹ Online mode currently only checks that ≥1 signature entry exists and that the payload matches the statement; full trust-root signature validation is not yet wired into AttestationBundleVerifier. Flag for follow-up.

4.5 Air-Gap Considerations

Because the bundle is self-contained, air-gap transfer is just a matter of moving the .tar.gz (and its optional .sha256 sidecar) across the boundary and running stella proof verify --bundle … --offline. There is no stella ca export command and no CRL/OCSP support in the proof CLI; certificate revocation checking is not implemented.


5. Transparency Log Integration

5.1 Rekor Overview

StellaOps can publish attestations to a Sigstore-style Rekor transparency log, and the Attestor service periodically re-verifies inclusion (RekorVerificationJob / RekorVerificationService, sprint SPRINT_20260117_001_ATTESTOR_periodic_rekor_verification). Within the proof surfaces covered by this runbook, transparency manifests as the transparency.ndjson entry inside an attestation bundle.

in-toto statement → DSSE envelope → (optional) Rekor → transparency.ndjson in bundle

5.2 Transparency in the CLI Verifier

The CLI verifier treats transparency as a presence check, not a re-validation:

# Inspect the transparency entries shipped in the bundle
tar -xzOf bundle.tar.gz transparency.ndjson | jq .

Not implemented. stella proof verify --check-rekor, stella proof verify --skip-rekor, and stella proof rekor-entry are not CLI verbs/flags. There is no built-in rekor-cli wrapper in stella proof.

5.3 Attestor-Side Rekor Verification

Continuous Rekor inclusion verification is an Attestor-service concern, surfaced operationally through:

See the Attestor module dossier for the Rekor verification job configuration (RekorVerificationOptions).


6. Troubleshooting

6.1 DSSE Signature / Payload Mismatch

Symptom (CLI): exit code 3 (SignatureFailure); message such as DSSE payload does not match statement _type, DSSE envelope not found or has no payload, or DSSE envelope has no signatures.

Diagnostic Steps:

# Inspect the envelope and statement
tar -xzOf bundle.tar.gz attestation.dsse.json | jq .
tar -xzOf bundle.tar.gz statement.json | jq -r '._type'

# Decode the payload and compare its _type
tar -xzOf bundle.tar.gz attestation.dsse.json | jq -r '.payload' | base64 -d | jq -r '._type'

Common Causes:

CauseResolution
Bundle modified after exportRe-export from the Export Center.
Payload/statement _type driftRe-export; the envelope payload must match statement.json.
Envelope has no signatures (online)Re-export with signing enabled, or verify with --offline.
Base64/encoding corruptionRe-download; the payload must be valid base64.

6.2 Spine Root-Hash / Chain Mismatch (API)

Symptom (API): verification.errors contains root_hash_mismatch, spine_id_mismatch, prev_hash_mismatch, or segment_id_mismatch; one or more segments report status: invalid.

Diagnostic Steps:

  1. Inspect the inline verification block and each segment’s verificationErrors from GET …/spines/{spineId}.
  2. Confirm segment ordering: index must be contiguous from 0 and each prevSegmentHash must equal the previous segment’s resultHash.
  3. Confirm the spine root hash: it is the hash of the colon-joined resultHash values across all segments, in order.

Resolution:

6.3 Checksum Mismatch (CLI)

Symptom: exit code 2 (ChecksumMismatch); message SHA-256 checksum mismatch (outer sidecar) or Checksum mismatch for '<file>' (internal).

Diagnostic Steps:

# Verify the outer checksum if a sidecar exists
sha256sum -c bundle.tar.gz.sha256

# Re-derive a single internal file's hash and compare to checksums.txt
tar -xzOf bundle.tar.gz attestation.dsse.json | sha256sum
tar -xzOf bundle.tar.gz checksums.txt

Resolution:

6.4 Bundle Extraction / Format Error

Symptom: exit code 5 (FormatError); message Failed to extract bundle contents.

Diagnostic Steps:

file bundle.tar.gz
gzip -t bundle.tar.gz
tar -tzf bundle.tar.gz
ls -la bundle.tar.gz

Resolution:


7. Monitoring & Alerting

Reconciliation note. Earlier drafts listed metrics such as proof_verification_total, proof_verification_failures, proof_verification_duration_ms, proof_verification_success_total, and certificate_expiry_days. None of those metric names exist in src/. The metrics below are the ones actually emitted. The CLI verifier (stella proof verify) is a one-shot client command and does not export Prometheus metrics.

7.1 Key Metrics (as implemented)

MetricSourceNotes
scanner_proofspine_validation_failures_totalProofSpineValidationMetrics (meter StellaOps.Scanner.ProofSpine)Incremented when a served spine DTO fails structural re-validation; tagged reasonCode + tenantId (tenantId defaults to unknown).
scanner_proofspine_validation_successes_totalProofSpineValidationMetrics (meter StellaOps.Scanner.ProofSpine)Incremented on successful structural re-validation; tagged tenantId. Used to compute a validation failure rate alongside the failures counter.
attestor_rekor_verification_runs_totalRekorVerificationMetrics (Attestor)Count of periodic Rekor inclusion-verification runs.
attestor_rekor_verification_run_failures_totalRekorVerificationMetrics (Attestor)Count of failed Rekor verification runs.
attestor_rekor_verification_failure_rateRekorVerificationMetrics (Attestor)Rolling failure rate of Rekor verification.

The proof-spine validation metrics live in the ProofSpine library, not the WebService: confirm against src/Scanner/__Libraries/StellaOps.Scanner.ProofSpine/Diagnostics/ProofSpineValidationMetrics.cs (meter name StellaOps.Scanner.ProofSpine; counters FailureCounterName / SuccessCounterName) and the Rekor metrics against src/Attestor/StellaOps.Attestor/StellaOps.Attestor.Core/Verification/RekorVerificationMetrics.cs before wiring dashboards; do not rely on the metric names alone.

7.2 Example Grafana / Prometheus Queries

# Proof-spine structural validation failures (Scanner)
sum(rate(scanner_proofspine_validation_failures_total[1h]))

# Rekor verification failure rate (Attestor)
attestor_rekor_verification_failure_rate

# Failed Rekor verification runs
sum(rate(attestor_rekor_verification_run_failures_total[1h]))

7.3 Example Alert Rules

groups:
  - name: proof-verification
    rules:
      - alert: ProofSpineValidationFailures
        expr: rate(scanner_proofspine_validation_failures_total[1h]) > 0
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: Scanner is serving proof spines that fail structural re-validation

      - alert: RekorVerificationFailing
        expr: attestor_rekor_verification_failure_rate > 0
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: Attestor periodic Rekor inclusion verification is failing

There is no signing-certificate-expiry metric or alert in the proof code today; any certificate-lifecycle alerting belongs to the signing/KMS subsystem, not this runbook. Flag for follow-up if cert-expiry alerting is required.


8. Escalation Procedures

8.1 Escalation Matrix

SeverityConditionResponse TimeEscalate To
P1 - CriticalMass bundle/spine verification failures (tamper suspected)15 minutesPlatform Team + Security Team
P2 - HighAttestor Rekor verification failing (attestor_rekor_verification_failure_rate > 0)1 hourPlatform Team
P2 - HighPersistent scanner_proofspine_validation_failures_total1 hourScanner Team
P3 - MediumSingle bundle verification failure4 hoursSupport Queue

8.2 P1: Mass Verification Failures Response

  1. Immediate Actions (0-15 min):

    • Capture exit codes / verification.errors from failing verifications.
    • Determine whether the failure is integrity (ChecksumMismatch, root_hash_mismatch) — indicating tamper — versus format/transport.
    • Notify stakeholders; treat suspected tamper as a security incident.
  2. Triage (15-60 min):

    • Re-export/re-fetch a known-good bundle or spine and re-verify to isolate whether the issue is producer-side or transport-side.
    • For spine failures, check scanner_proofspine_validation_failures_total and the Doctor Attestor checks (RekorConnectivityCheck, etc.).
  3. Post-Incident:

    • Record root cause and remediation in the relevant sprint Execution Log.
    • File follow-up work for any verification gaps surfaced (e.g. full trust-root signature validation in the CLI verifier — see §4.4).

8.3 Contacts

Update these to your deployment’s actual on-call rosters; the addresses below are placeholders, not verified routing.

RoleContactAvailability
Security Teamsecurity@stellaops.ioBusiness hours
Platform On-Callplatform-oncall@stellaops.io24/7
Attestor / Scanner Team(per deployment)Business hours

Appendix A: DSSE Envelope & Payload Types

A.1 Attestation-bundle DSSE envelope (attestation.dsse.json)

{
  "payloadType": "<in-toto predicate / statement media type>",
  "payload": "<base64-encoded in-toto statement>",
  "signatures": [
    { "keyid": "<key id>", "sig": "<base64-encoded signature>" }
  ]
}

The base64 payload decodes to the in-toto statement.json (with _type, predicateType, and a subject[] array). The CLI verifier requires the decoded payload’s _type to equal statement.json’s _type. Earlier drafts that claimed a manifest predicate with sbomHash/rulesHash/policyHash/feedHash and a fixed payloadType of application/vnd.stellaops.proof+json do not match the code — there is no such fixed payload type and the manifest hash fields are named concelierSnapshotHash, excititorSnapshotHash, and latticePolicyHash on ScanManifest.

A.2 Proof-spine segment DSSE envelope

Each spine segment carries a DSSE envelope whose payload type is application/vnd.stellaops.proofspine.segment+json (ProofSpineBuilder.DefaultSegmentPayloadType). The decoded payload contains segmentType, index, inputHash, resultHash, and prevSegmentHash, which the verifier cross-checks against the segment record.

A.3 Function-proof (FuncProof) DSSE envelope

FuncProof documents use the media type application/vnd.stellaops.funcproof+json (FuncProofConstants.MediaType, and the same value for FuncProofConstants.DssePayloadType). The DSSE envelope is verified by FuncProofDsseService (src/Scanner/__Libraries/StellaOps.Scanner.Evidence/FuncProofDsseService.cs).

Flag — verbs not wired. The stella funcproof generate|verify|info|export command builders and handlers exist (src/Cli/StellaOps.Cli/Commands/Proof/FuncProofCommandGroup.cs + FuncProofCommandHandlers.cs), but FuncProofCommandGroup.BuildFuncProofCommand is never registered into the CLI rootCommandFactory.BuildRootCommand wires ProofCommandGroup.BuildProofCommand (CommandFactory.cs:159) but has no corresponding BuildFuncProofCommand call. As a result stella funcproof … is not invokable in the current build despite the code being present. See the Appendix B note before documenting these as available.

A.4 Score proof bundle (ProofBundleWriter)

The score replay/proof bundle written by ProofBundleWriter is a ZIP. The current CreateZipBundleAsync writes exactly four entries: manifest.json, manifest.dsse.json, score_proof.json (the ProofLedger nodes + rootHash, serialized via ProofLedger.ToJson), and meta.json. The ledger rootHash (ProofHashing.ComputeRootHash) is sha256:<hex> over the canonical JSON array of per-node hashes — a flat ordered chain, not a Merkle tree.

Flag. The ProofBundleWriter class XML doc-comment lists an optional proof_root.dsse.json (DSSE envelope for the root hash), but the current CreateZipBundleAsync does not emit it and ReadBundleAsync does not read it — it is aspirational, not produced today. Do not expect a proof_root.dsse.json member in bundles from this writer.


Appendix B: CLI Quick Reference

# Attestation-bundle verification (ProofCommandGroup)
stella proof verify --bundle <path.tar.gz>            # Verify bundle (alias -b)
stella proof verify --bundle <path> --offline         # Skip transparency + crypto sig check
stella proof verify --bundle <path> --output json     # JSON output (alias -o; text|json)
stella proof verify --bundle <path> --verbose         # Extra debug logging only

# Proof-spine (CLI verb exists but is NOT YET IMPLEMENTED)
stella proof spine show <bundle-id>                   # Prints placeholder; use the HTTP API

# Function-level proof (FuncProofCommandGroup) — BUILDERS EXIST BUT NOT WIRED INTO THE CLI ROOT (see below)
stella funcproof generate --binary <path> [--sign] [--transparency] [--registry <ref>]
stella funcproof verify --proof <path> [--binary <path>] [--offline] [--strict] [--format text|json]
stella funcproof info <proof>
stella funcproof export <proof> --output <dir> [--format bundle|evidence-locker] [--include dsse,tlog-receipt,raw-proof]

Flag — stella funcproof is not currently reachable. The flag/verb shapes above are accurate to FuncProofCommandGroup, but BuildFuncProofCommand is never added to the root command (CommandFactory.BuildRootCommand registers BuildProofCommand only). Until a root.Add(FuncProofCommandGroup.BuildFuncProofCommand(...)) registration lands, treat stella funcproof … as not yet available at the CLI even though the implementation exists. (scanner.funcproof* keys in ConfigCatalog are server-side scanner config, not CLI command registrations.)

Not implemented (do not document as available): stella proof verify --check-rekor / --skip-rekor / --crl, stella proof compute-root, stella proof rekor-entry, stella proof show, stella proof offline-kit …, and stella signer cert show|rotate|export / stella ca export. The --offline-kit flag exists only on sbomer drift analyze (alias diff).


Revision History

VersionDateAuthorChanges
1.0.02025-12-20AgentInitial release
1.1.02026-05-30AgentDeep doc↔code reconciliation against src/Cli (ProofCommandGroup, AttestationBundleVerifier, FuncProofCommandGroup) and src/Scanner (ProofSpineEndpoints, ProofSpineVerifier, ProofBundleWriter). Corrected bundle layout (.tar.gz with attestation.dsse.json/statement.json/transparency.ndjson/metadata.json/checksums.txt), check names (file_integrity/dsse_signature/transparency_log), CLI flags/verbs, exit codes, DSSE payload types, ScanManifest hash fields, real metric names, and the API spine endpoints/scope (scanner.scans.read). Removed fabricated Merkle-tree / X.509 cert-chain / POST …/verify / offline-kit / stella signer cert content and flagged the offline crypto-signature gap.
1.2.02026-05-31AgentSecond reconciliation pass. Fixed ProofSpineValidationMetrics source location (ProofSpine library Diagnostics, not WebService) and footnote path; added the companion scanner_proofspine_validation_successes_total counter and the real meter name (StellaOps.Scanner.ProofSpine) + tags (reasonCode/tenantId). Corrected Appendix A.4 — ProofBundleWriter.CreateZipBundleAsync emits only manifest.json/manifest.dsse.json/score_proof.json/meta.json; proof_root.dsse.json is an aspirational doc-comment entry that is not produced. Corrected --offline-kit ownership (sbomer drift analyze / alias diff, not scan drift analyze). Flagged that stella funcproof … builders exist but BuildFuncProofCommand is never registered into the CLI root (CommandFactory.cs:159 wires only BuildProofCommand), so the verbs are not currently invokable.