Skip to content

canton: bootstrap guardianGovernance as a 2-of-3 external decentralized-namespace party#33

Merged
bengtlofgren merged 7 commits into
integration/cantonfrom
canton-guardiangovernance-bootstrap
Jul 8, 2026
Merged

canton: bootstrap guardianGovernance as a 2-of-3 external decentralized-namespace party#33
bengtlofgren merged 7 commits into
integration/cantonfrom
canton-guardiangovernance-bootstrap

Conversation

@bengtlofgren

Copy link
Copy Markdown

Summary

Stands up guardianGovernance — the co-signatory/key-maintainer party on CoreState introduced 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. PartyToKeyMapping is deliberately not used (deprecated in Canton 3.4); the owner keys go on PartyToParticipant.partySigningKeys with the same threshold.

The genesis co-sign ceremony is exercised over the JSON Ledger API's interactive-submission endpoints: a create CoreState co-signed by an external operator party and guardianGovernance. 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-anchored CoreState).

Changes

  • canton/devnet/guardian_governance.canton (new): console topology script — three externally self-signed root NamespaceDelegations, a 2-of-3 DecentralizedNamespaceDefinition, party allocation + Confirmation hosting with threshold partySigningKeys, 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-in crypto (no dependencies) — the local stand-in for guardian custody.
  • canton/devnet/setup_guardian_governance.sh (new): bash wrapper mirroring bootstrap.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 scratch dpm 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.sh green end to end against a fresh dpm 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

  • CN global-synchronizer submission itself (designed and parameterized via GG_SYNCHRONIZER_ALIAS / GG_CANTON_CONSOLE_CONFIG, not exercised — no CN localnet available).
  • Topology-level owner rotation (the namespace's own threshold governs it once created, but rotation is untested) and multi-participant Confirmation hosting.

This resolves the buildable core of README §11 Open Question 2 (guardianGovernance threshold party); the question is updated in place to list exactly what remains. Based directly on integration/canton; independent of the guardianObserver work in #32.

…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
bengtlofgren force-pushed the canton-guardiangovernance-bootstrap branch from fb63045 to 5be0795 Compare July 8, 2026 11:09

@connorstake connorstake left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks fine. will do some additional checks later.

@bengtlofgren
bengtlofgren merged commit 554e67d into integration/canton Jul 8, 2026
33 of 36 checks passed
@bengtlofgren
bengtlofgren deleted the canton-guardiangovernance-bootstrap branch July 8, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants