Skip to main content
SPECIFICATION

Receipt format and test results.

The receipt format, verification rules and dated primitive benchmarks behind AGA. Classical Ed25519 is the operational default and live signer; the hybrid measurements describe a separate library profile.

Test environment

CPU
AMD Ryzen 5 3550H
OS
Windows 11
Toolchain
Go 1.26.1
Method
go test -bench

These measurements are from commodity laptop hardware. Performance on a different CPU or in a deployed gateway must be measured separately; these are primitive benchmarks, not end-to-end latency.

The figures measure signing and verification cost, key and signature sizes, and the cadence those primitive costs permit. They come from the Go reference code using go test -bench; the benchmark harness is available with evaluation access.

Hybrid means the specified ML-DSA-65 + Ed25519 composite. Its ML-DSA-65 primitive is checked against NIST FIPS 204 known-answer vectors in the private reference runtime's test suite, available with evaluation access. The npm package's verifier agrees with a Go verifier in the public repository on all 28 v2 cases.

The hybrid library ships in aga-mcp-server, not in the aga-verify CLI, and it is not what the live gateway signs. Classical Ed25519 remains the zero-dependency operational default. These profile and corpus checks are not proof of deployment performance.

Signing and verification performance

Scroll the table horizontally on small screens. Keyboard users can focus it and use the arrow keys.

OperationAlgorithmns/opB/opallocs/op
SignEd2551933,850641
SignML-DSA-65202,8004503
SignHybrid264,3003,9064
VerifyEd2551978,43000
VerifyML-DSA-6540,6604503
VerifyHybrid121,1004503

Ed25519 signs in roughly 34 microseconds with one 64-byte allocation and verifies with none. ML-DSA-65 costs about six times as much to sign, though in these runs it verifies faster than Ed25519, and the hybrid mode runs both, so its cost is close to the sum. Timings are medians of ten runs recorded on 2026-05-24, and the memory columns come from the same runs. Until 2026-09-25 four memory figures here did not match those runs, which is why a hybrid row showed less memory than its ML-DSA-65 component.

Key and signature sizes

ComponentEd25519ML-DSA-65HybridHybrid ÷ Ed25519
Public key (raw)32 B1,952 B1,992 B62.3x
Signature (raw)64 B3,309 B3,381 B52.8x
Public key (hex)643,9043,98462.3x
Signature (hex)1286,6186,76252.8x

Post-quantum keys and signatures are roughly 50 to 60 times larger than their classical counterparts. That size, not speed, is the practical cost of the migration, and it informs bundle storage and transport planning.

Governance cadence feasibility

CadenceWindowHybrid sign+verifyDecisions/windowFeasible
100ms100,000 us385 us259Yes
200ms200,000 us385 us519Yes
500ms500,000 us385 us1,298Yes
1000ms1,000,000 us385 us2,597Yes

Hybrid sign-and-verify totals roughly 385 microseconds per governance decision. At a 100 ms cadence, that overhead sits at about 0.4% of the measurement window, so the cryptography is not the bottleneck at these cadences.

Primitives and their standards

AGA is built from published primitives. The canonicalization, the Merkle tree and the composite encoding are AGA-defined and pinned by the conformance vectors; the composite is not the IETF LAMPS composite, which is still an Internet-Draft. The post-quantum primitive is checked against NIST known-answer vectors in the private reference runtime's test suite (evaluation access); in the public repository, the npm package's verifier and a Go verifier are shown to agree on all 28 v2 cases.

PrimitiveStandardRole
SHA-256FIPS 180-4Artifact addressing and receipt chaining
Ed25519RFC 8032Classical signatures and the reference baseline
ML-DSA-65FIPS 204Post-quantum signatures, NIST security category 3
Composite (ML-DSA-65 + Ed25519)AGA-defined (not the IETF LAMPS draft, not interoperable with it)Hybrid signing profile, a library export
JSON Canonicalization (JCS-lineage)Vector-definedByte-reproducible signature inputs
Merkle treesVector-defined, no-prefix SHA-256 (not RFC 6962)Signed checkpoint root; a tail dropped from a bundle fails against the signed checkpoint, though an earlier genuine export still verifies

Receipt format

The governance receipt schema, canonicalization rules, signing and chain-linking construction, the evidence-bundle format, and the steps a conformant verifier runs. This is the format the benchmarks above measure.

Governance Receipt (15 fields)

FieldTypeDescription
receipt_idstringUnique receipt identifier (a UUID with an rcpt- prefix in 3.6.0 through 3.6.2)
receipt_versionstringSchema version ("1.0")
algorithmstringSigning algorithm identifier
timestampstringISO 8601 UTC timestamp
request_idstring | nullMCP request identifier (3.6.0 through 3.6.2 emits a numeric id as a string)
methodstringMCP method ("tools/call", or, on a passthrough receipt, the other MCP method it records)
tool_namestringName of the tool being called ("(passthrough)" on a passthrough receipt)
decisionstringPERMITTED or DENIED as aga-proxy emits it (its default profile, permissive, denies nothing on policy grounds; policy denial needs --profile standard or restrictive, or a --policy file in allowlist or denylist mode); verifiers check the signature over it, not the value
reasonstringHuman-readable decision rationale
policy_referencestringSHA-256 of the policy's canonical JSON on aga-proxy (the built-in profile when no --policy file is given); an empty string in aga-mcp-server 3.6.0 through 3.6.2
arguments_hashstringSHA-256 of canonical arguments (tri-state)
previous_receipt_hashstringChain link to previous receipt
gateway_idstringSigning gateway identifier
signaturestringEd25519 signature (128 hex chars)
public_keystringEd25519 public key (64 hex chars)

Canonicalization (JCS-lineage)

All JSON serialization uses a JCS-lineage JSON Canonicalization Scheme, defined by the conformance vectors rather than literal RFC 8785:

  • Object keys sorted in UTF-16 code-unit order, as JavaScript sorts
  • No whitespace outside string values
  • ECMAScript Number.toString() for number serialization (1.0 becomes 1)
  • Negative zero normalizes to positive zero
  • Array element order preserved (not sorted)

Signing and chain linking

SigningDigest: Canonical JSON of receipt WITHOUT the signature field. Ed25519 signature computed over these bytes.

ChainDigest: SHA-256 of canonical JSON of receipt WITH the signature field. Used as previous_receipt_hash in the next receipt.

arguments_hash tri-state: absent arguments = empty string, empty object = SHA-256("{}"), content = SHA-256(canonicalize(arguments)).

Evidence Bundle Format

An evidence bundle packages a receipt chain with Merkle proofs for offline verification:

FieldDescription
schema_versionBundle format label ("2.0" in 3.6.0 through 3.6.2 bundles; not checked by the verifiers)
bundle_idBundle identifier (a UUID with an rcpt- prefix in 3.6.0 through 3.6.2; not checked by the verifiers)
algorithmAlgorithm suite ("Ed25519-SHA256-JCS")
generated_atISO 8601 bundle creation timestamp
gateway_idSigning gateway identifier
public_keyEd25519 public key (64 hex chars)
policy_referenceUnsigned copy, not checked by the verifiers: on aga-proxy the SHA-256 of the policy's canonical JSON, empty on aga-mcp-server 3.6.0 through 3.6.2. Read the signed per-receipt value instead.
receiptsOrdered array of GovernanceReceipt objects
merkle_rootSHA-256 Merkle root of receipt leaf hashes
merkle_proofsInclusion proofs for each receipt
checkpointRequired. Gateway-signed object binding the chain head: algorithm, gateway_id, generated_at, head_leaf_hash, leaf_count, merkle_root, signature
offline_capableBoolean (always true; not checked by the verifiers)

Verification: six checks, seven with a pinned issuer key

1. Structural floor

Verify the algorithm identifier ("Ed25519-SHA256-JCS"), public key well-formedness, and that every receipt matches the strict schema: exactly the canonical fields, none missing or extra (values such as the decision are signed, not checked against a list). The check runs on the parsed object: a field name repeated in the file is read last-wins and still verifies (known issue 5 on /security). Fail closed on unknown algorithms.

2. Receipt signatures

For each receipt: remove signature field, canonicalize, Ed25519 verify against public_key.

3. Chain and ordering

First receipt: previous_receipt_hash = "". Each subsequent: previous_receipt_hash = SHA-256(canonical(prev receipt WITH signature)); timestamps non-decreasing.

4. Merkle and bijection

Walk each proof from leaf to root via sibling hashes; compare to merkle_root. Proof count = receipt count, leaf indices a bijection over 0..N-1.

5. Signed checkpoint

Verify the gateway-signed checkpoint binds the recomputed Merkle root, leaf_count, and chain head leaf hash.

6. Envelope consistency

The bundle's stated roots match what was recomputed. Each proof's leaf_hash = SHA-256(canonical(receipt WITH signature)).

Why a no-prefix Merkle tree is safe here: every leaf is the SHA-256 of a strict-schema receipt's canonical JSON, never a raw 64-byte node value, and check 4 requires one proof per receipt with leaf indices forming a bijection over 0..N-1, bound by the signed leaf_count in check 5. Passing an interior node off as a leaf would need a schema-valid receipt whose canonical JSON hashes to it.

A seventh check, gateway key match, runs only when the verifier is given a pinned issuer key: it confirms the bundle was signed by that key (provenance). Without a pin, the six checks prove integrity only.

Algorithm Identifiers

IDSignatureHashCanonicalization
Ed25519-SHA256-JCSEd25519 (64-byte signature)SHA-256 (32-byte digest)JCS-lineage

Additional algorithm suites may be defined in future versions. Verifiers must reject unknown algorithm identifiers (fail closed). The published verifiers check the bundle's identifier, but not all of them check the others: aga-verify, aga-proxy verify, the reference verifiers, the /verify, /boundary and /attested-bench pages and the verify.mjs in sample-bundle.zip do not check a receipt's own identifier, and aga-governance 0.3.2 does not check the checkpoint's (/security lists both).

Cross-Language Conformance

Implementations in three toolchains (JavaScript, Go, Python) are checked against a shared conformance suite in the project repository, including a standalone verifier that uses no third-party cryptography:

JavaScript

aga-mcp-server engine + aga-verify (npm)

conformance + unit suite

Python

verify.py reference verifier in the harness; the PyPI SDK (PyNaCl) is checked on the same 61 cases separately, as the harness feeds them. Given the literal bytes of the corpus's leaf_index case spelled as 0.0, the PyPI verifier returns FAILED. The npm verifiers and the reference verifiers return VERIFIED. The harness re-serializes object-level cases, so it runs that case as an integer.

61 of 61 agree as fed (SDK, 2026-09-23); the literal float leaf_index case fails (2026-09-25)

Go

Reference verifier CLI

cross-stack corpus checks

Offline Verifier

Standalone verification tool

cross-language conformance

The 61 classical cross-stack cases give identical verdicts as the harness feeds them: six verifier configurations agree on the 54 object-level cases, and the five file-parsing verifiers agree on the remaining seven raw-byte cases (the in-server engine never receives raw file bytes). The spec's canonical vectors pin byte-for-byte canonical output and matching Merkle roots. The post-quantum profile adds 28 composite cases across two independent-language oracles.

Published test corpus

A snapshot of the receipt specification's conformance files is published here as one pinned file, built on 2026-07-31: the canonical vectors, both JSON Schemas, the construction document, the JavaScript reference verifier, its conformance harness, a README for implementers and the license (9 files, listed below).

The LICENSE was replaced on 2026-09-25 with the Apache-2.0 text, because the 2026-07-31 copy's appendix carried a stray sentence and three words of its terms differed from the Apache text; every other file is unchanged. Compared file by file with the aga-receipt-spec folder of the source repository at v3.6.0 (unchanged at v3.6.2), the two vector files and the harness are byte-identical. The reference verifier, both schemas and the construction document are earlier versions: the repository's construction document adds a normative section on public-key and signature acceptance (§1.1) that the zip's copy states in one line, and its schemas add a note that they are illustrative.

The harness runs 14 tests over 3 evidence bundles of 1, 3, 5 receipts. The zip's verifier also handles two inputs differently from the repository's verifier. It treats a --pubkey that is not 64 lowercase hex as no pin (it reports integrity only, with pinned false, and exits 0), where the repository's verifier and aga-verify refuse it with exit 2. And it reports FAILED for a bundle labelled with the hybrid profile, as aga-verify does, where the repository's verifier reports UNSUPPORTED_PROFILE. Pass the pin exactly as published, or check it with aga-verify. The file was re-pinned on 2026-09-25 for the LICENSE only; its other files remain the 2026-07-31 snapshot until a reviewed release re-pins it against the repository's current versions. This download and its zero-dependency verifier cover the classical Ed25519 profile: the canonical primitive vectors and the evidence-bundle vectors. The 61 cross-stack verdict cases run from the source repository, not from this zip.

With Node, Go and Python installed and a Python virtual environment active (the harness runs python from your PATH), in a clone of github.com/attestedintelligence/aga-mcp-server, run these commands in order. The release tag pins the source used for the published 3.6.0 results:

  1. git checkout v3.6.0
  2. npm ci
  3. python -m pip install cryptography
  4. npm run build
  5. cd independent-verifier && npm ci && node build.mjs && cd ..
  6. npm run conformance:cross-stack

Without the verifier build the harness stops on a missing module. Without a Python Ed25519 library the default Python verifier reports FAILED on genuine bundles, and the run fails with the Python column in disagreement.

The README at that tag still carries a PyPI advisory recomputed on 2026-09-18, which says the fix for aga-governance raising on deeply nested input is not on PyPI. That advisory is superseded: 0.3.1 and later carry the fix, 0.3.0 is yanked, and 0.2.6, which has the same defect, is yanked too, so pin aga-governance>=0.3.1.

The 28 post-quantum composite cases are cross-verified in the reference implementation (the npm package and the Go oracle), which carries the ML-DSA-65 dependency this zero-dependency verifier deliberately omits. The zip is pinned by a SHA-256 so you can confirm the file you hold is the exact one published. Recompute the hash after downloading and confirm it matches. If it differs, do not trust the file.

SHA-256 (trust root)5e1fa147e5bcee2237a07f6acfb89ad7475a68d7e2c498faa4474d69b0d07bfd
sha256sum aga-conformance-vectors.zipshasum -a 256 aga-conformance-vectors.zipcertutil -hashfile aga-conformance-vectors.zip SHA256
Contents (9 files)
  • CANONICAL_CONSTRUCTION_v2.md
  • LICENSE
  • README.md
  • schema/evidence-bundle.schema.json
  • schema/receipt.schema.json
  • vectors/aga_evidence_bundle_vectors.json
  • vectors/aga_test_vectors.json
  • verify/conformance.test.mjs
  • verify/verify-sep.mjs

The reference verifier in this corpus carries the July 2026 cross-stack safe-integer fix (numbers outside the JS safe-integer range are rejected identically in all three languages). It now ships in every stack: aga-mcp-server 3.6.0 and later, aga-verify 2.2.0 and later, and aga-governance 0.3.1 and later all carry it and are published.

Run the corpus with node --test verify/conformance.test.mjs from the extracted directory: every genuine bundle must VERIFY and every negative mutation must FAIL. Verification runs without contacting the producer. The corpus is company-authored; audit the files yourself.

Source

The MCP server, the aga-verify offline verifier, the receipt specification, and this conformance corpus are public at github.com/attestedintelligence/aga-mcp-server. The repository is published under the MIT license; the receipt-spec directory carries its own Apache-2.0 license. Those published licenses state the source-availability posture in full.