Verification Library¶
ca2a-verify is the library and command-line tool that checks permission chains and signed handoff records offline, with no network access. This page lists its functions and options. It is for developers and auditors who need to check evidence after the fact.
ca2a-verify checks delegation credentials and signed TRACE record paths offline. The caller supplies trusted credential issuers and record-signing keys; cryptographic consistency alone does not establish authorization.
API¶
from ca2a_verify import verify_delegation_chain, verify_chain_file, ChainResult
result: ChainResult = verify_chain_file(
"chain.json", trusted_root_issuers={"<trusted-root-issuer-hex>"}
)
# result.hops, result.root_issuer, result.leaf_subject, result.leaf_scope,
# result.revocation ("not_checked" here, since no snapshot was supplied)
verify_delegation_chain(chain, trusted_root_issuers=..., max_depth=8, at_time=None)verifies a list ofDelegationCredentialagainst an explicit local root trust set and returns aChainResultsummary, or raises aCA2AErrorsubtype.verify_chain_file(path, trusted_root_issuers=..., max_depth=8, at_time=None)loads a chain from JSON (a bare list, or{"chain": [...]}) and verifies it against that trust set.- Both also take
revocations=Noneandmax_revocation_staleness=None.load_revocation_snapshot(path)loads a snapshot ({"as_of": ..., "revocations": [...]}) and checks every statement's signature.
Root trust is mandatory. A self-consistent chain from an unknown root is cryptographically well formed but is not authorized and therefore does not produce a successful verification result.
at_time is the Unix time validity windows are evaluated at; None means the current time. An auditor replaying recorded evidence passes the time the action was decided, not its own. See delegation chain.
Revocation status¶
ChainResult.revocation_checked is False, and ChainResult.revocation is "not_checked", whenever no revocation snapshot was supplied. The chain verified, but whether any hop was revoked is not known. With a snapshot and no revoked hop they are True and "not_revoked", and revocation_as_of is the snapshot's as_of. The CLI prints the same field:
ca2a verify-chain --chain chain.json --trusted-root-issuer <hex> --revocations revocations.json --max-revocation-staleness 300
Consulting a snapshot contacts nobody, so verification stays offline. Fetching current snapshots is the deployment's job. See delegation chain.
Errors¶
All verification failures are subtypes of CA2AError, re-exported as VerificationError. Each carries a stable code and an HTTP status. The specific codes and the invariants they map to are in delegation chain.
Offline by design¶
Chain verification contacts no server. For reproducible historical checks, preserve the document, trust set, verifier version, and evaluation time. The default current-time validity check can change its verdict as credentials expire.
Signed TRACE record paths¶
verify_trace_dag(records, trusted_keys=..., max_age_seconds=None) verifies signatures from trusted keys, record structure, and parent hashes along one ordered root-to-leaf path. It returns a TraceDagResult. The default omits a record-age limit for historical audits; supply max_age_seconds when freshness is required.
cross_check_trace_dag(records, chain) then checks path length, non-root credential IDs, and that each record's signing key (cnf.jwk) equals chain[i].subject, the Ed25519 delegate named by that credential. Run credential and signed-record verification first. This cross-check does not bind the TRACE record's free-text subject field (a SPIFFE/DID URI) to the credential subject, and does not prove task completion. Those are different namespaces; cnf.jwk is already a raw key and is what the hop "acted under."
The API name uses DAG terminology, but the input is one ordered path, not an arbitrary branching tree. The unsigned DelegationRecord helper has a separate verify_dag consistency check; it is not a substitute for signature verification. See provenance DAG and TRACE A2A profile.