CRUMB a card from devarno-cloud

LCBMessage & DAGNode Schema

weave beginner 4 min read

ELI5

LCBMessage is a parcel label: sender address (sender), tracking number (msg_id), send-date sequence counter (lamport), list of parcels this one depends on (parents), and the parcel contents (payload). DAGNode is the depot’s internal slip stapled to the label — it adds when the parcel arrived and whether it’s been handed to the customer.

Technical Deep Dive

Class Diagram

classDiagram
class MsgID {
+[u8; 32] 0
+from_bytes(bytes: [u8;32]) MsgID
+bytes() [u8;32]
}
class PeerID {
+u32 0
+new(id: u32) PeerID
+value() u32
}
class LCBMessage {
+MsgID msg_id
+PeerID sender
+u64 lamport
+Vec~MsgID~ parents
+Vec~u8~ payload
+new(msg_id, sender, lamport, parents, payload) LCBMessage
+to_bytes() Vec~u8~
}
class DAGNode {
+LCBMessage message
+u64 received_at
+bool delivered
+usize dependencies_remaining
+new(message, received_at) DAGNode
+satisfy_dependency()
+ready_to_deliver() bool
}
LCBMessage --> MsgID
LCBMessage --> PeerID
DAGNode --> LCBMessage

Source: mesh-node/src/proto.rs lines 10–121.

Wire Format (to_bytes layout)

---
config:
packet:
bitsPerRow: 8
---
packet-beta
title LCBMessage to_bytes wire layout
0-7: "msg_id [32 B Blake3]"
8-15: "sender [u8 PeerID]"
16-23: "lamport [u64 LE]"
24-31: "parents_len [u8]"
32-39: "parents[] [32 B × N]"
40-47: "payload [variable]"

Note: sender is serialised as a single byte (self.sender.value() as u8), capping PeerID to 0–255 on the wire. Larger peer sets would require a format change.

DAGNode Initialisation

DAGNode::new(message, received_at) sets:

  • delivered = false
  • dependencies_remaining = message.parents.len()
  • received_at = Unix microseconds (provided by caller; not set internally)

Field Semantics

FieldTypeRole
msg_idMsgID ([u8;32])Globally unique — Blake3 hash of content. Deduplication key in DAGState.messages.
senderPeerID (u32)Identifies originating peer. Stored in DAGState.peer_messages for per-peer tracking.
lamportu64Logical clock at send time. Used to update DAGState.lamport_clock.
parentsVec<MsgID>Causal dependencies. Length initialises dependencies_remaining.
payloadVec<u8>Opaque application bytes — typically a serialised Automerge change.
received_atu64 (DAGNode)Unix microseconds when message was received. Used for latency measurement.

Key Terms

  • MsgID → 32-byte Blake3-based identifier; immutable once created; used as BTreeMap key (Ord derived)
  • PeerIDu32 newtype; identifies a peer in the local mesh session
  • lamport → Logical send-time counter; not a wall clock; updated per max(local, received) + 1 rule
  • parents → Slice of MsgIDs that must be delivered before this message; empty for genesis messages
  • payload → Opaque bytes handed to the application after causal order is satisfied

Q&A

Q: Why is MsgID [u8; 32] rather than a u64 integer? A: Blake3 produces a 32-byte digest. Using the full digest as the ID enables content-addressing (peers verify the ID matches the hash of the operation) and makes collisions computationally infeasible. A u64 sequence number would require a central coordinator to assign.

Q: Can parents be empty for messages other than the genesis? A: Yes — any message whose sender considers no causal dependency necessary will have parents = []. This is valid but unusual in collaborative editing; it would appear as a concurrent root and be delivered immediately without waiting on any prior message.

Q: What does received_at measure and who sets it? A: It is Unix microseconds, provided by the caller of DAGNode::new. It is not set by DAGState itself. This design allows tests to inject deterministic timestamps.

Examples

Minimal message construction in a test (from proto.rs lines 255–262):

LCBMessage {
msg_id: MsgID::from_bytes([1; 32]),
sender: PeerID::new(1),
lamport: 1,
parents: vec![], // genesis — no dependencies
payload: vec![1],
}

A child message referencing the above as a dependency:

LCBMessage {
msg_id: MsgID::from_bytes([2; 32]),
sender: PeerID::new(2),
lamport: 2,
parents: vec![MsgID::from_bytes([1; 32])], // waits for msg [1;32]
payload: vec![2],
}

neighbors on the map