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.

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 --peer C Peer B web UI . /xudanu . /federation Ed25519 identity + /data --peer A --peer C Peer C web UI . /xudanu . /federation Ed25519 identity + /data --peer A --peer B /federation WS . BLAKE3-verified replication users / client WS peer (full node) federation link
Three full-node peers on a private network. Users hit any peer's web UI; peers replicate content and membership 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 bootstrapmembership_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 broadcastGovernancePropose 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, the broadcast channel has no subscribers, and is_peer_known returns true for everyone. You opt in at startup with CLI flags.

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:

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 (2 of 3 active) -> 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: 3 nodes (f=1): need 2 votes 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)Fault tolerance (f)Votes needed (2f+1)Can lose
3021 node OK
4131 node OK
7252 nodes OK
10373 nodes OK
2020 nodes fragile
Practical guidance: 3 nodes is the minimum for a resilient federation (tolerates 1 offline). 2 nodes works but both must always be online. 4 nodes is the sweet spot — tolerates 1 failure with margin.

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.

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. 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 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)

5–10 nodes (organization)

10–20 nodes (large federation)

20–50+ nodes (research / max scale)

Bottom line: For most use cases (3–10 peers on VPS or donated machines), Xudanu's federation works well out of the box. The full-mesh + periodic sync model is simple, robust, and self-healing. Scaling beyond 20 nodes is possible but will benefit from the incremental sync and gossip relay optimizations on the roadmap.

Testing it: Docker test bed

The docker-compose.yml runs three full-node peers on a private bridge network (host ports 8081/8082/8083), each with --peer pointing at the other two. On a 32 GB machine this is trivial for small/fresh data dirs — you could run 5–10+ peers; 3–4 is the sweet spot for exercising PBFT quorum and membership.

docker compose up --build
# then browse:
open http://localhost:8081   # peer A
open http://localhost:8082   # peer B
open http://localhost:8083   # peer C

For manual testing with two peers (no Docker):

# Terminal 1: Peer A
cargo run --release --features server --bin xudanu-server -- \
  run 127.0.0.1:8081 data-a \
  --peer 127.0.0.1:8082 \
  --trusted-peer-key <B's verifying key>

# Terminal 2: Peer B
cargo run --release --features server --bin xudanu-server -- \
  run 127.0.0.1:8082 data-b \
  --peer 127.0.0.1:8081 \
  --trusted-peer-key <A's verifying key>

Each server prints its verifying key at startup: Federation: this server's verifying key: <hex>. Copy it to the other peer's --trusted-peer-key flag.

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