Trellis
OverviewPrimerLabReadiness checkDocs
§L · lab · throwaway keys

The lab

Make keys, sign, verify and try to cheat the verifier with classical Ed25519 and the NIST post-quantum signatures, then run X25519 and ML-KEM-768 key exchange side by side. All of it executes in this tab. Nothing is saved, and nothing you type is sent anywhere.

§01

Signature scheme

Choose one. Everything marked “runs here” executes in this tab with real implementations.

FIPS 204 (Dilithium)pubkey 1,312 B · 41.0× Ed25519sig 2,420 B · 37.8× Ed25519min transfer 3,819 B · 3.1× the cap

The smallest ML-DSA set and the usual first candidate for replacing Ed25519. Even here the signature is about 38 times larger.

§02

Keypair

Generated in this tab's memory. Gone when you reload.

§03

Sign a message

Any UTF-8 text. The scheme signs its exact bytes.

Generate a keypair first.
§M

Measurements

Published sizes against Ed25519, timings from this device.

Scheme
ML-DSA-44
Family
lattice
Public key
1,312 B · 41.0×
Signature
2,420 B · 37.8×
Smallest transfer
3,819 B · 3.1× cap
Keygen (measured)
—
Sign (measured)
—
Verify (measured)
—

A Solana transaction must fit in 1,232 bytes. The smallest transfer row assumes one signer and carries the full public key, since a 32-byte address cannot stand in for a 1,312-byte verification key.

§04 · devnet

A real Ed25519 transaction, end to end

Every Solana signer is Ed25519 today. Make a throwaway Devnet keypair, sign a transfer with it and have a real Devnet validator check the signature, with no SOL needed. Then see exactly how many bytes a post-quantum signer would add to that same transfer.

RPC: devnet

Devnet only, and Devnet SOL is worthless. Verifying needs no SOL: the validator checks the signature in a simulation and nothing is sent. To actually send, the key gets 0.002 SOL from this site's Devnet faucet. The key never leaves this tab and is lost on reload; don't send anything real to it.

§05 · key exchange

X25519 next to ML-KEM-768

A signature says who sent something. Key exchange decides who can read it, and that part has a deadline that has already started: traffic captured today can be opened later by anyone who breaks X25519. That is why key exchange usually moves first. Both run below with fresh in-memory keys.

X25519classicalRFC 7748

Diffie–Hellman on Curve25519: each side mixes its secret with the other's public key and both land on the same value. 32 bytes each way, and exactly the problem Shor's algorithm solves.

Creates a keypair for Alice and one for Bob, then each side derives the shared secret on its own.
ML-KEM-768post-quantumNIST FIPS 203

Module-lattice key encapsulation, the standardised form of Kyber. One side publishes a key, the other wraps a fresh secret to it. Keys and ciphertexts run past a kilobyte; the speed is in the same range as X25519.

Creates a keypair, wraps a fresh 32-byte secret to its public key, then unwraps it with the secret key.
HybridHKDF-SHA256(ML-KEM ss ‖ X25519 ss)
Run both exchanges and the two secrets are merged into one 32-byte key here.

This is the reasoning behind X25519MLKEM768 in TLS 1.3: an attacker has to break both halves to recover the key. TLS puts the ML-KEM secret first as well, but feeds the pair into its own key schedule; the HKDF step here only illustrates the idea.

Sizes and timings
Published sizes for X25519 and ML-KEM-768 with timings measured in this browser.
SchemePublic keyCiphertext / shareShared secretKeygenEncaps / deriveDecaps
X2551932 B32 B32 B———
ML-KEM-7681,184 B
37.0× X25519
1,088 B
34.0× X25519
32 B———

Sizes come from the standards. Timings are one run each, taken with performance.now() on this device, so expect them to move between runs. X25519 has no separate encapsulation: the “share” is simply the other party's public key.