[AVAILABLE], all three runs below are real output, Node 24.
Goal
Sign and verify with Ed25519, then ML-DSA-65, then both together, and understand the one real cost difference between them: ML-DSA-65 signatures are randomized, Ed25519’s are not.Prerequisites
- Node ≥ 24 for native
node:cryptoML-DSA-65 support, this is a hard requirement, not a recommendation. - A gateway or default key already generated for local dev (see Quickstart).
Steps
1. Ed25519, the default
2. ML-DSA-65, opt-in
SetPRIMARY_SIGNATURE_PROVIDER=dilithium3 before CryptoBootstrap.create() runs (the
codebase calls this algorithm dilithium3 throughout, its formal name is ML-DSA-65, FIPS
204), and sign with the pq key instead of default:
3. Hybrid: both at once
CryptoBootstrap.createHybrid() builds both a primary and secondary CryptoProvider from
PRIMARY_SIGNATURE_PROVIDER and SECONDARY_SIGNATURE_PROVIDER, and produces one bundle
signed by both. The secondary key’s id is default-secondary (PARMANA_VERIFICATION_SECONDARY_KEY_ID,
see Cryptography), not pq, that key id is only used by the
standalone Dilithium3 tutorial above. This is the migration path: an artifact signed by both
algorithms can be verified by a consumer that only trusts one of them yet, while you migrate
the rest.
Verify
All three runs above printedVerified : true for the algorithm(s) they exercised. The
hybrid run’s two signature lengths (88 and 4,412 characters) match the standalone Ed25519 and
ML-DSA-65 runs exactly, the hybrid bundle isn’t a different computation, it’s both existing
ones run together.
Key/algorithm binding guard
Signing or verifying with the wrong key type for the configured provider fails closed with an error naming both the expected and actual key type, it does not silently dispatch based on the key’s own type (assertKeyType, used by both signature providers). See
Determinism and clocks for an unrelated but similarly
precise scoping note.
Troubleshoot
CryptoError: sign() expected a "X" key but received "Y". Your configuredPRIMARY_SIGNATURE_PROVIDERdoesn’t match the key you’re signing with,default.*.pemis Ed25519,pq.*.pemis ML-DSA-65, they are not interchangeable.- ML-DSA-65 throws about unsupported algorithm. Confirm
node --versionis 24 or higher, native support was added there. - No algorithm migration in production.
AuthorizationVerifiersupports exactly one configuredPRIMARY_SIGNATURE_PROVIDERat a time for envelope verification, re-keying while retaining the ability to verify previously-signed records is not yet built, see Roadmap.
Next
Deploy patterns
Where these keys live in a running deployment, and how key custody works
today.
Execution authorization
Where
algorithm shows up in the signed envelope itself.