An independent interpretation of the released Udanax Gold source · not Project Xanadu documentation · about this documentation
Federation Activation
How independent Xudanu servers connect, trust each other, replicate content, reach consensus, recover from failures, and scale — explained with diagrams for operators and developers. Updated for the hardened protocol: signed certificates, view change, key epochs, and 4-node BFT deployment.
A Xudanu federation is a set of independent server peers, each a full node (it serves the web UI, the client WebSocket /xudanu, and the peer WebSocket /federation). Peers connect to each other, replicate content (chunks verified by BLAKE3), maintain a shared membership via a web-of-trust of Ed25519 endorsements, and make cluster-wide decisions with PBFT consensus. Users connect to any peer's web UI; the peers federate behind the scenes.
Four full-node peers (the BFT minimum, 3f+1) on a private network. Users hit any peer's web UI; peers replicate content and run PBFT governance over /federation.
The activation layer (FR-3) — now implemented
Five pieces of runtime wiring turn the finished components into a live federation. Each was previously missing; all five are now implemented and merged.
1
Outbound dialer — On startup, a task per --peer dials ws://peer/federation, performs the mutual handshake as the client side, and holds the encrypted channel in a PeerPool. Reconnects with exponential backoff (1s→30s cap).
2
Startup bootstrap — membership_bootstrap_init() creates a self-endorsed membership entry so the node can act as a founder or be validated by others.
3
Peer-key registration — --trusted-peer-key <hex> registers trusted peer keys, fixing the is_peer_known reject-all trap. Without it, every connection is refused when federation is enabled.
4
Periodic sync / heartbeat — Every 30s sends Heartbeat; every 60s sends SyncPull, MembershipSyncPush, StateSyncPush, EndorsementSyncPush. Turns "connected" into "converged."
5
PBFT broadcast — GovernancePropose broadcasts GovernancePrePrepare to all peers via the governance_tx broadcast channel; peers relay votes and seal.
Federation is off by default. A standalone server (no --peer flag) is completely unaffected — the dialer never spawns and the broadcast channel has no subscribers. You opt in at startup with CLI flags. A standalone server and a federation cluster coexist happily on the same machine: only the port and data directory must differ. Prefer --pin-members <file> over piecemeal --trusted-peer-key flags — it makes admission strict (no trust-on-first-use) and seeds the unified validator set.
PBFT: Making decisions together
Xudanu uses Practical Byzantine Fault Tolerance (PBFT) for cluster-wide decisions — things like admitting a new member, expelling a bad actor, or recording a royalty payment. PBFT is a consensus protocol that lets a group of nodes agree on a decision even if some nodes are malicious or offline.
What PBFT is — in plain language
Imagine a committee of N people who must vote on a proposal. PBFT guarantees that:
The group can reach a decision as long as at least 2/3 of the members are honest and reachable (formally: tolerates f faulty nodes out of 3f+1 total).
No two honest members will ever disagree on the outcome.
Once a decision is sealed, it cannot be undone.
The hardened protocol (2026 revisions). PBFT now carries production-grade safeguards, each backed by an in-process test mesh and a TLA+/TLC model check (337k states, zero safety violations):
Signed vote certificates — every vote is an Ed25519 signature over the proposal digest (BLAKE3 of sequence+transactions). A sealed batch embeds 2f+1 prepare and 2f+1 commit certificates; recipients verify before executing. A byzantine leader cannot forge outcomes, and clients can no longer inject votes.
View change — if the leader stalls (10s round timeout), replicas automatically emit signed VIEW-CHANGE messages carrying their highest prepared certificate; the next leader assembles a NEW-VIEW and governance resumes. No operator action needed.
Key epochs (FR-75) — validator keys are valid for a range of sealed sequences, never wall-clock time. Expired keys stop counting votes; a shrunk voting pool halts governance safely rather than forking. Rotation requires the operator's offline recovery key or a quorum of other members — a stolen server key alone can never take over an identity.
State transfer — a node that missed sealed batches requests the certificate-verified log tail from any peer and catches up in order.
Strict peer admission — with genesis pinning (--pin-members), only pinned keys may connect: no trust-on-first-use.
PBFT three-phase flow with 4 nodes (1 offline). The cluster tolerates f faulty nodes out of 3f+1. PrePrepare broadcasts from the leader; Prepare and Commit votes flow between all replicas; sealing requires 2f+1 Commit votes.
The quorum rule, simply
For a cluster of N nodes, PBFT tolerates up to f faulty/offline nodes, where f = (N - 1) / 3. To make a decision, you need at least 2f+1 nodes to vote yes:
Cluster size (N)
Mode
Fault tolerance (f)
Votes needed (2f+1)
Can lose
1
single-server
—
1 (local)
— local mode
2–3
governance refused
—
—
content sync works; no BFT decisionsrefused
4
BFT
1
3
1 byzantine node, 0 crashes zero margin
5
BFT
1
3
1 byzantine + 1 crash recommended
7
BFT
2
5
2 nodes OK
10
BFT
3
7
3 nodes OK
Practical guidance: 4 nodes is the BFT minimum (3f+1) — with one unrenewed key expiry it drops to quorum 3-of-3, zero fault tolerance, so rotate keys promptly (the governance_lifecycle.margin field in /health is your canary). 5 nodes is the recommended default: tolerates one byzantine node and one additional crash. Clusters of 2–3 replicate content fine but the governance plane refuses to open rounds — there is no majority-safe quorum.
Broadcasting & synchronization
Once peers are connected, they need to stay in sync. Xudanu uses two complementary mechanisms: periodic sync (every 60 seconds) and event-driven broadcast (immediate, for governance).
How the periodic sync loop works
Every 60 seconds, each outbound connection sends a batch of sync frames to the peer on the other end. The peer processes them and replies with its own state. This ensures both sides converge even if they were temporarily disconnected.
Each outbound connection runs a select! loop that multiplexes inbound frames, broadcast frames (from PeerPool), heartbeats, and periodic sync. All data crosses an encrypted AEAD channel. CRDTs ensure convergence regardless of frame duplication or arrival order.
Why duplicates don't matter: Xudanu uses CRDTs (Conflict-free Replicated Data Types) for all federated state — membership (OR-Set), reconcile states (LWW Register + OR-Set alternatives), and content (BLAKE3-addressed). CRDTs are mathematically guaranteed to converge to the same state regardless of delivery order or duplication. A frame received 5 times has exactly the same effect as received once.
How Xudanu shares content
Xudanu's document model is content-addressed: every piece of content (a chunk of text, an edition, a blob) is identified by its BLAKE3 hash, not by a server-assigned ID. This makes replication straightforward — you can verify any piece of data by recomputing its hash.
The chunk replication flow
When a user edits a document on Peer A, the change flows through the system and reaches all other peers within one sync interval (60 seconds):
A user edits on Peer A. The edition is stored with a BLAKE3 fingerprint. During the next sync cycle, the SyncPush frame carries the edition payload across the encrypted channel. Peer B and C recompute the hash — if it matches, they import; if not, the data is rejected as tampered or corrupt.
What gets replicated
Data type
How it's verified
Deduplication
Works (documents)
BLAKE3 hash of all edition entries
Existing fingerprints checked before import
Editions (versions)
Content-addressed within the work
Per-work fingerprint set
Blobs (images, files)
BLAKE3 hash of raw bytes
Hash-addressed on disk
Membership
Ed25519 signature on each endorsement
OR-Set tags (idempotent merge)
Endorsements
Signed by the endorsing member's key
OR-Set (idempotent)
Reconcile state
LWW timestamp + content hash
Highest timestamp wins
Only published works are exported. Private drafts stay on the originating server. The federation_export_works function filters to read_club == public_club, ensuring private content never leaves its origin.
How a donated machine joins
Identity is the server's existing Ed25519 keypair (data/server.key); trust is a web-of-trust of signed endorsements (phase-19a, already built). A donated host proves who it is by its key, and gets vouched for by an existing member.
A donated host bootstraps, dials a founder, completes the mutual handshake, has its key registered, then joins with an endorsement — membership converges cluster-wide.
No central authority. Trust is peer-signed and auditable; a compromised donor can be ejected via signed retraction or a PBFT Expel transaction. Each donor bears their own machine, so cost stays distributed — there is no expensive central fleet.
Joining is directory-level; validating is consensus-level (FR-75). The handshake above admits the server to the membership directory — content replication and cross-server transclusion work immediately. But voting rights in PBFT require a sealed Admit transaction: the join-admitted key does not verify votes until governance admits it. For cold-starting a cluster, prefer genesis pinning (--pin-members): every node boots with the same validator set and governance is unified from the first round.
When things go wrong: failure & recovery
Federations run over real networks, and real networks fail. Xudanu is designed to self-heal — when a failed or partitioned node comes back, it catches up automatically. Here are the common scenarios and how the system handles them.
A peer's network goes down (VPN drops, host reboots, ISP hiccup). The other peers continue operating. When the partitioned node returns, it reconnects and syncs.
Network partition and automatic recovery. CRDTs guarantee convergence without conflict resolution — C simply pulls the state it missed.
Scenario 2: Node crash (data dir survives)
If a peer process crashes but its /data directory survives (common case — process killed, OOM, panic), the node restarts, restores from its chunk store, reconnects, and syncs. Content catches up via CRDT sync; governance catches up via state transfer — the restarted node requests the sealed-batch log tail from a peer and ingests it certificate-verified, in order. Data written before the crash is durable (checkpointed every 5 seconds). No manual intervention needed.
Scenario 3: Byzantine (malicious) node
A compromised node tries to inject tampered data or forge endorsements. The system rejects these automatically:
Tampered blobs/chunks: BLAKE3 hash mismatch on import → rejected.
False membership claims:MembershipJoinRequest with invalid proofs → rejected.
Cluster-wide removal: A PBFT Expel transaction removes the bad node's membership from all peers.
Forged governance: votes and sealed batches without 2f+1 valid Ed25519 signatures are rejected on arrival; a byzantine leader can stall a round (recovered by view change) but cannot forge an outcome.
Stolen validator key: votes stop at key-epoch expiry; the thief cannot rotate to a new key (rotation needs the operator's offline recovery key or a member quorum) and cannot survive the expiry.
The current leader crashes or stalls mid-round. After a 10-second timeout, the remaining replicas emit signed VIEW-CHANGE messages for the next view; when a quorum accumulates, the incoming leader broadcasts a signed NEW-VIEW and re-proposes. Governance continues in the new view within seconds. One caveat: a request whose round never reached prepare-quorum is discarded — the client retries with the new leader (values that did reach prepare-quorum carry over and cannot be changed).
Scenario 4: Split-brain (two partitions, both active)
If the network splits into two groups (e.g., A-B and C-D), both partitions continue accepting edits independently. PBFT cannot reach quorum in either partition (each has fewer than 2f+1 votes for a 4-node cluster with f=1). When the partition heals, CRDTs merge the divergent states — both sets of edits are preserved as alternatives, and the reconcile store's LWW (Last-Writer-Wins) registers pick the most recent timestamp.
Split-brain caveat: PBFT governance decisions cannot complete during a partition (by design — this prevents conflicting decisions). Content edits are NOT blocked — they are CRDT-merged on heal. If both partitions edited the same document, both versions are preserved as reconcile alternatives, and the user can choose which to keep.
Scenario 5: Slow or degraded node
A peer that is reachable but very slow (high latency, low bandwidth, overloaded CPU). Heartbeats detect this (30s timeout). The outbound dialer doesn't block on slow peers — each connection has its own task. Other peers continue syncing among themselves at full speed. The slow node catches up when it can.
Scaling: what to expect as you grow
Adding more machines increases resilience but also increases network traffic, because each peer connects to every other peer (full mesh). Here's what to expect at different scales.
Connection mesh: O(n²)
Each peer maintains an outbound connection to every other peer. With N peers, there are N × (N-1) directional connections (or N(N-1)/2 bidirectional pairs):
Peers (N)
Bidirectional pairs
Connections per peer
Assessment
3
3
2
trivial
5
10
4
easy
10
45
9
fine
20
190
19
moderate
50
1,225
49
heavy
100
4,950
99
very heavy
Future optimization: For 50+ nodes, a gossip protocol or relay-based topology (instead of full mesh) would dramatically reduce connections. This is on the roadmap but not needed for v1 — most federations will have 3–10 peers.
Bandwidth per sync cycle
Each 60-second sync cycle sends all published works, membership, states, and endorsements to each peer. For a fresh/small federation this is kilobytes. As content grows, bandwidth scales with total published content × number of peers.
Federation size
Content volume
Per-peer bandwidth (60s)
Notes
3 peers
~10 MB
~10 MB
Trivial on any connection
5 peers
~50 MB
~50 MB
Fine on broadband
10 peers
~200 MB
~200 MB
~3 Mbps sustained — fine for VPS
10 peers
~1 GB
~1 GB
~14 Mbps — needs a good link
20 peers
~1 GB
~1 GB × 19 peers
~270 Mbps aggregate — datacenter
Incremental sync is the next optimization. Currently SyncPull sends up to MAX_SYNC_ENTRIES (1000) entries per cycle. Deduplication by BLAKE3 fingerprint prevents re-import, but the full payload still transmits. A future "known fingerprints" filter (the field exists in the struct but isn't populated yet) will skip data the peer already has.
Memory and CPU per peer
RAM: Each peer holds its own chunk store, the membership OR-Set, reconcile states, and CRDT buffers. A peer with 100 works uses ~50–100 MB RAM. Federation adds ~2–5 MB per connected peer (channel buffers + sync state).
CPU: Dominated by BLAKE3 hashing (on import) and Ed25519 signature verification (on handshakes/endorsements). Both are fast (< 1ms per operation). A 4-core VPS handles 10+ peers comfortably.
Disk: Content-addressed storage grows with total published works. BLAKE3 deduplication means the same content stored on multiple peers takes space only once per peer.
Recommended configurations
Use case
Peers
Min specs per peer
Network
Personal / testing
2–3
2 vCPU, 2 GB RAM, 10 GB disk
Any broadband
Small team
3–5
2 vCPU, 4 GB RAM, 20 GB disk
10 Mbps uplink
Organization
5–10
4 vCPU, 8 GB RAM, 50 GB disk
50 Mbps uplink
Large federation
10–20
4 vCPU, 16 GB RAM, 100 GB disk
100 Mbps+ datacenter
Research / max scale
20–50
8 vCPU, 32 GB RAM, 200 GB SSD
Gigabit datacenter
Problem checklist by scale
A diagnostic reference for operators. If something seems wrong, check these items.
2–3 nodes (personal / small team)
2–3 nodes cannot do BFT governance — the governance plane refuses to open rounds below 4 validators (there is no majority-safe quorum). Content replication, cross-server transclusion, and membership sync all work fine. Add a 4th node (or 5th, recommended) to activate PBFT.
CommonKeys not cross-registered: Peers must know each other's verifying keys — via --pin-members (preferred, strict) or --trusted-peer-key flags. If you see "peer not in trusted peers list," check that the hex keys match.
CommonWrong peer address format:--peer expects ws://host:port/federation or host:port. Check that the port matches the listening port of the remote peer.
CommonFirewall blocks /federation: The WebSocket upgrade on /federation must be reachable. If using Docker, ensure the port is published.
TipFirst diagnostic stop:./scripts/federation.sh diagnose — checks node health, cross-node validator-set consistency, margin alerts, peer-trust rejections, view-change activity, and disk headroom in one command.
WatchBootstrap not called: If membership is empty, check that --peer was specified (which enables federation and triggers membership_bootstrap_init).
5–10 nodes (organization)
WatchKey management: Maintain the pinned member set as a versioned file; rotations need the offline recovery keys. Watch /health → governance_lifecycle — margin: 0 means one loss from halting; negative means governance is already halted (safely — nothing forks).
WatchSync bandwidth: With 10 peers and significant content, each sync cycle sends data to 9 peers. Monitor outbound bandwidth.
WatchStale membership: If a peer leaves without sending MembershipLeave, its entry persists until explicitly removed. Use the admin API to clean up.
TipUse 4+ nodes for PBFT: With 4 nodes (f=1), you can lose 1 node and still reach quorum. With 3 nodes (f=0), losing any node blocks governance.
PlanStartup thundering herd: If all peers restart simultaneously, they all try to dial each other at once. The exponential backoff helps, but consider staggered restarts.
WatchEndorsement chain growth: The membership OR-Set grows with each endorsement. At 20 nodes with cross-endorsements, the set can be several hundred KB. Not a problem, but watch for sync latency.
ShippedLeader failover is automatic: a stalled leader triggers view change after a 10s timeout — signed VIEW-CHANGE quorum, NEW-VIEW, re-proposal. At 20 nodes, vote traffic is O(n²) per round, which is the real cost of scale.
20–50+ nodes (research / max scale)
ChallengeFull mesh doesn't scale: 50 nodes = 2,450 connections. Each peer dials 49 others. This works but is wasteful. A gossip/relay topology is needed for sustainable scaling.
ChallengeSync payload size: With 50 peers and GBs of content, the 60-second full sync is impractical. Incremental sync (using known_fingerprints) is essential.
ChallengePBFT message complexity: O(n²) vote messages per round. At 50 nodes, each round generates ~2,500 vote messages. Practical but slow.
ChallengeMembership convergence latency: With 50 peers, a new endorsement takes several sync cycles to reach all nodes. Plan for eventual consistency, not immediate.
WatchNAT / reachability: Donated hosts behind NAT cannot be dialed. Each peer must be directly reachable (public IP or port-forward). A relay/TURN server is a future feature.
Bottom line: For most use cases (4–10 peers on VPS or donated machines — 4 minimum for governance, 5 recommended), Xudanu's federation works well out of the box. The full-mesh + periodic sync model is simple, robust, and self-healing; PBFT safety is model-checked and the validator lifecycle surfaces in /health. Scaling beyond 20 nodes is possible but will benefit from the incremental sync and gossip relay optimizations on the roadmap.
Testing it: 4-node test bed
The docker-compose.yml runs four full-node peers on a private bridge network (host ports 8081–8084), each with --peer wiring to the other three — the BFT minimum. Admin passphrases go in via XUDANU_ADMIN_PASSPHRASE from an uncommitted .env file (never on the CLI — arguments are visible in ps and docker inspect).
docker compose up --build
# then browse:
open http://localhost:8081 # peer A (Alice)
open http://localhost:8082 # peer B (Bob)
open http://localhost:8083 # peer C (Carol)
open http://localhost:8084 # peer D (Dave)
curl http://localhost:8081/health | grep -A8 governance_lifecycle
For local testing without Docker, one command runs the whole lifecycle — it inits the nodes, extracts their keys, writes pinned-members.json (strict admission + unified validators), and starts all peers with per-node logs:
./scripts/federation.sh start 4 /tmp/fed # also: status | diagnose | logs N | stop
The full operations guide — recovery playbooks (crash, leader failure, expired keys, state-transfer gaps), log locations, and the diagnostic reference — lives in FEDERATION-OPS.md in the source repository.
Sequencing (implementation order)
1
Peer-key registration + bootstrap — smallest; fixes the reject-all trap so anything can connect. done
2
Outbound dialer — the hard blocker; without it nothing is active. done
3
Sync / heartbeat loop — turns a connection into convergence. done