Why we picked Ed25519 and RFC 8785
Originally published 19 May 2026 on the earlier DCS AI site. Update: since this post, every R-Series receipt is hybrid-signed, Ed25519 together with ML-DSA-65 (NIST FIPS 204); the post-quantum section below has been brought up to date.
When you publish a receipt format, you commit every implementation to a set of design choices that are expensive to change. Every R-Series receipt is signed, canonicalised, hashed and chained using the primitives we picked. If those choices are wrong, every verifier inherits the mistake.
The format rests on Ed25519 signatures, RFC 8785 canonical JSON, SHA-256 digests and a hash-linked chain. None of these were defaults pulled from a tutorial. This post sets out what we considered, what we rejected and why.
If you only build on R-Series, none of this affects you directly: the verifier hides the primitives. If you are evaluating R-Series against alternatives, or thinking of designing your own provenance scheme, this is the reasoning underneath.
Four primitives, four choices
- Signature: Ed25519. Considered ECDSA over P-256, ECDSA over secp256k1, Schnorr, BLS and the lattice-based schemes.
- Canonicalisation: RFC 8785. Considered CBOR, MessagePack, Protobuf canonical form and a home-grown JSON canonicaliser.
- Hash: SHA-256. Considered SHA-512, SHA3-256, BLAKE2b and BLAKE3.
- Chain: an explicit linked list, each receipt carrying the digest of its predecessor. Considered Merkle trees and their variants.
Why Ed25519
The signature is the part that says "this agent did this thing". We needed it to be fast enough that signing never becomes the bottleneck, compact enough not to bloat receipts, implemented correctly in every major language, resistant to side channels, and standardised rather than clever.
Ed25519, specified in RFC 8032, meets all five.
Against ECDSA on P-256. ECDSA needs a unique, secret nonce for every signature, and a biased or repeated nonce leaks the private key. A well-known games console lost its firmware signing key exactly this way. RFC 6979 removes the dependence on a random-number generator by deriving the ECDSA nonce deterministically from the key and message, but an implementation has to adopt it. Ed25519 derives its nonce deterministically by design, so every conforming implementation avoids that class of failure. Curve25519 also uses constants chosen in the open for performance, and the signing algorithm is mechanically simpler, so there are fewer ways to implement it wrongly.
Against secp256k1. With ECDSA it carries the same nonce risk unless the implementation uses deterministic nonces (RFC 6979). We also prefer the receipt layer not to share a curve with unrelated systems, so a weakness found in one does not automatically become a weakness in the other.
Against Schnorr. Schnorr has real merits, especially for multi-party signing. But standard-library support is narrower than for Ed25519, and each R-Series issuer signs its own receipts, so aggregation is not a requirement today.
Post-quantum: where we were, and where we are now
This is the section that has changed. The original post argued for Ed25519 alone, with a planned migration to a post-quantum algorithm later. We have since made that move.
Every R-Series receipt is now hybrid-signed by default: an Ed25519 signature and an ML-DSA-65 signature, ML-DSA being the module-lattice signature standardised by NIST in FIPS 204. A verifier requires both. If either is missing or fails, the receipt fails.
The reasoning is diversification. Ed25519 is mature and widely understood, but its security rests on an elliptic-curve problem that a large enough quantum computer is expected to solve with Shor's algorithm. ML-DSA rests on a different mathematical family. Requiring both means a forger has to break both, and an unexpected weakness in the newer scheme does not leave a receipt unprotected.
The costs are real. An Ed25519 signature is 64 bytes; an ML-DSA-65 signature is 3,309 bytes. Lattice libraries and tooling are younger than their elliptic-curve counterparts, and verification logic is more involved. For records that may need to stand up years after they were written, we judged the trade worth it.
A stronger defence profile using ML-DSA-87 is in development. A hash-based signature (FIPS 205) is planned for long-horizon archives. ML-KEM (FIPS 203) protects the links that carry secrets; it is a key-establishment scheme, not a signature.
Why RFC 8785
Canonicalisation is the part of a signing scheme most people never think about, and the part where implementations most often disagree in silence.
JSON allows many byte representations of the same data. Two programs that both parse and re-emit JSON can produce different bytes, different digests and therefore different signatures for identical content. A signature over JSON is only portable if everyone agrees on one canonical byte form.
Against a home-grown canonicaliser. Sorting keys and stripping whitespace is the easy part. The hard cases are numbers such as 1.0 against 1 or exponent notation, Unicode escapes against raw UTF-8, byte-order marks, and key ordering for non-ASCII strings. RFC 8785, the JSON Canonicalization Scheme, answers each of them: keys sorted by UTF-16 code units, numbers serialised with the ECMAScript algorithm, minimal string escaping, no insignificant whitespace, UTF-8 output. Any conforming library produces the same bytes.
Against CBOR and MessagePack. Both are more compact and both have canonical forms. But JSON can be read by every auditor and regulator with nothing installed, and JSON canonicalisation has more independent implementations and conformance material. The size saving did not justify the loss of readability.
Against Protobuf. It needs schema distribution, its canonical form is less rigorously specified, and open-ended extension fields are awkward. Receipts should be self-describing.
Why SHA-256
SHA-256 is used for digests and for the chain link. The case is short: it is everywhere, hardware-accelerated on most processors, and has withstood long scrutiny. Length extension, the usual criticism, does not matter here because we use it as a content hash, not a message authentication code. BLAKE3 is faster but library support is uneven. SHA3-256 is a sound alternative and the format is designed so a different digest can be identified if one is ever needed.
Why a linked chain rather than a Merkle tree
Each receipt carries the digest of the one before it. Walk backwards from the newest receipt and you reconstruct the whole history.
- A single receipt can be checked with nothing but the receipt and the issuer's public keys.
- The chain shows order and makes a hidden insertion or deletion visible, because every receipt names a specific predecessor.
- It is easy to explain to an auditor. "This receipt follows that one" needs no description of roots and inclusion proofs.
Merkle trees, in the style of RFC 6962 transparency logs, are valuable for batch proofs and public logs. They complement a chain rather than replace it. Public anchoring of chain state is in development.
What could break
Every primitive eventually ages. The design question is how the format survives when one does. The receipt names the algorithms it was signed with, so an older receipt can still be checked with the rules it was made under, while newer receipts move to newer primitives.
- A signature scheme weakens: the hybrid design already means one leg is not enough to forge a receipt, and new algorithms can be introduced under a new version.
- SHA-256 weakens: new chains move to a different, identified digest; old chains stay as they are.
- Canonicalisation proves ambiguous: this is the most painful case, requiring a major version change. It is precisely why we chose an IETF specification with several independent implementations over anything of our own.
The point
Cryptographic formats fail more often from over-engineering than under-engineering. R-Series keeps the base deliberately plain: one canonical form, one digest, one chain structure, and a pair of signature algorithms from different families. Each was chosen as the most widely implemented and most reviewed option that meets the requirement.
If you think any of these choices is wrong, we want to hear it. You can see how the checks fit together on How proof works, and run a receipt through the verifier at dcslabs.ai/verify.