Skip to content

Conformance

v0.5.0 Interop: CI-enforced

Three implementations of the Agent Receipt Protocol — the Go, Python, and TypeScript SDKs, separate codebases that share no code — verify one another’s receipts. Every conformance vector is signed once with a single shared Ed25519 keypair; each SDK then re-verifies the signatures, canonical JSON (RFC 8785), and receipt hashes the other two produced. Verification is exercised in both directions: a positive corpus every implementation must accept, and a MUST-reject corpus of tampered receipts and broken chains every implementation must refuse. The frozen vectors span four pinned spec versions (v0.2.0 through v0.5.0), so a receipt emitted by any SDK at any of those versions is provably readable by the other two.

What this cross-language check does and does not prove

Section titled “What this cross-language check does and does not prove”

The three SDKs share no code: each is a separate implementation of the same specification in a different language (Go, Python, TypeScript). They must agree byte-for-byte on canonical JSON (RFC 8785) and receipt-hash output, and agree on which signatures are valid. Interop is wired through a single shared Ed25519 keypair: the TypeScript SDK is the canonical source of the keypair (sdk/py/tests/fixtures/ts_vectors.json); the Go and Python SDKs generate their own vectors with that same key. Because the key is shared, any SDK can verify a receipt any other SDK signed — the cross-language property the tests assert.

What this proves. The three language implementations do not diverge in serialisation. A canonicalization, number-formatting, string-escaping, or signature-encoding difference between Go, Python, and TypeScript would produce mismatched bytes or a failed signature and turn a test red. Cross-language byte-level drift is the bug class this suite is built to catch.

What it does not prove. These SDKs are written and maintained by a single author, so they are not independent interpretations of the specification. A single misreading of an ambiguous spec requirement can be encoded identically into all three and still pass every check here — the vectors agree with each other, not with an independent reading of the spec. This suite is a serialisation-drift detector, not a spec-ambiguity detector; catching spec-interpretation ambiguity would require implementations from independent parties, which do not exist yet.

Each row is a frozen vector set. The Go / Py / TS columns mark which implementations consume and verify that set in their test suites. Vector counts are generated from the committed JSON by scripts/conformance_matrix/count.py, and a CI reconciliation test asserts every count in this table — and the figures in the callout below — equal that script’s output. It re-runs whenever the vector corpus, the spec, this page, or count.py changes, so an edit that outdates a number here turns the check red before merge.

Vector setPurposeGoPyTSSpec version(s)Vectors
canonicalizationRFC 8785 canonical JSON + receipt-hash agreementversion-independent44
emit-failureemit-failure outcome contract (ADR-0025)version-independent2
malformed (MUST-reject)negative corpus every verifier MUST rejectversion-independent11
v0.2.0frozen v0.2.0 receipts + chains0.2.03
v0.3.0frozen v0.3.0 receipts + chains0.3.03
v0.4.0frozen v0.4.0 receipts + chains0.4.02
v0.5.0frozen v0.5.0 receipts + chains0.5.03
did:key resolutiondid:key v0.7 resolution wire shape (ADR-0007)did:key v0.73
disclosure envelopeparameter-disclosure envelope: pinned ciphertextenvelope v12
rotation eventkey-rotation event verifies under the outgoing key0.2.11

Each linked vector set above is pinned to an immutable commit, so a citation stays valid as the corpus grows.

The rotation event row is pinned at spec v0.2.1 rather than 0.2.0 because, per its vector README, every field is exactly what a 0.2.1 receipt already requires with ADR-0015’s keyRotation field layered on and nothing else changed — so its wire shape tracks the 0.2.1 schema baseline it extends.

Every vector in the MUST-reject corpus names the normative spec clause it enforces, so a reviewer can trace each negative case to the requirement it guards. The Enforces column is generated from each vector’s clause field by count.py — the same generator that emits the counts — and pinned to this page by the reconciliation test; it is never hand-typed. Each entry links to the published spec section it enforces. (The source spec’s §7.3.5 and §7.8 fold into the published chain-integrity-verification section, which has no finer on-page anchor.)

VectorLayerEnforces
wrong_proof_typereceipt§4.3.3 proof — type MUST be Ed25519Signature2020
mutated_action_typereceipt§7.8 End-to-end receipt verification — signature over canonical bytes
mutated_principal_idreceipt§7.8 End-to-end receipt verification — signature over canonical bytes
truncated_proof_valuereceipt§7.8 End-to-end receipt verification — signature over canonical bytes
wrong_multibase_prefixreceipt§4.3.3 proof — proofValue is u-prefixed base64url
flipped_proof_bytereceipt§7.8 End-to-end receipt verification — signature over canonical bytes
schema_missing_required_fieldreceipt§4.3.2 credentialSubject — chain.chain_id is required
missing_previous_receipt_hash_mid_chainchain§7.3 Chain integrity verification — hash linkage (previous_receipt_hash)
chain_wrong_previous_receipt_hashchain§7.3 Chain integrity verification — hash linkage (previous_receipt_hash)
chain_duplicate_sequencechain§7.3.5 Sequence number contiguity and store trust — sequence MUST equal predecessor + 1
chain_sequence_gapchain§7.3.5 Sequence number contiguity and store trust — sequence MUST equal predecessor + 1

Cross-language verification is enforced in continuous integration, not run by hand. Every pull request and every push to main that touches an SDK, the shared vectors, or the specification runs that SDK’s cross-language suite:

  • sdk-go.yml — runs the Go CrossLanguage integration tests, which verify TypeScript- and Python-signed receipts in Go and reject the malformed corpus.
  • sdk-py.yml — runs tests/test_cross_language_go.py and tests/test_cross_language_ts.py, verifying Go- and TS-signed receipts in Python.
  • sdk-ts.yml — runs the TypeScript cross-language suite, verifying Go- and Python-signed receipts in TypeScript.
  • cross-sdk-tests.yml — runs the shared spec-example and version-pinned vector checks, the CLI↔WASM verifier gate, and the reconciliation test that pins this page’s vector counts to count.py.

Path filtering scopes each run to the changed surface: a change to the shared vectors or the spec re-runs all three SDK suites; a change confined to one SDK re-runs at least that SDK’s cross-language verification. A regression that breaks interop — a canonicalization drift, a signature-encoding change, a verifier that stops rejecting a malformed receipt — turns a required check red before merge.

The frozen vectors and the suites that consume them live in the repository. Run each line from the repository root; the per-suite commands use a subshell so the directory change does not leak into the next line:

Terminal window
# Go: verify TS- and Python-signed receipts, reject the malformed corpus
(cd sdk/go && go test -tags=integration -v -run CrossLanguage)
# Python: verify Go- and TS-signed receipts
(cd sdk/py && uv run pytest tests/test_cross_language_go.py tests/test_cross_language_ts.py -v)
# TypeScript: verify Go- and Python-signed receipts
(cd sdk/ts && pnpm test -- cross-language)
# Shared spec-example and version-pinned vector checks
(cd cross-sdk-tests && go test -tags=integration -v)
# did:key resolution vectors (ADR-0007) — one command per SDK
(cd sdk/go && go test -tags=integration -v -run TestDIDKeyVectors ./did/...)
(cd sdk/py && uv run pytest tests/test_did_key_vectors.py -v)
(cd sdk/ts && pnpm exec vitest run did-key-vectors)
# Regenerate the vector counts in the matrix above
python3 scripts/conformance_matrix/count.py --format md
# Regenerate the "Enforces" clause-traceability table
python3 scripts/conformance_matrix/count.py --format enforces

See cross-sdk-tests/README.md for how the shared vectors are generated and consumed.