HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
josephg
diamond-types
Rust2021–2026
josephg / diamond-types

diamond-types

The world's fastest CRDT. WIP.

9Distinct people
20Mentions
0Explicit recommenders
1.9KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

The world's fastest CRDT. WIP.

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:56:07 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year20 dated mentions

Observed mentions: 2021-07-28 12:41:56 UTC to 2026-08-04 22:12:20 UTC.

024682021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20212022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 4 mentions; 0 submissions, 4 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments20232024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 3 mentions; 1 submissions, 2 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2026-01-01 00:00:00 UTC to 2027-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments. Incomplete calendar period.2026*

* An incomplete calendar period is marked with an outlined bar. Its count is not directly comparable with a full period.

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC202
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC404
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC808
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC312
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC101
2026 (partial)2026-01-01 00:00:00 UTC2027-01-01 00:00:00 UTC202

What Hacker News says

20 sources
I think it’s very easy to over apply that rule, and break a large function into a lot of small functions that you need to scroll up and down to reason about. Here’s a 150 line long function I wrote which I think is quite beautiful: https://github.com/josephg/diamond-types/blob/e143890a596aaf... This function traverses a DAG given 2 points in the dag, A and B. It breaks the dag into 4 regions - the nodes which are (transitively) only in the parent subgraph of A, B, in both or - implicitly - in neither. It runs in O(n log n) time. How would you improve this function? It could use a better doc comment. But do you honestly think it would be better if it were broken into a lot of small functions, each called once? How would you do it?
In "Clean" Code, Horrible Performance (2023)
(Xpost from my lobsters comment since the Author's active over here): I really disagree with this article - despite protestation, I feel like their issue is with Yjs, not CRDTs in general. Namely, their proposed solution: 1. For each document, there is a single authority that holds the source of truth: the document, applied steps, and the current version. 2. A client submits some transactional steps and the lastSeenVersion. 3. If the lastSeenVersion does not match the server’s version, the client must fetch recent changes(lastSeenVersion), rebase its own changes on top, and re-submit. (3a) If the extra round-trip for rebasing changes is not good enough for you, prosemirror-collab-commit does pretty much the same thing, but it rebases the changes on the authority itself. This is 80% to a CRDT all by itself! Step 3 there,
Read the rest
"rebase its own changes on top" is doing a lot of work and is essentially the core merge function of a CRDT. Also, the steps needed to get the rest of the way to a full CRDT is the solution to their logging woes: tracking every change and its causal history, which is exactly what is needed to exactly re-run any failing trace and debug it. Here's a modified version of the steps of their proposed solution: 1. For each document, every participating member holds the document, applied steps, and the current version. 2. A client submits (to the "server" or p2p) some transactional steps and the lastSeenVersion. 3. If the lastSeenVersion does not match the "server"/peer’s version, the client must fetch recent changes(lastSeenVersion). The server still accepts the changes. Both the client and the "server" rebase the changes of one on top of the other. Which one gets rebased on top of the other can be determined by change depth, author id, real-world timestamp, "server" timestamp, whatever. If it's by server timestamp, you get the exact behavior from the article's solution. If you store the casual history of each change, you can also replay the history of the document and how every client sees the document change, exactly as it happened. This is the perfect debugging tool! CRDTs can store this casual history very efficiently using run-length encoding: diamond-types has done really good work here, with an explanation of their internals here: https://github.com/josephg/diamond-types/blob/master/INTERNA... In conclusion, the article seems to be really down on CRDTs in general, whereas I would argue that they're really down on Yjs and have written 80+% of a CRDT without meaning to, and would be happier if they finished to 100%. You can still have the exact behavior they have now by using server timestamps when available and falling back to local timestamps that always sort after server timestamps when offline. A 100% casual-history CRDT would also give them much better debugging, since they could replay whatever view of history they want over and over. The only downside is extra storage, which I think diamond-types has shown can be very reasonable.
In Lies I was told about collaborative editing, Part 2: Why we don't use Yjs
The thing that can make real world text CRDT implementations complex is that the optimisations kinda bleed into all the rest of your code. The 2 big optimisations you want for most text CRDTs - including egwalker - are: - Using a b-tree instead of an array to store data - Use internal run-length encoding. Humans usually type in runs of characters. So store runs of operations instead of individual operations. (Eg {insert "abc", pos 0} instead of [{insert "a", pos 0}, {insert "b" pos 1}, {insert "c" pos 2}]). But these two ideas also affect one another. Its not enough to just use a b-tree. You need a b-tree which also stores runs. And you also need to be able to insert in the middle of a run. And so on. You need some custom collections. If you do run-length encoding properly, all iteration throughout your code needs to make use of the compr
Read the rest
essed runs. If any part of the code works character-by-character, it'll become a bottleneck. Oh and did I mention that it works even better if you use columnar encoding, and break the data up into a bunch of small arrays? Yeahhhh. So thats why diamond types - my optimized egwalker implementation - is tens of thousands of lines of code instead of a few hundred. (Though in my defence, it also includes custom binary serialization, testing, wasm bindings, and so on.) Rust makes the implementation way easier to implement thanks to traits. I have simple traits for data that can be losslessly compressed into runs[1]. A whole bunch of code takes advantage of that, by providing tooling that can work with a wide variety of actual data. For example, I have a custom vec wrapper that automatically compresses items when you call push(). I have a "zip" iterator which glues together other iterators over run-length encoded data. And so on. Its great. Though now that I think about it, maybe all that trait foo is what makes it headache inducing. I swear its worth it. [1] Eg MergableSpan: https://github.com/josephg/diamond-types/blob/00f722d6ebdc9f...
In CRDT: Text Buffer
I’ve run into a similar problem in collaborative text editing. I assign an integer to each edit (which is a single character insert or delete). But obviously, humans usually type characters and delete characters in runs. I needed a fast associative map from edit id (integer) to some associated data. But the associated data is internally run length encoded so that I can assign a single value to an entire sequential run of edit operations. For example, set(10..20, X), set(12..15, Y) gives a map with 3 values: {10..12: X, 12..15: Y, 15..20: X}. I can do lookups by querying individual values, and the query returns the maximum run containing a consistent value. I’m not sure how applicable my solution is to your problem, but it might work well. I implemented a custom btree with the semantics I needed, since btrees are fast and have great cache
Read the rest
locality. The trick is making one that understands ranges, not just singular keys like std BtreeMap. The code is here if you’re curious. It’s implemented on top of a pair of vecs - which makes it 100% safe rust, and curiously faster than the equivalent tree-of-raw-allocations approach. Please forgive the poor documentation - it’s an internal only type and the code is new enough that the paint hasn’t dried yet. https://github.com/josephg/diamond-types/blob/master/src/ost...
In Ask HN: Fast data structures for disjoint intervals?
> I think far more interesting these days would be projects like Veilid, Hyphanet's Locutus I have not assessed Veilid yet but it's on my list and at a first glance seems like a very serious and informed attempt. I'm personal friends with Freenet / Hyphanet's Ian Clarke and spoke with him about Locutus when he was just getting started. It sounded awesome then and I will give this a second look too, though when he explained it to me it sounded like it had the same limitations with deletion that Nostr or the global IPFS network would have. It does seem important to note here that both Veilid and Locutus are much less mature and battle-tested than libp2p and Tor and have less Lindy longevity (longevity as a function of age.) We already suffer a lot from being on the bleeding edge, so it's nice to limit the number of bleeding edge tools we us
Read the rest
e. Libp2p, notably, has been rock solid for us and barely a time drain at all, apart from some unexpected interactions with Tor which are mostly about the lack of an official first-class Tor transport, which is specific to our use case and should start to change soon when Tor's Arti is ready. > and ultimately Nostr -- even though not truly P2P in that sense -- which already happens to have a first try going with nostrchat.io. Nostr and Bluesky both seem very promising for the open-world use case of social networking, and it has been amazing to see Nostr grow so rapidly as a community. I am rooting for this project and we might use it someday in Quiet for public feeds. Timed deletion is the user requirement that drives me away from building Quiet on Nostr. Based on conversations I've had with users doing sensitive work (and based on my own experience as a founder of Fight for the Future) timed deletion is extremely important to team security, and for deletion to be meaningful one needs more control over where the data is relayed than what Nostr provides in the default mode. A group that wanted trustworthy timed deletion would have to control their own private Nostr relay. Technically, a Tor relay could subvert the timed deletion of some Quiet messages just by capturing all traffic, but this is much less of a worry. > If P2P is something that is truly desired, I feel like projects like Briar (https://briarproject.org/how-it-works/) have solved this with Bramble (https://code.briarproject.org/briar/briar-spec/blob/master/p...) more eloquently than it could be done on top of IPFS. Bramble could work for us and I would recommend that anyone look into it. Briar is probably the most similar thing to Quiet that exists right now. There are big differences between Quiet and Briar, but we could definitely build Quiet on Bramble if it adequately supports iOS. My worry would be its maturity as a tool for people building things other than Briar. That could be worth the risk though and I do recommend anyone else reading this thread look at Bramble if you are doing something similar. > I could nevertheless imagine it being overtaken fairly quickly by other projects sporting a rather lightweight and more managable basis, that allows for increased development speed and ultimately for faster iteration on features that users might wish for (e.g. DMs, @-mentions, message deletion, mobile clients, you-name-it) -- without the need to invest heavily into e.g. performance (or reliability!) issues of the underlying framework. This is definitely something we will keep an eye on, and thank you for the thoughtful advice! My guess is that as soon as we have a significant number of real users we will need to build things that don't happen to be supported by whatever stack we choose (whether that is our current stack, Bramble, Veilid, Automerge, etc.) So the question is what's the easiest one to maintain and adapt. So far libp2p and IPFS have both been good in that department: implementations in many languages, active development, an absence of major problems showing signs of maturity (especially in libp2p), etc. Also, my 2 cents are (for anyone following along) that if I had to do this all over again I would use Tor + Libp2p + Automerge. Libp2p and Gossipsub are solid, flexible, and will be around a while. No need to reinvent the wheel. The conceptual framework behind Automerge and Briar/Bramble are pretty similar (sync state!) but the Automerge team exists to serve people building other apps, while the Bramble team mostly focuses on Briar AFAIK. What's nice about Automerge is that the community around it (Ink & Switch, Martin Kleppmann, and other academics) is all at the academic frontier, so the level of thought and anticipation of user needs that goes into their decisions is very thorough, even if the implementations lag behind the papers. If I was doing real-time p2p text editing I would also look at the Braid project (braid.org) and Seph Gentle's work on Diamond Types, since that's where the most thought has gone into the raw performance you need for text CRDTs that can handle large documents: https://github.com/josephg/diamond-types
In Quiet – Encrypted P2P team chat with no servers, just Tor
I was responding to the diamond-types CRDT author's question in the parent comment. Their github project page [1] mentions text editing a lot: > This repository contains a high performance rust CRDT for text editing. This is a special data type which supports concurrent editing of lists or strings (text documents) by multiple users in a P2P network without needing a centralized server. > This version of diamond types only supports plain text editing. Work is underway to add support for other JSON-style data types. See the more_types branch for details. In any case, I agree with the metaphor and batching granular operations can always be done. [1]: https://github.com/josephg/diamond-types
In Ask HN: What is new in algorithms and data structures these days?
Anything CRDT (conflict free replicated datatypes: https://en.wikipedia.org/wiki/Conflict-free_replicated_data_...) related is fun to read up on and play with. Papers and references (page maintained by central academic in the world of CRDTs): https://crdt.tech Group doing research into how they can be used to build interesting collaborative (and async) applications: https://www.inkandswitch.com A few of the major open source implementations - mostly for rich text editing or JSON like data structures: - Yjs: https://github.com/yjs/yjs - Automerge: https://github.com/automerge/automerge - Peritext: https://www.inkandswitch.com/peritext/ - Dimond types: https://github.com/josephg/diamond-types People building eventually consistent database syncing with them: - https://electric-sql.com (Postgres <-> SQLite) - https://vlcn.io (SQLite <-> SQLit
Read the rest
e) Open source colaborative servers (coordination, persistance, presence): - https://github.com/ueberdosis/hocuspocus - https://github.com/partykit/partykit - https://github.com/firesync-org/firesync
In Ask HN: What is new in algorithms and data structures these days?
> I mostly just find them to be incredibly noisy compared to a well-written function. I really like Ocaml where it's statically typed without needing to actually specify them. Yeah; I haven't worked with ocaml but I've done some haskell (where you think about types so much more). Personally I don't mind rust / typescript's approach of needing types at the function boundary (function input & output types must be specified) while doing inference wherever possible inside each method. As an example, here's a very complex function in a project I'm working on chosen vaguely randomly[1]. The function diffs a run-length encoded DAG using a breadth-first search. Visually scanning for types, there's a couple at the top of the function - both in the function definition and the BinaryHeap: let mut queue: BinaryHeap<(LV, DiffFlag)> = BinaryHeap::n
Read the rest
ew(); But I think thats about it. Maybe there's more manually specified types in "normal" rust because most functions are smaller than that. But, it doesn't feel so bad. In this case I could probably even remove the explicit type annotation for that queue definition if I wanted to, but it makes the compiler's errors better leaving it in. [1] https://github.com/josephg/diamond-types/blob/66025b99dbe390...
In Elixir and Rust is a good mix
diamond-types (for reference for others [0]) still only supports plain text, is that right? I was thinking of using it for more general use cases such as an offline habit tracker, which isn't text of course, but I was interested to hear more on the progress towards other data types such as generic JSON data. Currently for this use case I've been using autosurgeon [1] so far which has a nice Rust API for structs, even if it might be slower than yjs (or yrs, its Rust implementation) or diamond-types. [0] https://github.com/josephg/diamond-types [1] https://github.com/automerge/autosurgeon
In Automerge 2.0
So exciting! Strangely enough, a couple of hours before this release, we just managed to wrap our heads around Yjs after playing with it on and off for a few weeks! For anyone not up to date with the world of CRDTs, Seph Gentle's two blog posts have become legendary: * https://josephg.com/blog/crdts-are-the-future/ * https://josephg.com/blog/crdts-go-brrr/ these are also worth checking out: * https://github.com/y-crdt/y-crdt (rust implementation started by the creator of Yjs, Kevin Jahns) * https://github.com/y-crdt/ypy (python bindings for the rust implementation) * https://github.com/josephg/diamond-types (Seph Gentle's rust implementation of YATA, the algorith behind Yjs)
In Automerge 2.0
I'd also add - Local First Software [https://news.ycombinator.com/item?id=31594613 (28 comments)] by Martin Kleppmann (who works on Automerge at the company Ink and Switch, perhaps better known as the author of Designing Data Intensive Applications), which introduces Automerge - CRDTs: The Hard Parts [https://news.ycombinator.com/item?id=23802208 (124 comments)], a video talk also by Kleppmann - CRDTs go brrr, 5000x faster CRDT implementations [https://news.ycombinator.com/item?id=28017204 (151 comments)], by the creator of another CRDT in Rust library, Diamond Types [https://github.com/josephg/diamond-types]
In Automerge 2.0
Using invariants like this is now my preferred programming style. Almost all of my complex container classes have a dbg_check() method which checks all the internal invariants hold, and panics if not. When testing and debugging, I’ll add calls to dbg_check() after each mutation of my data structure. And then I write a fuzzer which exercises my API in a loop. Each iteration, I check that my invariants still hold. The nice thing about this sort of invariant checking is that it makes it very fast and easy to narrow in on bugs. The crash happens right after a change which made the data invalid, not later when the invalid data is accessed. And a few dozen lines of fuzz testing can find an extraordinary quantity of subtle bugs. It’s remarkable. Devastating for the ego, but remarkable. Edit: Simple dbg_check implementation example: https://githu
Read the rest
In Adding design-by-contract conditions to C++ via a GCC plugin

Showing the 15 most recent of 20 sources. Enable JavaScript to search and browse every indexed mention.

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.