Rütli — open-source threshold KMS (FROST-Ed25519) for Canton validators: request for SIG Security review & champion
- StakeCraftOP
Hi all, I’m Vasily from Stakecraft.
We’ve built Rütli, an open-source (Apache-2.0), self-hosted threshold KMS for Canton node keys: GitHub - Stakecraft/rutli: Open-source, self-hosted threshold KMS (FROST-ed25519) for Canton Network and Splice node authority keys. A Canton KMS driver plus a t-of-n cosigner cluster: no single machine ever holds the key. · GitHub
- It plugs into an unmodified participant through the KMS Driver SPI, with no fork.
- The namespace and signing keys are FROST-ed25519 shares across a t-of-n cosigner cluster, so the whole key never exists on any machine.
- Encryption keys (ECIES P-256) are hosted in the same cluster, and the participant holds no private keys at all.
- It has multi-party approval, signing policies, proactive share refresh and recovery.
- An existing validator can be migrated in without changing its namespace or party IDs.
Status:
- a Splice 0.9.0 / Canton 3.6.1 DevNet validator has run on it since Oct 7;
- an internal security review is done;
- the external audit is the next step.
We’re preparing a Dev Fund proposal under RFP 26 (Key Management and Signing Controls). It funds:
- an independent audit with a public report;
- a Splice TestNet run and the 1.0 release;
- onboarding of independent validator operators.
We’re looking for a Champion from the Tech & Ops Committee, and we’d value any feedback from this SIG on scope. Happy to do a 20-minute walkthrough or a live demo. The draft proposal is ready to share.
- StakeCraft
Following up with more detail: the problem we’re solving, how Rütli compares with what’s available today, what we know it doesn’t do yet, and what the grant would cover.
The problem
A participant’s namespace key is its identity and cannot be rotated: leak it and the node is someone else’s; lose it and every party and Canton Coin balance under it is gone. On a default validator, the namespace, signing and encryption keys all sit unencrypted in the participant database, and in every
pg_dumpof it. The Splice docs also note that an existing participant cannot simply be switched to a KMS.How Rütli compares with the options available today
Whole key exists? Open source Self-hosted Encryption key Move an existing node in (same namespace) Default (participant DB) Yes, DB + backups n/a Yes In the DB n/a Cloud KMS (AWS / GCP) Inside the provider No No In the KMS No, new validator Blockdaemon Builder Vault No (MPC) No Yes Threshold No (external-keys mode) MPCH Stronghold-KMS Not public No Not public Not public Not public Rütli No (FROST) Yes, Apache-2.0 Yes Whole, sealed on each cosigner Yes, rehearsed on DevNet A few things the table can’t show:
- Signing controls. Rütli adds client-signed intents that every cosigner checks, M-of-N human approvals, rate limits and a hash-chained audit log on top of the key. Cloud KMS has IAM and logs; we found nothing comparable documented for Builder Vault’s Canton driver.
- Who you trust. Rütli’s t-of-n cosigners can sit with different clouds or operators, so no single party holds the key.
- Availability. Cloud KMS is Canton Enterprise Edition only. Builder Vault is commercial and needs Canton 3.5+. Rütli is free and needs Canton 3.5+; it is tested on 3.6.1 with Splice 0.9.0.
Sources: Canton KMS docs, Splice validator security, Blockdaemon Builder Vault Canton KMS, MPCH ecosystem page. Corrections are very welcome, especially from the vendors.
Wallet and custody platforms (Fireblocks, Dfns, BitGo and others) protect the keys of the parties and assets they custody. Rütli protects the participant’s own keys: namespace, protocol signing and encryption. The two are complementary, and so is the Decentralization Manager (multi-party control at the Daml and topology level).
Under the hood
- FROST implementation. The Zcash Foundation’s FROST-Ed25519 (RFC 9591), with distributed key generation, so the key is never assembled.
- Nonce safety. Each nonce is burned to disk before its signature share is released, so a crash or restart can never reuse it.
- Share refresh without changing the public key, and share repair from the surviving nodes.
- Defence in depth. mTLS with per-peer names and pinning; per-node sealing (AES-256-GCM); every cosigner checks a client-signed intent before it releases a signature share.
- Evidence.
- Current release candidate:
v0.4.0-rc.6. - The DevNet cosigners run in three separate locations.
- Upstream
KmsDriverTestconformance passes. - The internal review is published with every finding and its fix (internal review), along with the threat model.
- Current release candidate:
Limits, stated up front
- Not externally audited yet. Please don’t put production authority keys behind it until that’s done; the audit is the first thing the grant funds.
- The encryption key is not threshold. It is replicated whole, sealed, on each cosigner. Builder Vault does threshold ECIES here, and we’re behind on this point.
- No clear signing. The KMS Driver SPI hands the driver a 34-byte digest, so Rütli applies policy per key and per operation, not per transaction content.
- One Kubernetes cluster is one trust domain. For MainNet, cosigners belong in separate clusters, accounts or organisations.
The grant, in more detail
- The independent audit will be done by Cure53, with a public report and a retest of the fixes.
- Rütli 1.0 follows a TestNet run of at least four weeks. It ships with operator documentation and an RFP 26 reference architecture for self-operated, hosted and institutional deployments.
- Onboarding is paid per independent operator, and only once that operator has run Rütli on MainNet for 30 days or more.
One more ask
Node Deployment & Operations: if you’d consider a DevNet or TestNet pilot, please reply here or DM us. Feedback, or a short letter of support, helps the proposal.
About us: Stakecraft has run validators since 2018; on Canton we run DevNet, TestNet and MainNet validators and provide node-as-a-service.