Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Evidence Attestation

The Pretorin platform can sign each evidence record with a DSSE envelope wrapping an in-toto Statement v1, per ADR 0003 — Evidence DSSE Attestation Envelope. This page covers how to fetch and independently verify those envelopes from the CLI or any compliant DSSE verifier.

Beta surface. Attestation is gated by the platform-side EVIDENCE_ATTESTATION_MODE flag (off | shadow | dual | primary). The CLI commands return a friendly “no attestation available” message until your deployment enables the surface (dual or primary).

Why DSSE attestation matters

The evidence record itself is mutable on the platform side (status transitions, lifecycle updates, recipe re-runs). The DSSE envelope is the immutable, signed snapshot that audit packages reference. With it:

  • Auditors can verify offline that a piece of evidence as presented matches what the platform observed at attestation time.
  • CI pipelines can gate releases on a verifier returning exit 0 over the evidence used in the bundle.
  • Tooling downstream of the platform (cosign, in-toto verifiers) can consume the envelope without trusting the API.

CLI commands

Fetch the envelope

# Pretty summary
pretorin evidence attestation get ev-abc123

# Raw DSSE envelope JSON — pipe to any DSSE verifier
pretorin evidence attestation get ev-abc123 --json

# Lineage view: every prior signing of this evidence (newest first)
pretorin evidence attestation get ev-abc123 --lineage
pretorin evidence attestation get ev-abc123 --lineage --include-archived

get returns the latest completed envelope by default. Each evidence row may have multiple historical attestations (after a key rotation, after a content edit, etc.); --lineage surfaces all of them.

Verify the signature

# Default: verify against the prod-tier key registry
pretorin evidence attestation verify ev-abc123

# Other environments (staging, dev)
pretorin evidence attestation verify ev-abc123 --env staging

# Pin a specific signing key — extra safety check
pretorin evidence attestation verify ev-abc123 \
  --key-fingerprint 8b4c2e...64-hex-chars

verify exits 0 on success and 1 on failure. With --json the CLI emits a structured result:

{
  "evidence_id": "ev-abc123",
  "ok": true,
  "reason": null,
  "fingerprint": "8b4c2e...",
  "attested_at": "2026-05-27T00:00:00+00:00",
  "expected_env": "prod"
}

What the verifier actually checks

The CLI verifier is a direct port of the platform’s reference implementation, so client and server agree byte-for-byte on validity. Six checks run, in order:

  1. Envelope shape. payloadType must be application/vnd.in-toto+json; at least one signature must be present.
  2. Payload decode. The base64-encoded payload must decode to JSON.
  3. Signature. The signature is over the DSSE PAE bytes ("DSSEv1 LEN(type) type LEN(payload) payload"), not the canonical JSON Statement bytes directly. The verifier reconstructs the PAE and runs ECDSA P-256 + SHA-256 against it.
  4. Trust root. The public key comes from GET /api/v1/public/keys. The verifier matches the envelope’s signatures[].keyid to a registry entry by SHA-256 fingerprint of the DER SPKI. The public_key_pem field embedded on the envelope row is never used as the trust root — it’s there for offline human inspection only.
  5. Key validity. The matched key must be inside its valid_from/valid_until window at attested_at, not revoked before attested_at, and have an environment_label matching --env. Prod keys do not verify staging envelopes and vice versa.
  6. Statement shape. _type must be https://in-toto.io/Statement/v1, predicateType must be in the accepted set (default: https://pretorin.com/attestations/evidence/v1), and the subject must carry a non-empty SHA-256 digest.

Any failed check returns a structured reason that maps to one of the platform’s failure classes (shape, signature_mismatch, key_untrusted, other).

Verifying with cosign

The DSSE envelopes are spec-compliant, so any DSSE-aware verifier works. To verify with cosign:

# 1. Fetch the envelope.
pretorin evidence attestation get ev-abc123 --json > attestation.dsse.json

# 2. Pull the matching public key from the registry.
PUBKEY_FP=$(jq -r .signatures[0].keyid attestation.dsse.json)
pretorin --json evidence attestation get ev-abc123 \
  | jq -r .signatures[0].keyid  # double-check the fingerprint matches

# 3. Verify with cosign. Use the PEM from `GET /api/v1/public/keys` — the
#    CLI's own `verify` command does this lookup for you, but for cosign
#    you fetch the registry entry yourself.
cosign verify-blob-attestation \
  --insecure-ignore-tlog \
  --key pretorin-prod.pem \
  --signature attestation.dsse.json

CI integration

A common pattern is to gate a release on every evidence record in the bundle having a verifiable attestation:

#!/usr/bin/env bash
set -euo pipefail

EVIDENCE_IDS=( $(cat evidence-bundle.txt) )

for id in "${EVIDENCE_IDS[@]}"; do
  if ! pretorin evidence attestation verify "$id" --env prod >/dev/null 2>&1; then
    echo "::error::Attestation verification failed for $id"
    exit 1
  fi
done

echo "All ${#EVIDENCE_IDS[@]} evidence records have valid attestations."

MCP integration

External AI agents (Claude Code, Codex CLI, custom MCP clients) can call get_evidence_attestation to fetch the envelope as JSON:

// MCP tool call
{
  "tool": "get_evidence_attestation",
  "arguments": {
    "evidence_id": "ev-abc123",
    "include_lineage": true,
    "include_archived": false
  }
}

The response is a structured { evidence_id, attestation, lineage? } payload. When the platform attestation surface is disabled or the row has no envelope yet, the tool returns a structured attestation_unavailable error rather than an opaque HTTP 404, so the calling agent can react appropriately.

Predicate body — what’s actually signed

The Pretorin v1 predicate (https://pretorin.com/attestations/evidence/v1) wraps the 12-field audit metadata baseline that lands on every evidence row at write time:

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "name": "ev-abc123",
      "digest": { "sha256": "..." }
    }
  ],
  "predicateType": "https://pretorin.com/attestations/evidence/v1",
  "predicate": {
    "predicate_schema_version": "v1",
    "evidence_id": "ev-abc123",
    "kind": "configuration",
    "captured_at": "2026-05-27T00:00:00Z",
    "source_uri": "file://./terraform/main.tf",
    "source_label": "Terraform IaC",
    "producer_kind": "recipe",
    // ... plus the rest of the audit_metadata contract
  }
}

The subject digest is computed over the evidence body bytes; verifying the digest alongside the signature is what lets an auditor confirm the evidence content hasn’t changed since attestation.

See also

  • Evidence Commands — the full pretorin evidence surface.
  • MCP Tool Reference — the get_evidence_attestation MCP tool.
  • ADR 0003 (in the platform monorepo) — the architecture decision and threat model.