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_MODEflag (off|shadow|dual|primary). The CLI commands return a friendly “no attestation available” message until your deployment enables the surface (dualorprimary).
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:
- Envelope shape.
payloadTypemust beapplication/vnd.in-toto+json; at least one signature must be present. - Payload decode. The base64-encoded
payloadmust decode to JSON. - 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. - Trust root. The public key comes from
GET /api/v1/public/keys. The verifier matches the envelope’ssignatures[].keyidto a registry entry by SHA-256 fingerprint of the DER SPKI. Thepublic_key_pemfield embedded on the envelope row is never used as the trust root — it’s there for offline human inspection only. - Key validity. The matched key must be inside its
valid_from/valid_untilwindow atattested_at, not revoked beforeattested_at, and have anenvironment_labelmatching--env. Prod keys do not verify staging envelopes and vice versa. - Statement shape.
_typemust behttps://in-toto.io/Statement/v1,predicateTypemust 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 evidencesurface. - MCP Tool Reference — the
get_evidence_attestationMCP tool. - ADR 0003 (in the platform monorepo) — the architecture decision and threat model.