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.

Contents
What a Federation Is The Activation Layer (FR-3) — Now Implemented PBFT: Making Decisions Together Broadcasting & Synchronization How Xudanu Shares Content How a Donated Machine Joins When Things Go Wrong: Failure & Recovery Scaling: What to Expect as You Grow Problem Checklist by Scale Testing it: Docker Test Bed

What a federation is

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.

PRIVATE FEDERATION NETWORK (e.g. Docker bridge, or WAN between donated hosts) Users (browser) Users (browser) web + /xudanu WS Peer A web UI . /xudanu . /federation Ed25519 identity + /data --peer B C D Peer B web UI . /xudanu . /federation Ed25519 identity + /data --peer A C D Peer C web UI . /xudanu . /federation Ed25519 identity + /data --peer A B D Peer D web UI . /xudanu . /federation Ed25519 identity + /data --peer A B C /federation WS . BLAKE3-verified replication users / client WS peer (full node) federation link
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 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):
Leader (L) proposes Replica A validates Replica B validates Replica C offline (f=1) PHASE 1 PrePrepare Leader broadcasts the proposal to all replicas GovernancePrePrepare PHASE 2 Prepare Each replica broadcasts its Prepare vote to all others GovernancePrepareVote 2f+1 Prepare votes received? YES (3 of 4) -> advance to Commit PHASE 3 Commit Replicas broadcast Commit votes; on 2f+1, the round is sealed GovernanceCommitVote SEAL Sealed 2f+1 Commit votes -> round is sealed; transactions are executed GovernanceSealed -> all nodes execute transactions Quorum requirements by cluster size: 1 node: local mode (quorum 1) 4 nodes (f=1): need 3 votes 7 nodes (f=2): need 5 votes 10 nodes (f=3): need 7 votes
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)ModeFault tolerance (f)Votes needed (2f+1)Can lose
1single-server—1 (local)— local mode
2–3governance refused——content sync works; no BFT decisions refused
4BFT131 byzantine node, 0 crashes zero margin
5BFT131 byzantine + 1 crash recommended
7BFT252 nodes OK
10BFT373 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.

PeerPool broadcast() / send() Sync Timer (60s) build_sync_frames() Heartbeat (30s) liveness + dead-peer detection Outbound Connection tokio::select! { ... } recv from peer -> process broadcast frame -> encrypt+send heartbeat tick -> send sync tick -> batch send Encrypted AEAD Channel ChaCha20-Poly1305 Peer processes frames Frame types sent during each sync cycle: SyncPull MembershipSyncPush StateSyncPush EndorsementSyncPush CrdtSyncPull CRDT guarantee: merge(A, merge(A, B)) == merge(A, B) — duplicates are harmless; convergence is mathematical, not best-effort.
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):

User edits document Edition stored BLAKE3 hash of chunk content Peer A chunk_store edition_payload span_provenance federation_export SyncWorkEntry SyncPush AEAD encrypt ChaCha20-Poly1305 Peer B recompute BLAKE3 hash matches? import chunk chunk_store edition visible Tampered blob? recomputed hash != expected hash -> REJECTED Peer C same flow: verify + import deduplicates by BLAKE3 fingerprint t=0s: user edits edition stored export + sync encrypt + transmit verify + import Total latency: typically < 1 second within a sync cycle; at most 60s until the next sync tick.
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 typeHow it's verifiedDeduplication
Works (documents)BLAKE3 hash of all edition entriesExisting fingerprints checked before import
Editions (versions)Content-addressed within the workPer-work fingerprint set
Blobs (images, files)BLAKE3 hash of raw bytesHash-addressed on disk
MembershipEd25519 signature on each endorsementOR-Set tags (idempotent merge)
EndorsementsSigned by the endorsing member's keyOR-Set (idempotent)
Reconcile stateLWW timestamp + content hashHighest 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.

1 . bootstrap self-endorse (membership_bootstrap_init) 2 . dial founder --peer outbound dialer 3 . handshake mutual Ed25519 + X25519 / AEAD 4 . key registered --trusted-peer-key is_peer_known = true 5 . join + endorse MembershipJoinRequest -> endorsed -> cluster converges
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.

Scenario 1: Network partition (node temporarily unreachable)

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.

t=0 — Healthy federation A B C t=1 — Partition! C is isolated A B C X (broken) A-B still operate edits continue t=2 — C reconnects (backoff retry succeeds) A B C t=3 — C syncs missed data, cluster converges SyncPull -> gets all missed updates BLAKE3 verify -> import CRDT merge -> identical state No conflict resolution needed -- CRDTs are designed for exactly this scenario. Recovery is automatic. The dialer's exponential backoff (1s, 2s, 4s, ... 30s cap) retries until the peer is reachable, then the sync loop catches up.
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:

Scenario 3b: Leader failure — automatic view change

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 pairsConnections per peerAssessment
332trivial
5104easy
10459fine
2019019moderate
501,22549heavy
1004,95099very 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 sizeContent volumePer-peer bandwidth (60s)Notes
3 peers~10 MB~10 MBTrivial on any connection
5 peers~50 MB~50 MBFine 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

Recommended configurations

Use casePeersMin specs per peerNetwork
Personal / testing2–32 vCPU, 2 GB RAM, 10 GB diskAny broadband
Small team3–52 vCPU, 4 GB RAM, 20 GB disk10 Mbps uplink
Organization5–104 vCPU, 8 GB RAM, 50 GB disk50 Mbps uplink
Large federation10–204 vCPU, 16 GB RAM, 100 GB disk100 Mbps+ datacenter
Research / max scale20–508 vCPU, 32 GB RAM, 200 GB SSDGigabit 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.

5–10 nodes (organization)

10–20 nodes (large federation)

20–50+ nodes (research / max scale)

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
4
Donated-host join + PBFT broadcast — cluster-wide trust and decisions. done
5
Real two-server integration test — proves 1–4 end-to-end. done
6
BFT hardening round — signed vote certificates, automatic view change, key epochs + rotation authority (FR-75), state transfer, genesis pinning, per-IP connection caps, randomized mesh fuzz, and a TLA+/TLC safety proof. done
7
Operations — 4-node demo cluster, lifecycle scripts (start/stop/status/logs/diagnose), and the operator guide (FEDERATION-OPS.md). done