Federation Root Ceremony HOWTO¶
This document covers only the ceremony that creates and signs a root pack. Installation, Seed Directory bootstrap, endorsements, TLS evidence, rotation, and running-Node diagnostics are covered by the broader Federation Bootstrap and Trust HOWTO.
This HOWTO describes the key-generation path for a federation-root ceremony:
each custodian generates a fresh ceremony key with
tools/federation-root-ceremony/federation_root_ceremony.py keygen. It does
not cover importing a node participant key, using a node data-dir, or deriving
an operator identity from a custodian key.
Related documents:
- Proposal 076:
doc/project/40-proposals/076-federation-identity-and-network-selector.md - Solution 041:
doc/project/60-solutions/041-federation-root/041-federation-root.md - Root charter:
doc/normative/50-constitutional-ops/ORBIPLEX-MAIN-ROOT-CHARTER.md - Tool reference:
node/tools/federation-root-ceremony/README.md
Roles¶
- Coordinator prepares the governance roster, unsigned root draft, manifest, ceremony transcript, and final assembly.
- Custodian generates one dedicated key on their own machine, verifies the frozen digest, signs the exact root draft, and returns only public material and a detached signature.
- Verifier independently runs manifest, assembly, and final pack checks before publication. This may be the coordinator plus at least one custodian.
The coordinator never receives private key material. Custodian keys may later attest or authorize a separate operator identity through an explicit binding, but they are not operator identities and must not be converted into one.
Pre-Ceremony Checklist¶
Agree on these values before any digest is frozen:
federation_id, for exampleorbiplex-main;pack_version;- root subject, usually an
org:did:key:...for production; - custody policy reference, for example
org-custody:orbiplex-main-root:v1; - threshold, for example
2-of-3; - charter or governance
policy_ref; - exact custodian roster and display names;
- ceremony id;
- publication target for the final
federation-root.v1pack; - redaction rules for the public transcript.
All commands below use repository-relative paths. Run them from the repository
root that contains node/.
1. Coordinator Creates the Workspace¶
mkdir -p ceremony/keys ceremony/public ceremony/signatures ceremony/out
ceremony/keys/ is shown only for local examples. In a real ceremony, each
custodian keeps their own private-key directory on their own machine; the
coordinator should not create or collect private keys.
2. Each Custodian Generates Their Key¶
Each custodian runs this locally, using their own label in file names:
node/tools/federation-root-ceremony/federation_root_ceremony.py keygen \
--out ceremony/keys/alice.ed25519.pem \
--public-json ceremony/public/alice.public.json \
--prompt-passphrase
The tool asks for and confirms a passphrase, writes an encrypted PEM private
key, and never accepts the passphrase as a CLI argument. For non-interactive
offline automation, use --passphrase-stdin or a short-lived,
permission-restricted --passphrase-file.
Expected local outputs:
ceremony/keys/alice.ed25519.pem— passphrase-encrypted private Ed25519 key, mode0600, keep secret and offline;ceremony/public/alice.public.json— publickey_publicrecord, safe to send to the coordinator.
The custodian sends only alice.public.json to the coordinator. Do not send the
PEM file, paste it into chat, attach it to tickets, or include it in the
transcript.
3. Coordinator Builds the Roster¶
The coordinator extracts each key_public from the public records and prepares
the unsigned root draft from the explicit governance roster:
node/tools/federation-root-ceremony/federation_root_ceremony.py \
root-draft-from-roster \
--confirm-roster \
--production-orbiplex-main \
--out ceremony/unsigned.federation-root.json \
--federation-id orbiplex-main \
--pack-version 1 \
--issued-at-now \
--policy-ref policy:[email protected]#appeals \
--org-id org:did:key:z6Mk... \
--custody-policy-ref org-custody:orbiplex-main-root:v1 \
--threshold-min-signers 2 \
--authorized-key-public z6MkAlice... \
--authorized-key-public z6MkBob... \
--authorized-key-public z6MkCarol...
--confirm-roster is required because authority comes from the governance
roster, not from whichever signatures later appear in a directory.
4. Coordinator Freezes the Digest¶
node/tools/federation-root-ceremony/federation_root_ceremony.py digest \
ceremony/unsigned.federation-root.json
Record the returned sha256:... digest in the ceremony transcript. This digest
is the value every custodian must independently compare before signing.
5. Coordinator Creates and Verifies the Manifest¶
node/tools/federation-root-ceremony/federation_root_ceremony.py manifest-init \
--production-orbiplex-main \
--out ceremony/manifest.json \
--ceremony-id orbiplex-main-root-001 \
--federation-id orbiplex-main \
--min-signers 2 \
--total-signers 3 \
--root ceremony/unsigned.federation-root.json
Then verify that the manifest still points to the frozen root draft:
node/tools/federation-root-ceremony/federation_root_ceremony.py manifest-verify \
--production-orbiplex-main \
--manifest ceremony/manifest.json
Send the following to each custodian:
ceremony/unsigned.federation-root.json;ceremony/manifest.json;- the expected
sha256:...digest through a second channel, if possible; - the exact ceremony id and roster summary.
Do not send private keys because the coordinator must not have them.
6. Each Custodian Verifies Before Signing¶
Each custodian verifies the digest locally:
node/tools/federation-root-ceremony/federation_root_ceremony.py digest \
ceremony/unsigned.federation-root.json
They compare the output with the digest recorded by the coordinator. If it does not match, stop the ceremony. Do not sign a "nearly identical" root draft.
Each custodian should also inspect at least:
federation_id;pack_version;attestation_roots[];custody_policies[];threshold/min_signers;- their own
key_publicin the authorized signer list; policy_ref;- bootstrap and Seed Directory entries.
7. Each Custodian Signs the Frozen Root¶
Each custodian signs with their local private key:
node/tools/federation-root-ceremony/federation_root_ceremony.py sign \
--root ceremony/unsigned.federation-root.json \
--key ceremony/keys/alice.ed25519.pem \
--prompt-passphrase \
--out ceremony/signatures/alice.sig.json
The passphrase unlocks only the local encrypted PEM. It is not written to the signature record, manifest, transcript, or root pack.
The custodian sends only alice.sig.json back to the coordinator. The detached
signature is public ceremony material; the private key remains local.
8. Coordinator Assembles the Root Pack¶
After collecting enough detached signatures, the coordinator assembles strictly:
node/tools/federation-root-ceremony/federation_root_ceremony.py assemble \
--production-orbiplex-main \
--strict \
--root ceremony/unsigned.federation-root.json \
--out ceremony/out/federation-root.v1.json \
ceremony/signatures
Strict assembly rejects invalid and unauthorized signatures. For production
orbiplex-main, the command also enforces the production profile and requires
--strict.
9. Everyone Verifies the Final Pack¶
The coordinator and at least one custodian verify the final artifact:
node/tools/federation-root-ceremony/federation_root_ceremony.py verify \
--production-orbiplex-main \
ceremony/out/federation-root.v1.json
The reported digest should match the frozen digest. If verification fails, do not publish the pack.
10. Publish and Archive¶
Publish:
ceremony/out/federation-root.v1.json;- the redacted ceremony transcript;
- public custodian records;
- manifest and final digest.
Archive privately:
- each custodian's encrypted private key, controlled only by that custodian;
- local machine notes needed for future rotation/recovery;
- any non-public incident notes.
Never archive private keys in the repository, public transcript, issue tracker, chat, or shared backup. A backup of the encrypted PEM is acceptable only when the backup and passphrase are managed as separate secrets for that custodian.
Failure Rules¶
Stop and restart from a new draft or new digest when:
- any custodian receives a different digest;
- the manifest no longer verifies;
- the roster is wrong or incomplete;
- the threshold is wrong;
- a private key may have been copied to the coordinator or a public channel;
- strict assembly rejects a signature;
- final verification reports a different digest or rejected signature.
Facts beat convenience: if the frozen bytes change, signatures over the old bytes are not reusable.