LAN Clustering.
hidden_dim values per token, in fp16. The plan is a pure function of the model shape and the candidate members, so every member computes the identical layout.- STATUS
- Testnet
- CRATE
- tenzro-model::cluster
- RPC
- tenzro_clusterPlan
When a cluster forms
A cluster trades roughly half the decode speed — each token becomes a network round-trip — for capacity, so it is only worth forming when the model does not fit on the single biggest member. single_box_fit finds the largest member whose VRAM covers the whole model; should_cluster turns that into a FitDecision:
RunLocal — fits on the biggest single member; run there
ClusterRequired — no single member can hold it; a cluster is needed
ClusterForced — would fit locally, but the user chose a clusterFitDecision::forms_cluster() is true for the latter two.
Layer assignment
assign_layersbin-packs the model's layers into contiguous ranges, one per member, proportional to each member's VRAM (largest-remainder apportionment). Remainder layers go to the highest-VRAM members first. Every admitted member gets a non-empty range. The result is keyed by address and deterministic — members are passed in a stable address order, so each one derives the same map.
assign_layers(model.layers, &members) →
HashMap<Address, PipelineStage { start_layer, end_layer }>Stage ordering and the reachability gate
order_stages decides which contiguous slice each member holds. It greedily chains members nearest-neighbour to minimize total transfer cost, and gates on data-plane reachability — a member can only carry per-token activations if the head can reach it directly:
MemberReachability::data_plane_eligible():
LocalDirect — same local segment (mDNS / private range) ✓
Direct — publicly dialable or hole-punched ✓
RelayOnly — only through a circuit relay ✗
SymmetricNat — no working hole-punch ✗order_stages returns a NetworkGate with the ordered members and the excluded ones, each carrying a RejectReason for display.
Membership requirements
The RPC wire protocol that carries activations between stages has no version negotiation, so every member must run the exact same runtime build commit (LLAMA_CPP_COMMIT, stamped at build time). Mixed compute backends are fine — one machine on CUDA, another on Metal, another on CPU — because each member is just a device executor for its layer range.hardware_gate rejects members whose build commit differs or whose VRAM cannot hold a single layer.
Planning over RPC
tenzro_clusterPlan is a pure function of its params — it reads no node state — so any operator can dry-run a placement before committing hardware:
tenzro_clusterPlan {
model: { layers: 80, hidden_dim: 8192, total_vram_gb: 140.0 },
members: [ <ClusterMember>, ... ],
user_forced: false,
} →
{
fit: "ClusterRequired",
forms_cluster: true,
activation_bytes_per_token: 16384,
stages: [
{ address: "<hex>", start_layer: 0, end_layer: 39 },
{ address: "<hex>", start_layer: 40, end_layer: 79 },
],
}From plan to running pipeline
Planning and serving are the same decision. When you call tenzro_serveModel, the node reads the model's GGUF header for its layer count and hidden dimension, discovers reachable members from gossiped ClusterProfile announcements, and runs the same placement function. If one host fits the model it serves alone; if not, the cluster forms automatically and the layers stream across stages.
Two opt-outs override the automatic decision: force_single keeps serving on one host even when a cluster would form, and an explicit cluster_members list replaces gossip discovery for a fixed set of machines.
Why activation cost is small
Layer-wise pipelining only ships the boundary activation between adjacent stages — hidden_dim × 2 bytes per token in fp16 — not the full hidden state of every layer and not weights. For an 8192-wide model that is 16 KB per token per boundary, which a commodity LAN carries comfortably. This is what makes a cluster of ordinary machines a viable way to serve a model that no one of them could hold.