Verification¶
Verification is the step where a relying party (the service deciding whether to trust an agent) checks a manifest: is the signature from an issuer it trusts, has the manifest expired or been revoked, and do the recorded artifacts match what it observes. This page documents the function that runs those checks and the optional web endpoint that exposes them.
The core verification engine and FastAPI router. See Tutorial: Server-side verification for usage examples.
Public API¶
For gateway and runtime-session binding, call the package-root export rather than the private _verify module:
verify_manifest() is the supported high-level entry point. VerificationContext.trusted_keys maps an issuer key_id (the SHA-256 hex of the public key bytes) to its base64url-encoded Ed25519 public key, the form returned by Ed25519KeyPair.public_b64url(). A consumer that holds raw public key bytes must base64url-encode them before populating trusted_keys. VerificationContext.trusted_key_issuers maps the same key_id to the issuer URIs that key may sign for (spec section 5.3.3). It is required for VALID: without it the result is UNVERIFIABLE, and a key not authorized for the manifest's issuer is MISMATCH. agent_manifest.signing_pre_image() is the v0.1 signature pre-image: signers and verifiers share it for the exact RFC 8785 canonical byte sequence, including the hitl_record.approvals normalization, so a relying party never reconstructs the pre-image itself. It does not apply to v0.2 manifests. A v0.2 manifest has no field allowlist and no approvals normalization at the payload level; a verifier checks the COSE Sig_structure over the payload bytes exactly as received (agent_manifest.verify_cose_manifest(), built on agent_manifest.cose_payload() for the producer side), with hitl_record.approvals carried in the unprotected header rather than normalized out of the signed payload.
Core function¶
verify_manifest ¶
verify_manifest(manifest: Union[dict[str, Any], bytes], context: VerificationContext, revocation_store: 'RevocationStore', *, _envelope: Optional[CoseVerification] = None) -> VerificationResult
Core verification engine - hosting-model agnostic and fail-closed.
Checks version compatibility, signature, expiry, revocation, artifact hashes, delegation chain, and HITL. Returns a VerificationResult with per-field status and mismatch details.
Accepts either envelope, selected by what it is given (ADR-0011):
- A
dictis a version 0.1 manifest carrying a detachedsignatureblock, verified over the RFC 8785 pre-image exactly as it always has been. bytesare a version 0.2 COSE envelope (COSE_Sign1orCOSE_Sign). The signature is checked over the payload as received, and receipts, attestation, and approvals are read from the unprotected header after the signature is settled.
Fail-closed semantics (spec 5.3 - VALID requires a valid signature):
- A manifest with an unsupported (or missing)
versionreturnsINCOMPATIBLE_VERSIONwithout further processing (spec 2.4). - A manifest without a
signatureblock returnsSIGNATURE_MISSING. - A signed manifest verified without any
trusted_keysin the context returnsUNVERIFIABLE- neverVALID. - Issuer key authorization (spec 5.3.3) completes before the signature is verified, on both envelopes. Without
trusted_key_issuersit cannot complete and the result isUNVERIFIABLE; a key not authorized for the manifest'sissuerisMISMATCH. - A delegation chain that cannot be verified (no
delegation_public_keys) is markedUNVERIFIABLEand the overall result isUNVERIFIABLE(spec 3.4.1 / 5.2). - A delegation chain that verifies signatures and scope narrowing but carries a non-empty
scope_grant.constraints(a Cedar statement this verifier does not parse or evaluate) is also markedUNVERIFIABLE, notVALID(spec 3.4.1 / 5.2). enforce_hitl=Truewith nohitl_recordin the manifest is a failure (HitlResult.MISSINGand a non-VALID overall result).- An approval is never
APPROVEDunless its own signature verifies with a trusted approver key and binds the current manifest ID and scope.
The _envelope parameter is internal: it carries an already-appraised COSE envelope into the shared pipeline and is not part of the public API.
Context¶
VerificationContext ¶
Bases: BaseModel
Runtime artifact hashes and keys provided by the trusted component.
Results¶
VerificationResult ¶
Bases: BaseModel
OverallResult ¶
Bases: str, Enum
FieldsVerified ¶
Bases: BaseModel
FieldResult ¶
Bases: str, Enum
DelegationResult ¶
Bases: str, Enum
HitlResult ¶
Bases: str, Enum
MismatchDetail ¶
Bases: BaseModel
EvidencePack is an optional reference (trace id, signer, hash, and URI) to an externally retained evidence pack that a verifier can record alongside a result.
EvidencePack ¶
Bases: BaseModel
TRACE envelopes and evidence packs¶
EvidencePack above is only a reference to a pack. To appraise the pack itself, and the per-tool-call TRACE envelopes inside it, use:
A TRACE envelope (spec §6.3.2) is signed by a TEE-sealed key over the RFC 8785 canonical form of every field except signature. The envelope carries no algorithm or key id of its own, so the caller supplies both; trusted_keys uses the same key_id → base64url public key mapping as VerificationContext. An evidence pack (spec §5.2.1) instead carries a detached pack_signature object in the form of §3.6, so hybrid signatures work there but not on envelopes.
Read admissible, not just status. A TRACE reporting manifest_verification_result: MISMATCH or EXPIRED can have a perfectly valid signature (the runtime honestly recorded a bad state), but spec §6.3.2 says it "MUST NOT be accepted as evidence of a valid tool call for regulatory reporting purposes". verify_trace_envelope() returns status=VERIFIED with admissible=False in that case.
Passing manifest= additionally enforces the §6.3.2 hash-conflict rule (SCHEMA F-21): if the envelope's policy_hash differs from the manifest's artifacts.policy_bundle.hash, the envelope must declare MISMATCH. One that claims VALID over a conflicting hash is a spec violation and fails.
Verification is fail-closed: a missing key, an unknown algorithm, or a build without the [pq] extra yields UNVERIFIABLE, never VERIFIED.
verify_evidence_pack() also appraises the section 5.2 verification_result inside the pack. The pack signature proves who assembled the pack, not what the result says, so the pack is VERIFIED only when that result is VALID and its manifest_id names the pack's manifest. Its verification_signature is a bare string signed by the attestation service over verification_result_pre_image(); pass result_key_id (and result_algorithm for ML-DSA-65) to check it. Without result_key_id it is not checked, verification_result_signature_verified stays False, and the result carries a verification_result_signature_not_appraised warning. signature_verified refers to the outer pack_signature only.
verify_trace_envelope ¶
verify_trace_envelope(envelope: dict[str, Any], *, trusted_keys: Optional[dict[str, str]] = None, key_id: Optional[str] = None, algorithm: str = 'Ed25519', manifest: Optional[dict[str, Any]] = None) -> TraceVerificationResult
Verify one TRACE envelope's signature and manifest binding (spec 6.3.2).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
envelope | dict[str, Any] | The TRACE envelope dict. | required |
trusted_keys | Optional[dict[str, str]] | key_id (sha256 hex of raw public key bytes) -> base64url public key. The envelope carries no key id of its own. | None |
key_id | Optional[str] | Which trusted key signed this envelope. Optional when exactly one key is trusted. | None |
algorithm | str |
| 'Ed25519' |
manifest | Optional[dict[str, Any]] | When supplied, enforces the spec 6.3.2 hash-conflict rule (SCHEMA F-21) against | None |
Returns:
| Name | Type | Description |
|---|---|---|
A | TraceVerificationResult | class: |
TraceVerificationResult | the envelope as evidence -- a verified signature alone is not enough. |
verify_evidence_pack ¶
verify_evidence_pack(pack: dict[str, Any], *, trusted_keys: Optional[dict[str, str]] = None, expected_pack_hash: Optional[str] = None, trace_key_id: Optional[str] = None, trace_algorithm: str = 'Ed25519', verify_envelopes: bool = True, result_key_id: Optional[str] = None, result_algorithm: str = 'Ed25519') -> EvidencePackVerificationResult
Verify an evidence pack's pack_signature, its embedded verification result, and its TRACE envelopes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pack | dict[str, Any] | The evidence pack document (spec 5.2.1). | required |
trusted_keys | Optional[dict[str, str]] | key_id -> base64url public key, covering the pack's TEE-sealed key, the envelope signing key and, when | None |
expected_pack_hash | Optional[str] | When supplied (e.g. the | None |
trace_key_id | Optional[str] | Key id for the envelopes inside the pack. | None |
trace_algorithm | str | Envelope signature algorithm. | 'Ed25519' |
verify_envelopes | bool | Set False to appraise only the pack signature. | True |
result_key_id | Optional[str] | Key id of the attestation service that signed the embedded section 5.2 result. When given, its | None |
result_algorithm | str |
| 'Ed25519' |
Returns:
| Name | Type | Description |
|---|---|---|
An | EvidencePackVerificationResult | class: |
EvidencePackVerificationResult | only when the pack signature verifies, the embedded result says | |
EvidencePackVerificationResult |
| |
EvidencePackVerificationResult |
| |
EvidencePackVerificationResult | (when checked) every envelope is admissible. |
TraceVerificationResult dataclass ¶
Appraisal of one TRACE envelope.
admissible is the question a relying party actually asks: may this record be used as evidence of a valid tool call? That needs a verified signature and a manifest_verification_result outside :data:INADMISSIBLE_RESULTS. A perfectly-signed envelope reporting MISMATCH is authentic and inadmissible at the same time -- the signature proves the runtime honestly recorded that the policy hash did not match, which is exactly the case spec 6.3.2 excludes from reporting.
EvidencePackVerificationResult dataclass ¶
Appraisal of an evidence pack and, optionally, the TRACEs inside it.
signature_verified describes the outer pack_signature only. verification_result_signature_verified is True only when the embedded section 5.2 result's verification_signature was checked against a caller-named key (result_key_id) and verified.
TraceStatus ¶
Bases: str, Enum
Outcome of appraising a TRACE envelope or evidence pack.
compute_pack_hash() returns the spec §5.2.1 pack_hash: the SHA-256 of the pack's canonical bytes excluding pack_signature. trace_signing_pre_image() and evidence_pack_pre_image() are the shared pre-image functions; producers and verifiers MUST both use them so the byte sequences match.
compute_pack_hash ¶
Return "sha256:<64-hex>" over the pack's canonical bytes (spec 5.2.1).
trace_signing_pre_image ¶
Return the RFC 8785 canonical bytes a TRACE signature covers.
Every field except signature itself. Both producers and verifiers MUST call this so the byte sequences are identical.
evidence_pack_pre_image ¶
Return the RFC 8785 canonical bytes pack_signature covers.
Every field except pack_signature, which spec 5.2.1 appends to the document after hashing and signing.
verification_result_pre_image ¶
Return the RFC 8785 canonical bytes a section 5.2 verification_signature covers.
Spec 5.1.2 rule 4 requires the signature to cover the whole result, so this is every field except verification_signature itself, the same construction as :func:trace_signing_pre_image.
Revocation¶
RevocationStore is the revocation lookup a verifier consults during verify_manifest(); the default is in-memory, and production deployments back it with a persistent store. RevocationRecord is a single revocation entry: which manifest was revoked, when, why, and by whom.
RevocationStore ¶
In-memory revocation store. Production should use a persistent backend.
RevocationRecord ¶
Bases: BaseModel
FastAPI router¶
create_router ¶
create_router(manifest_store: dict[str, dict[str, Any]], revocation_store: RevocationStore, cose_context: Optional[VerificationContext] = None) -> Any
Return a FastAPI APIRouter with /verify and /revocation-status endpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest_store | dict[str, dict[str, Any]] | Dict mapping manifest_id -> manifest dict. | required |
revocation_store | RevocationStore | Revocation store instance. | required |
cose_context | Optional[VerificationContext] | Trust configuration for | None |