[AVAILABLE], all three runs below are real output from this session, commit
651497a, 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. 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,
confirmed in this session. 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, CLAIMS.md 2.13).
This is what caught the mismatch in tutorial 51 before this guide’s own fix, 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 an explicit Future Claim, not yet built (CLAIMS.md §4, 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.