Blog / Lattice
Lattice: Separating Identity, Address, and Message History
Starting with how contacts find each other and who keeps their messages, this essay compares Lattice with Matrix and examines the group-state, ordering, and device-recovery choices personal nodes must face.
Explore Lattice sourceThis article is also available in Chinese 阅读中文版
On this page9 sections
Version note. Implementation claims are pinned to
50d2bb192ba842c11cca6e7f3313ff3a379f3b89. The author has confirmed that the information-exchange center only records and provides the address/IP associated with an account. It does not handle login, authorization, or blocking. Users may choose a real IP or a relay IP. The detailed group, ordering, and new-device recovery rules remain for the author to decide.
When Alice wants to message Bob, she first needs to find Bob’s Store. The system must also explain which nodes carry the message, who keeps it, and why Bob’s device should accept it. Groups add a harder question: if both sides change membership during a network partition, how should the conflict be resolved after they reconnect?
These questions are related, but they need not be answered by the same service. Lattice’s core choice is to express identity, location, history ownership, and message admission separately. Its starting point differs from Matrix’s homeserver and shared-room organization of federation; the useful comparison is where each system places control and operational responsibility.
Start with the whole system: who stores what, who decides what
In Lattice’s target model, Alice and Bob each hold identity keys and may each run a Store that keeps their own communication history. An information-exchange service maintains a mapping between an account or public-key fingerprint and an address/IP, helping contacts find the currently listed location. A user chooses whether to publish a real IP or a relay IP. If a relay is used, a Peer may carry the message along the path; that does not make it the final owner of a person’s history.
Scroll horizontally on a narrow screen.
The information-exchange service’s boundary is settled: it handles the relationship between an account and an address/IP. User identity keys, message admission, device trust, and group membership are expressed through other mechanisms. Verifying an address claim’s signature checks that the corresponding key signed it; it does not grant communication permission.
A public-key fingerprint identifies an identity, an address/IP helps locate a node, a signature verifies a claim or message, the receiving side decides whether to accept it, and end-to-end encryption limits what intermediaries can read. A signed address claim does not show that the endpoint is online or that the recipient accepts messages from strangers.
The states “public,” “friend request,” and “closed” still need a product definition: do they control address visibility, message admission, or two independent settings? The design must also say where requests arrive, whether rejection still allows address discovery, and how an old cached address expires.
What exactly fails when the address service is unavailable
If Alice already has Bob’s address, a directory outage does not necessarily cut them off. She can try the address she has as long as it remains valid and reachable and Bob’s Store is running. The outage affects new lookups and address updates. If Bob has moved, the old address no longer works, and Alice has no other source for the new one, a public-key fingerprint alone cannot reveal Bob’s location.
The address service is an out-of-band discovery path; Store nodes carry the message. Communication depends on whether the saved endpoint remains reachable and whether users selected a relay. A real IP and a relay IP have different reachability, operational, and network-privacy costs. The protocol and product still need to say whether delivery tries a backup path, queues locally, or reports an unknown result.
The pinned snapshot has no directory redundancy or automatic migration path. The center is outside message governance, while address lookup remains an availability dependency whose cache lifetime and update process shape how users regain contact.
Where Lattice’s structure differs from Matrix
Matrix is an open federated protocol: different homeservers can participate in the same room and propagate room history under a shared event model.1 A Matrix user ID includes a server_name, but that name is not a physical machine’s IP address. Matrix server discovery and delegation can map a server name to its actual hosting location. The accurate comparison is that the systems express identity, hosting names, and communication endpoints in different places.23
Lattice makes a public-key fingerprint central to contact identity and stores the Store address separately as routing information. That lets protocol discussions treat identity keys and node locations as distinct fields. The cost is that users must understand how key loss, stale addresses, identity rotation, and recovery relate. Keeping the fingerprint does not automatically migrate history, session keys, or the address cached by a contact.
Room state is another substantive difference. Matrix does more than link messages in a DAG: it defines authorization evidence, room versions, and rules for resolving branched state.4 Lattice has prototype structures for Rooms, member roles, RoomEvents, and message DAGs, but a DAG alone does not make every Store reach the same conclusion about membership. Interoperating with Matrix rooms would also take more than converting Protobuf to JSON; it requires mappings for identity, permissions, event history, key sharing, and failure semantics.
| Dimension | Matrix | Lattice’s direction and cost |
|---|---|---|
| Identity and location | User IDs include a server name; discovery and delegation resolve that name | Public-key fingerprint and Store address are separate; key custody is more direct, as is responsibility for recovery |
| History ownership | Homeservers participating in a room store and propagate room events | Personal Stores are more prominent; consistency across Stores in a group remains a protocol problem |
| Room authority | Room rules define authorization and state resolution | A narrower personal-node or small-group model is possible, but concurrent membership changes still need rules |
| Device trust | Specifications include device verification, cross-signing, and key backup mechanisms | Device keys and crypto components exist; verification and recovery still need a complete workflow |
| Ecosystem | Existing clients, servers, and rooms | A custom Protobuf/WebSocket protocol is more directly controlled, but does not interoperate with Matrix clients and rooms by default |
A reason to choose Lattice would be to explore how public-key identity, personal Stores, user-selected addresses, and embeddable Rust capabilities can form a communications system—and to accept the cost of a protocol still under construction. If the goal is to use mature Matrix clients, join existing rooms, or depend on its interoperability ecosystem, Matrix is the better fit. Architecture choices follow requirements; they do not need another system to be disparaged in order to make sense.
Why groups need more than a DAG
A message DAG can express causal history and hold concurrent branches. It does not decide who may invite a member, remove an administrator, or which side had the latest membership state during a partition. Imagine Alice is the room owner and Bob is an administrator. A partition occurs. Alice removes Bob on one side; on the other side, Bob—still acting on the old state he can see—invites Dana. Both events may be correctly signed by their respective keys. When the network heals, every Store still has to answer: was Bob’s invitation valid? At what point did the revocation take effect? Is Dana now a member?
Scroll horizontally on a narrow screen.
One candidate is to propagate all events and then resolve state under shared authorization evidence and deterministic rules. Nodes that receive the same event set could converge, but the rules must define which history determines Bob’s authority and whether the other side may continue accepting actions under stale state during the partition. A signature proves who issued an event; it does not prove that signer or recipient has seen a newer revocation.
Another candidate is to require common confirmation for administrative changes, such as confirmation from an agreed sequencer or quorum. This can give confirmed operations a common order, but the side that cannot reach confirmation during a partition must stop confirming new membership state. The quorum participants, threshold, membership changes, and failure behavior all need definition. This is not a free “more decentralized” option; it trades availability and consistency differently.
Message display order needs to be separate from permission state: replies preserve causal context, an interface can order its local events, and group administration may require common confirmation. Causal precedence alone does not define a total order for all events.5 If Stores use their local receive times to place late messages, the design must decide whether temporary differences between nodes are acceptable and whether a confirmed prefix must remain stable.
Matrix’s useful lesson is that room-event DAGs, authorization rules, and state resolution must be designed as related mechanisms. Lattice can start with narrower group functionality; at any scope, it needs explicit authorization evidence and partition-recovery rules for membership changes.
New-device recovery: borrow the experience, not an assumed implementation
New-device recovery has at least three parts: proving the device belongs to the same user, securely transferring the keys it needs, and deciding which history it can restore. Matrix specifies device verification, cross-signing, and encrypted key backup separately. A recovery key can unlock a key backup, but it cannot recreate ciphertext that was never backed up.6 Recovering one’s own device identity also differs from adding a new group member: a new member must pass that group’s admission rules, while a same-user device must extend the user’s trust chain.
Lattice could treat a Matrix-like experience as a candidate direction: verify a new device while an old one is online, or use separately stored recovery material after all old devices are lost. A final flow still needs to define root-key custody, device verification, backup responsibility, session-key scope, access to pre-join group history, and the identity state contacts see after recovery material is lost.
An E2EE design note proposes that a new device should not receive old Megolm sessions from group members. Megolm key rotation and history visibility need to be considered alongside membership changes.7 That could support a policy where a new device decrypts group messages only from the time it joins, while the user may recover previously held keys from their own encrypted backup. These choices can coexist, but the source of each key and who may recover it must be explicit. The author has not confirmed a complete recovery model, so this article does not present that path as settled behavior.
The pinned prototype path
When Alice sends a message, her client uses Bob’s identity and address information to construct the protocol message. An Envelope binds the sender, recipient, payload, and parent messages, and is signed by the sender. If the body is end-to-end encrypted, intermediate Stores or Peers should see only ciphertext. Alice’s Store forwards by recipient address; Bob’s Store validates and saves the message, and Bob’s client syncs it. If lookup fails, the flow stops at discovery. If delivery fails, the sender needs to distinguish “not delivered,” “processing,” and “unknown” instead of reporting that submission means receipt.
Scroll horizontally on a narrow screen.
The pinned backend WebSocket path includes frame handling, signature verification, configured contact-admission checks and rate limiting, message persistence, forwarding by recipient address, and incremental sync. The DAG code can identify missing parents and has pending/outlier handling. The Peer’s bounded cache has a TTL, so it offers temporary relay behavior rather than durable offline history. The discovery implementation is a WebSocket service with an in-memory mapping prototype. Although its types and documentation use the name DHT, the code is not yet a complete distributed Kademlia network.
Two different operations can be mistaken for “sending.” The backend Store path can receive and forward Envelopes. FFI send_message constructs a local message, writes it to SQLite, and attempts to index it; that function does not currently establish cross-Store network delivery. The pinned commit uses vodozemac for Olm/Megolm cryptographic components; Matrix SDK’s no-network-I/O core and storage interfaces are also an engineering reference.89 The repository includes source for an encrypted round trip through a Store, covering a specific message path. Device verification, cross-device key sharing, recovery, and background delivery are not yet one complete user workflow. These tests were not rerun for this article.
The prototype shows how some of these choices enter code: Protobuf defines node data, the Rust core holds DAG, Room, and interface logic, Store handles server-side communication, Peer offers temporary caching, and FFI exposes a subset of local capabilities. Separating protocol, rules, implementations, and assembly can help validate different runtime shapes. Yet the current implementation still has one SQLite storage assembly, direct WebSocket dependencies in the backend, a limited FFI path, and incomplete cross-Store room-state semantics. A shared core can reduce duplicate implementation; it does not automatically finish mobile background delivery, message repair, interoperability, or device recovery.
The current ordering implementation uses DAG depth, each Store’s local receive time, and a hash as keys. Lamport’s account of causal order helps explain the distinction between “happened before” and a total order, but receive time is observed independently at each Store. If the product requires a confirmed display prefix never to change, it needs an additional ordering or common-confirmation rule.
Benefits and costs
The benefit of this separation is that identity keys, network addresses, history ownership, message admission, group governance, and device recovery can be designed independently. Readers can ask where each authority sits, which paths remain during an outage, and what a user can recover after losing a device.
The costs include operating and backing up a personal Store, exposing network location through a direct address, running relays with clear retention, handling partitions and late group events, balancing recovery convenience with key security, and building without Matrix’s interoperability ecosystem. These boundaries may suit developers embedding communication; they also mean more setup and support for people who expect chat to work out of the box.
Questions the author still needs to decide
- Do “public,” “friend request,” and “closed” control address visibility, message admission, or separate settings for each?
- When a user publishes a real IP or relay IP, exactly which address does the directory store, and who confirms expiry, withdrawal, and updates?
- For concurrent group changes, is the priority to allow actions during partitions and converge later, or to require common confirmation before changes take effect? How much revocation delay is acceptable?
- Must all Stores eventually display the same order, or must a confirmed display prefix never be reordered? These promises have different implementation costs.
- Which parts of identity, device trust, and history should new-device recovery restore? If recovery material is lost, should contacts see an identity reset or another recovery state?
These questions affect protocol fields, stored state, and what users can expect. Answering them is how the prototype’s architecture becomes predictable product behavior.
Pinned snapshot and further reading
Implementation claims in this article are pinned to 50d2bb192ba842c11cca6e7f3313ff3a379f3b89. The earlier crate-by-crate audit and detailed source trail are preserved in docs/drafts/lattice-prototype-audit-en.md and are not the main line of this article.
参考文献 / References
-
Matrix.org, Matrix Specification v1.19: Architecture. Protocol context for homeservers, room events, and federation. ↩
-
Matrix.org, Matrix Specification v1.19: User Identifiers. The server-name namespace in MXIDs. ↩
-
Matrix.org, Matrix Specification v1.19: Server Discovery. Server-name resolution and delegation; a name should not be equated with a physical machine address. ↩
-
Matrix.org, Matrix Specification v1.19: Room Version 12. Room-event authorization and state resolution. ↩
-
Leslie Lamport, Time, Clocks, and the Ordering of Events in a Distributed System, Communications of the ACM 21(7), 1978. The distinction between causal ordering and a total order. ↩
-
Matrix.org, Matrix Specification v1.19: Client-Server API. Cross-signing, devices, and key-backup specifications. ↩
-
Matrix.org, Megolm group ratchet. Group-session key limitations and rotation boundaries. ↩
-
Matrix.org, vodozemac documentation. The Olm/Megolm cryptographic implementation used by Lattice. ↩
-
Matrix.org, matrix-sdk-base API documentation. An engineering reference for a no-network-I/O client core and storage interfaces. ↩