canton: bootstrap guardianGovernance as a 2-of-3 external decentralized-namespace party#33
Merged
bengtlofgren merged 7 commits intoJul 8, 2026
Conversation
…espace party Adds the console topology script that creates guardianGovernance as a 2-of-3 decentralized-namespace external party (DSO-style): three owner namespaces, a DecentralizedNamespaceDefinition with a 2-of-3 threshold, and the party hosted at Confirmation with the same threshold on its PartyToParticipant.partySigningKeys (PartyToKeyMapping is deprecated as of Canton 3.4). Every topology transaction is built unsigned and signed externally (Signature.fromExternalSigning) rather than via the participant's own vault, so an owner's private key never has to touch a participant host. guardian_key_tool.js is a small Ed25519 keygen/signing helper (Node's built-in crypto only, no dependencies) standing in for real guardian custody (HSM/KMS/offline signer) locally; production only needs to repoint GG_OWNER_SIGN_CMD_* at the real signer.
setup_guardian_governance.sh mirrors bootstrap.sh's shape (wait for the Ledger API, then run the scripted step): it self-generates three local test owner keys by default (GG_GENERATE_TEST_KEYS=1), or accepts externally-supplied public keys and signers for production (GG_GENERATE_TEST_KEYS=0 + GG_OWNER_PUBKEY_*/GG_OWNER_SIGN_CMD_*), and runs guardian_governance.canton. genesis_guardian_governance.sh exercises the resulting party over the JSON Ledger API's interactive-submission endpoints: prepares a `create CoreState` command co-signed by an external operator party and guardianGovernance, has owners sign the prepared hash, and executes. Three modes cover the anti-forgery property: 2-of-3 succeeds, 1-of-3 is rejected (threshold), and no guardianGovernance signature at all is rejected (a compromised operator cannot forge the anchor).
test_guardian_governance.sh runs the full local test strategy against a scratch dpm sandbox: builds and uploads the real wormhole-core DAR, bootstraps guardianGovernance (2-of-3) and a single-key external Operator, then asserts the decentralized namespace/threshold/hosting shape and all three genesis-signing outcomes (2-of-3 succeeds, 1-of-3 and no-signature are rejected). Verified green end to end.
Adds a canton/README.md §9 subsection covering the new devnet scripts (what each one does, the sandbox-vs-CN fidelity gap, and the single-participant-hosting limitation) and updates Open Question 2 to reflect what's now built (2-of-3 genesis co-signing, confirmed locally) versus what remains (CN-synchronizer submission, owner rotation, multi-participant hosting).
CI (cspell) flagged 'keypair' as unknown in README.md:745. Also commits an already-pending local fix: test_guardian_governance.sh's header referenced an external design-plan doc readers don't have access to -- the bullet list right there already documents what it asserts, so the doc reference was just noise.
bengtlofgren
force-pushed
the
canton-guardiangovernance-bootstrap
branch
from
July 8, 2026 11:09
fb63045 to
5be0795
Compare
connorstake
approved these changes
Jul 8, 2026
connorstake
left a comment
There was a problem hiding this comment.
looks fine. will do some additional checks later.
kcsongor
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stands up
guardianGovernance— the co-signatory/key-maintainer party onCoreStateintroduced in #29 — as a real external, decentralized-namespace party: a namespace owned by three guardian keys with a 2-of-3 threshold, the same pattern the Canton Network DSO party uses.Every topology transaction is built unsigned (
topology.transactions.generate), signed by each owner's external signer, and only then submitted (topology.transactions.load) — the participant never holds an owner's private key, matching real guardian custody (HSM/KMS/offline signer). The custody boundary is a CLI contract (GG_OWNER_SIGN_CMD_*: hex hash in, hex signature out); locally a dependency-free Node Ed25519 helper fills it, production repoints it at the real signer without touching the topology code.PartyToKeyMappingis deliberately not used (deprecated in Canton 3.4); the owner keys go onPartyToParticipant.partySigningKeyswith the same threshold.The genesis co-sign ceremony is exercised over the JSON Ledger API's interactive-submission endpoints: a
create CoreStateco-signed by an externaloperatorparty andguardianGovernance. All three outcomes are asserted — 2-of-3 succeeds, 1-of-3 is rejected (threshold), and operator-alone is rejected (the anti-forgery crux: a compromised operator cannot fabricate the guardian-anchoredCoreState).Changes
canton/devnet/guardian_governance.canton(new): console topology script — three externally self-signed rootNamespaceDelegations, a 2-of-3DecentralizedNamespaceDefinition, party allocation + Confirmation hosting with thresholdpartySigningKeys, and an artifacts JSON handoff (party id, namespace, fingerprints, threshold).canton/devnet/guardian_key_tool.js(new): Ed25519 keygen/signing helper using only Node's built-incrypto(no dependencies) — the local stand-in for guardian custody.canton/devnet/setup_guardian_governance.sh(new): bash wrapper mirroringbootstrap.sh's shape; self-generates three test owner keys by default (GG_GENERATE_TEST_KEYS=1) or accepts externally-supplied public keys and signers for production.canton/devnet/genesis_guardian_governance.sh(new): genesis-signing demo over interactive submission with three modes —happy(2-of-3),threshold(1-of-3, rejected),forge(no guardianGovernance signature, rejected).canton/devnet/test_guardian_governance.sh(new): end-to-end harness against a scratchdpm sandbox— builds/uploads the real wormhole-core DAR, bootstraps guardianGovernance (2-of-3) and a single-key external Operator, asserts the topology shape and all three genesis outcomes.canton/README.md: new §9 subsection documenting the scripts, the sandbox-vs-CN fidelity gap (the decentralized namespace is guardian-owned, so CN "registration" is just submitting the same topology transactions to the global synchronizer), and the single-participant-hosting limitation; Open Question 2 updated to built-vs-remaining.Testing
canton/devnet/test_guardian_governance.shgreen end to end against a freshdpm sandbox: 3-owner/threshold-2 namespace shape,guardianGovernance::<dn>hosted at Confirmation, genesis 2-of-3 succeeds, 1-of-3 and operator-only rejected.Out of scope / follow-ups
GG_SYNCHRONIZER_ALIAS/GG_CANTON_CONSOLE_CONFIG, not exercised — no CN localnet available).This resolves the buildable core of README §11 Open Question 2 (
guardianGovernancethreshold party); the question is updated in place to list exactly what remains. Based directly onintegration/canton; independent of theguardianObserverwork in #32.