Verify a Trust Record¶
Check the integrity and schema conformance of a TRACE Trust Record you received from a third party, and decide whether to trust the agent session it describes.
What you'll learn¶
- What
verify_record()does internally, step by step - How to verify against a pinned trusted key instead of the embedded key
- How built-in schema validation rejects malformed signed records
- How to collect all validation errors with
iter_errors() - How to interpret
runtime.platformto distinguish development from hardware-attested records - What to do when verification fails
Prerequisites¶
Understand What verify_record() Does¶
When you call verify_record(record, trusted_key) the library:
- Requires the exact TRACE v0.2 profile and validates the complete record against its canonical JSON Schema
- Reads
record["signature"]and base64url-decodes it to raw bytes - Resolves the trusted key you supplied, checking
kty == "OKP"andcrv == "Ed25519"before reconstructing anEd25519PublicKeyfrom thexfield - Enforces freshness: rejects records whose
iatis older thanmax_age_seconds(default 24h) or further in the future thanmax_future_skew_seconds(default 5m), and, if you passexpected_nonce, compares it in constant time toruntime.nonce - Rebuilds the canonical payload with all fields except
signature, using RFC 8785 (JCS) - Calls
Ed25519PublicKey.verify(sig_bytes, payload_bytes)from thecryptographylibrary - Returns
Noneon success, raisescryptography.exceptions.InvalidSignatureon a bad signature andValueErroron every other rejection
A trusted key is required. A record cannot authenticate itself with the key it embeds, so verify_record will not fall back to cnf.jwk unless you explicitly opt in with allow_embedded_key=True (which emits a UserWarning, since it proves only internal consistency, not authenticity).
import json
from agentrust_trace import verify_record
from cryptography.exceptions import InvalidSignature
with open("session.trace.json") as f:
record = json.load(f)
# trusted_jwk obtained out-of-band (see "Verify Against a Pinned Public Key" below)
try:
verify_record(record, trusted_jwk)
print("signature valid")
except InvalidSignature:
print("signature invalid — record may have been tampered with")
except ValueError as e:
print(f"verification failed: {e}")
ValueError is raised when schema conformance fails, no trusted key is supplied, the record is missing a signature field, a key or signature cannot be decoded, the JWK type is not Ed25519, or the record is stale. Treat all of these as verification failure.
Verify Against a Pinned Public Key¶
The embedded cnf.jwk names the key the record claims made its mandatory signature binding. It cannot establish trust by itself. Supply a trusted public key out of band; verify_record() requires the embedded and trusted keys to have the same RFC 7638 thumbprint, then verifies the signature with the trusted key.
Pass either an Ed25519PublicKey object or a JWK dict you obtained out-of-band (for example, from the issuer's published key manifest):
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
import base64
# Trusted JWK obtained from the issuer's key directory
trusted_jwk = {
"kty": "OKP",
"crv": "Ed25519",
"x": "<base64url public key bytes>",
}
# verify_record accepts a JWK dict directly
verify_record(record, trusted_jwk)
Or with an Ed25519PublicKey object:
x_bytes = base64.urlsafe_b64decode(trusted_jwk["x"] + "==")
pub_key = Ed25519PublicKey.from_public_bytes(x_bytes)
verify_record(record, pub_key)
When you supply a key, the library first requires it to identify the same public key as cnf.jwk, ignoring optional JWK metadata such as kid, and then uses the trusted key for signature verification. A key mismatch raises ValueError; a matching key with an invalid signature raises InvalidSignature.
Validate the Schema¶
Signature verification confirms the record was not modified after signing. It says nothing about whether the record conforms to the TRACE v0.1 schema. A valid signature over a malformed record is still a malformed record.
Call validate_json() to check schema conformance. It raises jsonschema.ValidationError on the first violation:
from agentrust_trace import validate_json
import jsonschema
try:
validate_json(record)
except jsonschema.ValidationError as e:
print(f"schema violation: {e.message}")
print(f"at path: {list(e.absolute_path)}")
To collect all violations at once instead of stopping at the first:
from agentrust_trace import iter_errors
errors = iter_errors(record)
if errors:
for e in errors:
print(f"{list(e.absolute_path)}: {e.message}")
else:
print("schema valid")
Run schema validation before trusting any claims in the record. A record that passes both verify_record() and validate_json() was signed by the stated key and has a well-formed structure.
Check the EAT Profile¶
Every TRACE record carries an eat_profile field that identifies the spec version. Reject records with an unexpected profile before parsing their claims:
EXPECTED_PROFILE = "tag:agentrust-io.com,2026:trace-v0.2"
if record.get("eat_profile") != EXPECTED_PROFILE:
raise ValueError(f"unexpected eat_profile: {record.get('eat_profile')!r}")
Interpret the Appraisal Status¶
After verifying the signature and schema, read appraisal.status:
status = record["appraisal"]["status"]
if status == "affirming":
# All evidence passed appraisal. Safe to act on the session output.
pass
elif status == "warning":
# Evidence passed with conditions. Review before acting.
pass
elif status == "contraindicated":
# Evidence failed. Treat the session output as untrusted.
raise RuntimeError("appraisal contraindicated — do not process agent output")
elif status == "none":
# No appraisal performed (software-only Level 0 record).
# Acceptable for development; not acceptable for production.
pass
Distinguish Software-Only from Hardware-Attested Records¶
The runtime.platform field tells you the attestation root. Before trusting a record in a production context, confirm it is not a development record:
runtime = record["runtime"]
if runtime["platform"] == "software-only":
# All-zero measurement, no TEE binding. Only accept in dev/test.
raise ValueError("software-only records are not accepted in production")
# Hardware-attested platforms
HARDWARE_PLATFORMS = {
"intel-tdx",
"amd-sev-snp",
"nvidia-h100",
"nvidia-blackwell",
"aws-nitro",
"arm-cca",
"google-confidential-space",
"tpm2",
}
if runtime["platform"] not in HARDWARE_PLATFORMS:
raise ValueError(f"unknown platform: {runtime['platform']!r}")
print(f"platform: {runtime['platform']}")
print(f"measurement: {runtime['measurement']}")
For hardware-attested records, runtime.measurement is a real digest from the TEE. To confirm the key was generated inside the attested enclave, compare runtime.measurement against the published Reference Integrity Manifest at runtime.rim_uri. See Hardware attestation platforms for per-platform details.
Complete Verification Sequence¶
Put it together for a production verifier:
import json
from agentrust_trace import verify_record, validate_json, iter_errors
from cryptography.exceptions import InvalidSignature
import jsonschema
def verify_trust_record(path: str, trusted_jwk: dict) -> dict:
with open(path) as f:
record = json.load(f)
# 1. Schema validation first — reject malformed records early
errors = iter_errors(record)
if errors:
messages = [e.message for e in errors]
raise ValueError(f"schema violations: {messages}")
# 2. Signature verification
try:
verify_record(record, trusted_jwk)
except InvalidSignature:
raise RuntimeError("signature invalid — record tampered or wrong key")
except ValueError as e:
raise RuntimeError(f"record malformed: {e}")
# 3. Profile check
if record.get("eat_profile") != "tag:agentrust-io.com,2026:trace-v0.2":
raise ValueError(f"unexpected eat_profile: {record.get('eat_profile')!r}")
# 4. Appraisal
status = record["appraisal"]["status"]
if status == "contraindicated":
raise RuntimeError("appraisal contraindicated — do not process agent output")
return record
What to Do When Verification Fails¶
If verify_record() raises InvalidSignature:
- Do not process the agent output.
- Do not rely on any claim in the record.
- Log the failure with the record's
subjectandiatfields for audit purposes. - Investigate whether the record was modified in transit or the wrong key was used.
A failed signature means either the record was tampered with after issuance, or it was not signed by the key in cnf.jwk. Either way, the record cannot be trusted.
Summary¶
You verified a TRACE Trust Record by checking its Ed25519 signature, validating its schema, and interpreting the appraisal status. Signature verification uses the embedded cnf.jwk by default; pass a trusted key to pin verification to a specific issuer. Schema validation with validate_json() or iter_errors() is a separate step that confirms the record structure is well-formed.
Related tutorials: