worm-blossom

worm-blossom

2026Week 2305 Jun

“productive discomfort”

A drawing of a street on a summer evening.
A drawing of Sammy, with a bandana and sunglasses on, and a swollen chin.

In case you missed it last week, sneakerweb is our new project where users exchange and collect websites via physical media like USB sticks. This website data is stored locally (with Willow) and made accessible via a local web server.

One of the nice things about having website data on disk is that you can build a complete overview of everything you’ve collected over time. So I’ve been designing and building a ‘front page’ to the sneakerweb which you’ll see if you browse to sneakerweb.localhost.

But what should this front page look like? We really want to do better than a base32 encoded public keys. I started looking into the designs of search engines of yore, and of Gemini gemlogs (which have a very elegant, clear presentation). We’re not at the point where we’re going to crawl sneakerweb site contents, so maybe authors could provide some metadata for us?

A .sneakerweb file could provide some details like a title, description, author, and we know when the site was last updated through Willow. And then we can use that data to build something that looks a bit like an old search engine results page.

Quaint, but sad.

Sadly it evokes the spirit of dullness. It requires a culture of self-volunteering bureaucracy, and agreeing with our taxonomy of websites (e.g. does a site always have a single author? A title? A description?).

Maybe search engine results are a dead end, and instead I should be looking at webrings. Friend of the site Miaourt linked me to Nekoweb, a hosting service where sites are presented as tiles in a grid, and the owners seem to have a lot of latitude in how their tile is presented. Some of them follow a fixed format, and some break out of it completely.

I love giving space to arbitrariness, so I am going to rip out the metadata stuff I’ve been working on and try to give way to authors’ ideas of how their site should present itself. I’m not sure yet if this will be as simple as an image file, or a tiny iframe pointed at some little sneakerweb.html page.

~sammy

Ourcana, or Data Rippling Out

Our first interview on worm-blossom.org! We chat with Sarah Aronson, aka Heni, about her app Ourcana, and its underlying Willow implementation... in Dart! And... 10,000 soon-to-be Willow users!!!

Sammy: Sarah! I guess the best place to start would be by introducing yourself.

Sarah: Hi, I’m Sarah Aronson. I’ve been programming since high school when I worked on FIRST Robotics Competition robots. High schoolers program robots that fight each other. Professionally, I’ve worked in places like the John Hopkins Applied Physics Lab with a modular prosthetic limb. I was on the Federal Railroad Administration doing graph theory for them in a pretty practical and applied way. I worked for a trucking company as their machine learning archictect, before AI really meant Gen AI.

Sarah: And in the end, I decided I wanted to work on an app for myself, and that’s what I’ve been doing for the past year or so. I want to write something that’s going to benefit me, going to benefit others, help others. And eventually, this space for the app, Ourcana, just sort of opened up.

Sammy: Naturally leading to the question of “what’s Ourcana”?

Sarah: To give some context, there was an app called Simply Plural. The plural community is a community that identifies with the experience of what is sometimes referred to as Disassociative Identity Disorder, or the experience of having different personality states. A lot of people who are adults naturally have this. You have a work personality, a friend personality.

Sarah: But for individuals who are plural, these are a lot more concrete. They like to have applications that give them logs. So I can have a log on Ourcana or Simply Plural where I describe what maybe my work personality is. And I give some notes like: “OK, I notice I like coffee; I like to talk to people this way; I’m most productive when I do this or that thing”.

Sarah: And then when people using these apps go to a therapeutic session to work with a professional to improve their mental health, or when maybe things are a little tough and they need to check their notes, so to speak, they open these apps and check, “OK, where am I? What was I doing? What context did I have?”, and it can be pretty helpful.

Sammy: When you started building Ourcana, what were the design challenges from a technical and experiential perspective?

Sarah: In March, the Simply Plural website and its developer announced the app will shut down, probably in June or July. The community was in a bit of a panic, and a lot of replacement apps popped up And many of them fell into the same class of problems. For some, there were drama or accusations, saying, “The data on the server is not encrypted. How do we know you’re not reading user data”? Other times, very nefarious apps would say, “Well, we are reading user data, and you’re not allowed to use our app if you do X, Y, Z”.

Sammy: Oh.

Sarah: I thought that that was untenable for privacy. I needed a way to communicate securely, authenticate who I am talking to, know that a message is not fake, as well as a layer of encryption. Before Ourcana, I was working on Fluffy Chat with the matrix.org protocol, which is an end-to-end encrypted (or at least end-to-end encryption capable) chat for online communities that’s self-hosted and federated. And I realized that that kind of technology was probably the direction I wanted to go. But the nature of very fast synchronous chats is not necessarily aligned with document storage and creating journals and fine-grained permission systems.

Sarah: So that was what I was thinking about. How do I do permissions? How do I do authentication? How do I add encryption on top? I need some kind of protocol to wrap it all together. And that’s really what I was thinking about in the development of Ourcana, at least to start.

Sammy: It also sounds like the trauma of Simply Plural shutting down and taking everything with them led to you wanting a solution which would make that situation impossible, or at least provide a fallback.

Sarah: Absolutely. What really struck me was when Simply Plural went down was a lot of people were left asking questions. What happened? Does my data work offline? Can I keep my data? Can I export my data? And the answers were, frankly, “We don’t know. We haven’t tested it”.

Sarah: So I wanted to create Ourcana with an open source server from the start, and the idea that any client can connect to any number of servers.

Sarah: I think I mentioned [on the worm-blossom Discord server] the idea of dropping a rock in a pond and the data ripples out. To me, that means I change a document, and several external servers eventually get my data through consistency sync. And that means if one or two go down, well, I’ve got my data on the remote servers. I’ve got a copy of the data on my phone.

Sarah: And what really interested me about Willow was this idea that I could hold onto data for my friends without being able necessarily to mutate it, tamper with it, and if encrypted, perhaps even read it. I really like the idea that, say, my partner could send me a copy of her data, keep some of it private, and I’d have a backup for my partner on my phone. So no matter what happens — server goes down, her phone explodes — I can say, “here you go”. My number one design concern was ensuring that Ourcana doesn’t crash and burn like Simply Plural does and everyone loses their data and we’re back in the current situation again.

Sammy: Right. You’ve got a kind of communal backup system.

Aljoscha: One of the slogans of Secure Scuttlebutt was “Your friends are the data centre”.

Sarah: Yeah, I saw that. I got that sense from Willow too. This idea that your friends can hold onto things for you. And I thought, wow, that’s really how you do sharding of data. That’s multiple backups, off-site backups, the whole 3-2-1 plan in one go. And then there was the perspective of putting it onto devices with Flutter, and on servers with Rust.

Sammy: Right, Flutter. You’ve written your own implementation of Willow in Dart. How does that relate to our Rust implementation?

Sarah: Rust is compiled to assembly ahead of time. That makes Rust very powerful for bare metal engineering. But it’s not actually all that portable. When I wanted to create Ourcana, I thought about every platform. I thought about iOS, Android, Windows, Mac, Linux, web, and I realized only really Flutter and Dart can do this. Flutter is the SDK, Dart is the underlying language that runs on its virtual machine.

Sarah: Dart, frankly, is very slow. It’s like JavaScript, perhaps even worse. Where it pays off is that it lets you run the same code everywhere in a portable way. And I knew that if I could match the Rust implementation over the wire in a bit-perfect way, I would know that I’d gotten the Dart implementation right.

Sarah: So to me, the Rust code is something that you run on a big node that is always on, always listening maybe. But it’s a service. Flutter and Dart are for clients, clients that wake up, do something, and then you put your phone down and you go about your life. That, to me, is the difference. It’s a trade-off between performance and scalability on the one side, and highly portable client use on the other.

Sarah: And I’ll mention that I plan to release the Willow Dart submodule, the client I have that runs the WTP draft spec, as open source. I want to make sure that’s available to everybody so they can also interop with this cool Rust server I’ve dropped onto the internet.

Sammy: Ourcana is something that people are using already, isn’t it?

Sarah: Yes. I don’t have analytics in the app as a privacy decision. But as far as I can estimate from Discord users and app installs, we’re looking at 9,000 users, maybe even 10,000. I haven’t even counted web. Web is the other big platform, most people just sort of go to ourcana.space, hit Enter, start using it.

Sammy: Fantastic. So, we can actually put the headline “10,000 Willow users!!!” at the top of this interview!

Sarah: Well I’m hoping to get the update with all the Willow stuff out on Friday. I can’t wait to ship something functional to users based on this tech. I’m so excited. I’ve been developing like crazy, just testing, testing, breaking things a lot.

Sammy: Well, I think I can speak for Aljoscha as well when I say that it’s extremely exciting for us to see these designs used in the real world. And not for a weapons platform or something, you know? Actually something nice.

Sarah: Well, I mean, for me, it’s, you know, I would like to securely communicate with my friends and loved ones. And I think that’s what it’s about.

A drawing of Aljoscha, fingers interlaced, with short hair!

I had quite the productive week! The fuzz test I mentioned last week for exterminating bugs in the Rust Bab implementation? It doesn’t crash anymore. It was almost a bit anticlimactic: I expected right label omission to be the trickiest part, so I left that for the end. Left label omission gave me some trouble first. Once I fixed that, I turned on right label omission in the fuzzer, but everything continued working. Looks like I actually got those parts right immediately. Well, I won’t complain. The new code is released as bab_rs 0.6.0. Next up: implementing a backend that can store multiple non-overlapping subslices of the same string.

But that is not all: in working on the sneakerweb, we decided to revisit the Willow Drop Format specification. In a nutshell: the old version of the spec would first encode the total number of entries in the drop, followed by the concatenation of the encodings of those drops. Analogously, for each entry, it would first encode the total number of payload slices for that entry, followed by the encodings of these slices. This sounds fairly obvious, but it comes with a problem: when the data store is modified concurrently to creating a drop, the number of entries (or the number of payload subslices of some entry) might be invalidated, resulting in an invalid drop. This makes drop creation fallible, not to mention somewhat more tricky to implement.

So I changed it to a less-upfront-more-streaming-friendly encoding approach where each entry (and each subslice) simply indicates whether it is the final one or not. Basically what I was ranting about for variable length integer encodings, except that here we now chose the less efficient but more flexible approach.

This decision was not easy. Sacrificing efficiency hurts. Not only is an upfront encoding slightly more compact, but also a decoding process can allocate resources more efficiently when it knows these counts in advance. On the other hand, optimisations based on these counts would also be a bit of a footgun, because the counts cannot actually be trusted. A malicious drop might state that it has a large quantity of entries, so that the decoder would allocate vast amounts of resources. So in some sense, the new design prevents brittle implementations.

But the main argument that convinced me to go with the new approach was an observation that Sarah made: if you do not need to specify the entry count upfront, then you can create drops in a completely streaming fashion. Instead of writing a drop to a file, you could write to a TCP connection. You would never set the this-is-the-final-entry flag, so you could always continue appending new entries. Bam, instant one-directional streaming sync for Willow! Sammy immediately coined the term “dripdrop” for this usage of the drop format.

I’m particularly interested in this usage because the WTP currently lets the client stream changes to the server. This frankly does not fit well into the pull-based WTP, but I feared that the WTP would have been a lot less useful without clients being able to proactively place their data on a server. Now, this could be achieved by dripdropping instead! So there is a possible future where I get to remove roughly a third of the WTP spec, because dripdropping achieves the same goal. Exciting!

So I ended up tweaking the spec with a specific eye on making dripdropping more efficient. There are some neat changes that effectively allow sending verifiable slice streams for payloads, whereas before drops contained only the bare minimum of Bab verification metadata (because drops are meant to be ingested in one go, not incrementally, after all). You could already include incremental verification metadata in the old format, but it would have been fairly inefficient. The new spec makes this scenario a whole lot more efficient to encode.

The drop format is still very much about working with static data sets in bulk, this hasn’t changed. It just so happens that the new encoding is also convenient and efficient for dripdropping. What a happy coincidence!

And finally: Sammy, next Blossoquest update when?!

~Aljoscha

2026Week 2229 May

“wormy and blossomy”

An illustration of a gelateria. Sammy sits out front, slumped in a chair and fanning herself. A little mushroom guy walks out of the gelateria, very pleased with himself. Aljoscha is inside, ordering lots of icecream. A helpful cat is serving him.

We're listening to...

Halcali
Originally released in 2003, this song is pure summer vibes two decades later. That the music video is vaguely Christmas themed troubles me.
Broadcast
I somehow hadn’t heard of Broadcast before. Listening to this album makes me wonder whether I would have started listening to non-classical music earlier in my life if I had been exposed to any contemporary music beyond mainstream radio. Would young Aljoscha have enjoyed this, or would the snob have dismissed it?
A drawing of Aljoscha, fingers interlaced, with short hair!

Just a short update from me this week, I did some teaching (prep) and need to get some more done later today.

There has been a nice flurry of activity on the Discord recently. Nothing quite as motivating to work on Bab as having to write multiple times that the WTP isn’t ready yet and blocked by me wrapping up the remaining Bab work. The current state: I have this massive fuzz test that writes data into storage, interrupting at random points, and exercising with various optimisation options. When I disable all optimisations, it cannot find crashes any longer. Some of the optimisations still don’t work though, and I’ve reached the point where I might have to revisit the actual verification logic instead of simply fixing some silly off-by-one errors. So I’m really glad that I did this whole writeup on the architecture of this nontrivial piece of code that I wrote sufficiently long ago that I forgot most of it already.

~Aljoscha

A drawing of Sammy, with a bandana and sunglasses on, and a swollen chin.

Welcome to the new Summer’26 edition of worm-blossom.org! I was starting to feel like the site was calcifying around its previous look, and instigated change for the sake of (seasonal) change.

But! Something I dislike is when old content is re-poured into new containers. So previous weekly updates retain their original colour scheme and illustrations. Our publishing system Macromania is very good at accommodating arbitrariness like this.

So what’s planned for the summer?

DWeb Camp planning is coming along. The p2p track received many exciting submissions, and I think we’ll be able to put on a great show. And it’s only a month and a bit away! Both Aljoscha and I will be there, we’ll do a talk on Willow, and there’s also something else we’d like to showcase…

sneakerweb is our new experiment in peer-to-peer publishing, a squirrelly little sibling to the world wide web.

On the world wide web, data travels immediately but through countless intermediaries, each with their own policies and failure points. On the sneakerweb, website data travels gradually and directly from user to user.

On the world wide web, data is stored on servers which are always connected and always powered on. On the sneakerweb, website data is stored on user devices.

On the world wide web, access to data is addressed by domain names which are scarce, rented, and even seized. On the sneakerweb anyone can claim their own (admittedly far less memorable) domain forever.

It’s called the sneakerweb because data travels over the sneakernet, "an informal term for the transfer of electronic information by physically moving media” (wikipedia).

At least for now, sneakerweb is a command line tool that will:

  • claim sneakerweb domains
  • publish websites to those domains
  • serve the sneakerweb to an ordinary web browser
  • import/export website data from files obtained from others
  • block unwanted data
It’s all built on Willow and we’re aiming to demo it at Dwebcamp!

~sammy

2026Week 2122 May

“consistent in-universe rules for how glasses work”

A drawing of Aljoscha, fingers interlaced, with short hair!

This is the final week showcasing material from Sammy’s archive. I saved some of my favourites for this one =)

~Aljoscha

A drawing of Sammy, with a bandana and sunglasses on, and a swollen chin.

Dear readers, I am kind of back. I had surgery a few weeks ago, and until today you could always hear the faint warning beep of having only a single heart container filled. Today I have just enough hearts to write a short editorial for famous music blog worm-blossom.org!

Outside of sleeping, eating, napping, and reading, I haven’t been up to much. Certainly no computering, or even drawing. But I’ve been walking. This is walking for the sake of it, without a purpose (getting bread) or a destination (my studio). I just wander around the neighbourhood, ambling down different streets, taking inefficient detours. I love how automatic walking is, you put one foot in front of the other and pretty soon they don’t need your supervision anymore. And then your mind starts wandering along with them, peeking down inner side streets.

My walks right now aren’t like how I imagine Aljoscha’s are. There are no ideas or insights, only impulses, quiet excitement, images. Maybe, someday, they’ll assemble into something which can be used, but for now all I have (all I need?) is forward propulsion into the next week.

~sammy

A drawing of Aljoscha, fingers interlaced, with short hair!

Yay, Sammy is (kind of) back! Though I feel like she needs to catch up with a couple of changes: clearly worm-blossom.org is not a popular music blog but a popular data structure blog. So let me tell you more about data structures! (I’m sorry, but this is what I thought about this week...)

  • I think I made a mistake with the two-dimensional skip-list of last week: the links from top-right and bottom-left sublists back to the pivot are not needed. The variant without them should actually be the default. In particular, this is not connected to the two-kinds-of-edges observation about one-dimensional skip-lists, I got something mixed up there. And while removing these edges destroys unilateral connectivity, you gain the property that there is a unique path from the "least" vertex to any other vertex.
  • I made some progress to generalising G-trees to the multidimensional setting. In the one-dimensional setting, you can pair up each item in a G-node with a lesser subtree, and then you add to the G-node the single subtree of items of lesser rank that are greater than the greatest item in the G-node. In the two-dimensional case, things are more tricky. You can pair items with a bottom-left and a top-left subtree of lesser-ranked items, and this works fine. But what do you do about the items that lie to the right of any items of the G-node? If you add a single right-subtree to the G-node, you end up not generalising the 2d-zip-trees; to do that you'd need to also split up those right items along the y axis. But then you get a number of right subtrees scaling with the number of (terminal) items in the G-node. And how do you (elegantly) store this variable number of subtrees?
  • A more fun observation regarding multidimensional G-trees: you get 1d-zip-trees by instantiating 1d-G-trees with sorted linked lists as the inner set datastructure. And it just so happens that sorted lists are what the layers of a skip-list are. And for the multidimensional case, it looks very much like you need to indeed instantiate k-dimensional G-trees with k-dimensional skip-list-layers to obtain the k-dimensional zip-trees. Well, assuming the whole right-subtree problem had been solved =S. But this feels very, very neat to me!
  • Let me get out one more point that requires good understanding of G-trees: I came up with a new interesting instantiation, and a painfully-obvious-in-retrospect one at that: use almost-complete binary search trees as the inner set data structure. Where zip-zip-trees try reduce the height penalty of having successive items of equal rank (i.e., with no item of higher rank in between them), this instantiation simply defines the problem away by always using a minimal-height tree to arrange such sequences. Maintaining these is inefficient (you cannot do insertions and deletions on almost-complete binary search trees in logarithmic time), but we know that G-nodes are small with high probability, so we do not need to care. This instantiation is still history-independent (because almost-complete trees are required to “fill the lowest layer from the left”, they are uniquely defined for any item set — and if I'm missing something here, then you can always redefine them in a history-independent way). The only drawback I see, aside from somewhat complicated insertion and deletion algorithms, is that this instantiation is not clamping-invariant.
  • Finally, a short observation on skip-lists: the same generalisation that goes from zip-trees to G-trees also works for skip-lists. And it is a lot more simple and obvious for skip-lists even: you keep the inter-layer connections, but instead of using sorted linked lists as individual layers, you use arbitrary set data structures as the individual layers. I've seen some papers that do this with specific replacement data structures, but none that actually works out generalised algorithms, and the algorithms that the underlying set data structure has to provide. This should be fairly simple though; certainly less elaborate then the G-tree case. And from this, you get at least one immediately interesting data structure: the skip-skip-list, which uses skip-lists for its layers (fully analogous (and properly isomorphic to) zip-zip-trees).

I'm starting to think I should do my phd about this sort of stuff, instead of older research that grew out of append-only logs and that I barely care about any more...

~Aljoscha

2026Week 2015 May

“I refuse to use a self-referential motto”

A digital drawing of two people sitting in a stylised tree.
In my headcanon they are sitting in a one-dimensional zip-tree.
A drawing of Aljoscha, fingers interlaced, with short hair!

Like last week, the collection of images comes from Sammy’s archive. Except for the hand-drawn sketches of data structures, those are mine. May the contrast bring out the quality of Sammy’s work even more.

~Aljoscha

A drawing of Aljoscha, fingers interlaced, with short hair!

I made a thing: a visual guide to zip-trees.

Every once in a while, I think about data structures. Efficient implementations of the three-dimensional data model of Willow require three-dimensional data structures. And those are tricky: whereas the one-dimensional case admits various search trees that allow for both querying and updating in logarithmic time, the multi-dimensional case is more difficult (here’s a rough introduction). There are problems with all existing data structures, so I have pondered a generalisation of zip-trees to three dimensions for a while now.

This week, I finally worked out the last missing algorithms. As part of my note taking I developed a graphical notation that could record the algorithms a lot more concisely than pseudocode can:

A hand-drawn diagram representing an algorithm to split a two-dimensional search tree at a given point.
Splitting a two-dimensional zip-tree rooted in R at point P. If you know what the various elements mean, this drawing (almost) completely describes the algorithm.

While I used this notation only as an aid in developing the algorithms and as a means of recording them, I naturally started to wonder whether the notation contained enough information to fully replace pseudo-code in a proper writeup. I need to share my algorithms for them to be useful to anyone, after all, but writing pseudocode is a pain. In particular, when it comes to symmetries and rotations, pseudocode is either verbose or littered with difficult-to-grok parameters. The graphic notation, in contrast, handles rotations and symmetries gracefully.

So I started refining the notation a bit: properly handling empty trees, teasing apart individual diagrams into sequential steps, incorporating item ranks, etc. Additionally, I started wondering about generating the diagrams instead of drawing them by hand. And when I was somewhat confident that all of this could possibly work and even be useful, I decided to create a proof-of-concept document based on one-dimensional zip-trees. And that is the thing I made this week.

~Aljoscha

Before the graphic-notation-frenzy took over my week, I made some additional progress on multi-dimensional data structures. I did a complexity analysis of the multi-dimensional zip-tree algorithms. In a tree of logarithmic height, query times are logarithmic. Update times are logarithmic on average, but they can be in O(n^0.792) (master theorem for 3 subproblems of size n / 4) in the worst case. This is not great, but probably good enough maybe? At the very least, there are such results in the first place, whereas R-trees and their ilk typically just hope for the best.

Okay, this update is now officially turning into a data structure fiesta, because I have one more insight to share, but it takes a bit of preliminary work. Let me start with a sketch of my definition of two-dimensional zip-trees.

Given a set of points in a two-dimensional space, and a function from those points to ranks (natural numbers), the 2d-zip-tree on the set is a 4-ary tree, defined recursively: the root is the point of maximal rank (I’ll simply write "maximal rank", implicitly assuming that there is some deterministic tie-breaking in place in case of duplicate ranks).

Next, partition the remaining points into four sets:

  • bottom-left (less than the root in both dimensions),
  • top-left (less that the root in the first dimension, greater than the root in the second dimension),
  • bottom-right (greater that the root in the first dimension, less than the root in the second dimension), and
  • and top-right (greater than the root in both dimensions).

Recursively build the 2d-zip-trees for these sets, and use the resulting trees as the bottom-left, top-left, bottom-right, and top-right children of the root.

A hand-drawn diagram representing the recursive nature of 2d-skip-trees.
Two-dimensional search trees, with rectangles indicating subtrees.

This definition directly generalises the corresponding one-dimensional definition.

I want to sketch a definition of two-dimensional skip lists, but to get there we first need to take a look at one-dimensional skip-lists. I’ll assume familiarity with the concept, but want to present an alternate, non-standard definition. This definition makes the isomorphism between skip-lists and zip-trees much more obvious than the standard definition.

Given a set of points with associated ranks, we can define the bottommost layer of the skip-list recursively: select the point of maximal rank, call it the pivot. Partition the remaining points into two sets: those less than the pivot, and those greater than the pivot. Recursively construct the bottommost skip-list layer for each of these sets. Add an edge from the greatest point in the lesser recursive construction to the pivot, and add an edge from the pivot to the least point in the greater recursive construction.

A hand-drawn diagram representing the recursive nature of a single skip-list layer.
A single layer of a skip-list, constructed recursively. This surfaces a hierarchical nature that is obscured by the traditional definition.

For the higher layers, you proceed identically, except that you filter out items whose rank is less than the layer count. Then you add inter-layer links as usual.

This definition might seem overly complicated, given that it simply results in layers that are ascendingly sorted linked lists. But it has two advantages: first, it is utterly obvious how the definition relates to that of zip-trees, and second it generalises nicely to higher dimensionalities.

From the preceeding definition of one-dimensional skip-lists, we can easily get to a definition of two-dimensional skip-lists. And the connection to two-dimensional zip-trees will be just as obvious as in the one-dimensional case.

Given a set of points in a two-dimensional space, with associated ranks, the bottommost layer of the 2d-skip-list on the set is defined recursively: the pivot is the point of maximal rank.

Next, partition the remaining points into four sets:

  • bottom-left (less than the pivot in both dimensions),
  • top-left (less that the pivot in the first dimension, greater than the pivot in the second dimension),
  • bottom-right (greater that the pivot in the first dimension, less than the pivot in the second dimension), and
  • top-right (greater than the pivot in both dimensions).

Recursively build the bottommost 2d-skip-list layers for these sets. Add an edge from the "greatest" (defined later) element of the bottom-left one to the pivot, and add an edge from the pivot to the "least" (defined later) element of the top-right. Add an edge from the pivot to the "least" elements of the top-left and bottom-right ones, and add edges from the "greatest" element of the top-left and the bottom-right one to the pivot.

The "least" element in a subset is defined as follows: throw away all elements that are greater in both dimensions than some other element in the subset. Of the remaining elements, the one of least rank is the "least" element. Analogously, the "greatest" element in a subset is defined as follows: throw away all elements that are less in both dimensions than some other element in the subset. Of the remaining elements, the one of least rank is the "greatest" element.

A hand-drawn diagram representing the recursive nature of a single 2d-skip-list layer.
A single layer of a 2d-skip-list, constructed recursively. Unlike the one-dimensional case, the hierarchical nature is required to make sense of the construction, because ranks influence tie-breaking in the choice for "least" and "greatest" elements.

And then you do the usual business of constructing sparser layers and connecting the layers. The resulting data structure supports efficient range queries (unlike Nickerson’s ad-hoc constructions), and is isomorphic to two-dimensional zip-trees.

The structure of the resulting graphs is quite neat: effectively, you get a linked list from the bottom-left to the top-right, with choices between incomparable successor candidates tie-broken by rank. This main diagonal doesn’t contain all items, but it sprouts off smaller diagonals, which go from the bottom-left to the top-right of smaller quadrants. And those have smaller sub-diagonals themselves, and so on. And because those sub-diagonals are connected back to the mid-point of their super-diagonal, you get the expected connectivity properties of one-dimensional skip-lists: for any two vertices in the data structure, there is a directed path from one to the other.

For higher dimensionalities, the pattern generalises nicely: you have a primary diagonal from the least to the greatest element, and then a bunch of sub-diagonals, each again with their sub-diagonals.

A final fun fact about two-dimensional skip-lists: if you do not connect sub-lists back to the pivot of their super-list, you loose the fact that there's a directed path between any two vertices, but every vertex is still reachable from the least (bottom-left-most, ties broken by minimal rank) vertex in the list. In fact, searching for items starting from there (and also doing range queries, I think), still works. This is analogous to my observation about two kinds of edges in one-dimensional skip-lists in Figure 18 of the G-tree paper.

2026Week 1908 May

“un jour je serai de retour près de toi”

A drawing of Aljoscha, fingers interlaced, with short hair!

Sammy is taking some weeks off, so I will be publishing the next few updates on my own. Unlike the last time, Sammy left me with quite a collection of drawings to spruce up the place in the coming weeks.

~Aljoscha

I lack context for most of these images. Some are “drawings” and some are “dreams”.

Some are a quite old, others more recent, and I have little idea which are which.

What I do know is that I am giddy to share these, despite Sammy probably groaning about my curatorial and presentational choices.

And I remain continually grateful that a person with this sort of creativity and skill is choosing to collaborate with me.

A drawing of Aljoscha, fingers interlaced, with short hair!

Speaking of collaborating on things: there's all this software stuff we are working on. I am still rather slow when it comes to making progress. Still have a cough that refuses to disappear, and still have a bit of a hard time focussing on programming.

The other day, I had a surreal experience of alienation. I opened my text editor, looked at the code, and for a few moments I saw that code as if I didn't know how to program. A jumble of hostile characters — all sorts of parentheses, isolated words of English, random punctuation; and everything in a stark monospace font, with high-contrast colours assigned seemingly at random. Utterly inhuman and hostile.

And then things started to fall into place again. The parentheses are balanced. The indentation gives high-level structure. The fragments of English are terse commands. Commands whose semantics I know.

And yet, I was unable to put everything together into full understanding. Some of the functions do so much that even a single line of code conveys more information than I can grasp at a single moment. And there are 50 of these lines on the screen at a time. I have a rough picture of what the code should do, but I know for certain that the actual behaviour deviates from my intentions. The computer has no notion of a rough picture, it must work on a level of detail that I am fundamentally unable to handle. So our exchange will necessarily be fraught with frustration.

This experience has passed, and I am now in the process of making the high-level rough picture of the Bab APIs more precise. Throwing a fuzzer at the thing, finding integer overflows, documenting and asserting invariants to turn these low-level failures into workable semantic concepts. Not the most enjoyable kind of work, but it has to happen. Perhaps someone else would be faster with this than I am, but that line of thought doesn't actually get any work done, so...

~Aljoscha

We're listening to...

Einstürzende Neubauten

Ten internet points to anyone who knows about the connection between this band and this week’s motto.

Call for Pixel Art

A worm-blossom project we have not really posted about here before is the Seasonal Clock. Over on Secure Scuttlebutt, Gergely Polonkai has started a neat project: porting the seasonal clock to the Pebble e-paper watches. Quoting their post:

I’m very bad at making anything that even borders artistic. If anyone around here has the time to draw all 24 emojis in 32×32 and 48×48 sizes, both in 1bit and using the Pebble palette, I’d be very grateful.

I’ll release it as open source (as many other watchfaces out there), but I’m willing to pay a commission fee to whoever picks up this task.

Here’s a quick draft of the face as it looks in my head (just with your emoji art and better text 😁):

So if anyone feels like exercising their pixel art skills, please reach out. Gergely is on the worm-blossom Discord, and I can also relay messages via Scuttlebutt.

2026Week 1801 May

“I will narrativise until proved otherwise”

A four panel comic titled Blossoquest.
      
      In the first panel, the Bottleneck looks at Bonnet. "Why me?" he asks. "Aw stop it now" she replies.
      
      "I know how you broke everyone out of Bigburg", she says. In this second panel, we see the Bottleneck lifting his head off of his shoulders, light emanating from his neck. He stoands surrounded by other figures, a huge hole broken into a brick wall.
      
      In the third panel, the Bottleneck ruminates to himself. "Bigburg...". In the background, we see distorted, angry, sad faces.
      
      In the fourth panel, we see the giant buildingi in the horizon. Bonnet says "Where that train's headed makes Bigburg look like... Smallburg! You see why I'm talking to you now?".
A teeny megaphone blaring out noise

bab-hash.org is online!

We've launched a new website for Bab over at bab-hash.org. You don't know what Bab is? Go over to the website and find out! It's informative and our cutest website yet.

bab_rs is particularly unique as a kind of 'batteries-included' API: it goes beyond creating digests, and provides APIs for persisting Bab-annotated data to disk. We've been using it in Willow's persistent store implementation and it's something we're very proud to share.

A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

This week I wrapped up my work on bab-hash.org! I’m very happy with the cuteness of this website, not just in how it looks, but also in how succinctly and sweetly it conveys the purpose of Bab.

I always start out with drawing logos and visual motifs.

With all due respect to the BLAKE3 fans out there, it’s not easy to make an engaging website about a family of hashing functions. What’s this stuff for? Why should anyone care?

We designed Bab for peer-to-peer data exchange. This is a context where we expect interrupted transfers, we expect malicious peers, and we expect to be connected to many peers at once. These scenarios lead to real problems and opportunities for ordinary people: nobody wants to have to start over an interrupted download; nobody wants to unwittingly store (or even propagate) illicit data; and everybody wants fast downloads.

These are very tangible applications for something very abstract, and that’s exactly what I wanted to foreground on Bab’s website. I started thinking about each of these settings as a transfer, with a source on one side and our user on the other, with little comic panels no less!

Or I could represent these scenarios as familiar download windows with styled progress bars. A very familiar UI.

Some marker sketches of both the connection comics and dialog boxes.

Which was better…? In the end I decided to do both.

While styling the dialog windows I wanted to play with a classic computer feeling, so I plonked in a pixel typeface designed by my friend Robin Mientjes, a typeface which was originally designed for a webcomic I made a long time ago. The version used on bab-hash.org is actually a hugely expanded version of that typeface, and it’s so nice to have the extra symbols, italics, and family variants!

The final combination.

The pixellated aesthetic of the download windows then natually took over everything else. I wanted a fuzzy, hand-drawn feeling despite how low-res the artwork is, so I drew all of the artwork using a stylus with a desktop pixel art editor called Dottie.

The setup of mirroring a desktop app to the tablet so I could use the stylus was a bit convoluted, but got the job done.

For me the real star of the website remains Aljoscha’s specification. It has wonderful interactive diagrams which you can step through, and which use colour in such a smart way. It was invaluable in figuring out how to approach this site’s design.

And of course, the site is totally static, and built with Macromania. I don’t even think about my usage of Macromania anymore, it’s become such an everyday tool for me. We really need to release it someday.

Speaking of someday, I'm going on a short break! I'll be back in a few weeks. I've provided Aljoscha with materials from my archives to decorate the place with while I'm gone.

~sammy

A drawing of Aljoscha, fingers interlaced, with short hair!

Whereas Sammy's editorial represents a burst of concentrated productivity, I am still in a phase of intake and reflection. I'm still nursing a particularly persistent cold (I'll have to pass on singing in the Berliner Philharmonie this weekend :[), but at least my mind is slowly drifting back to programming again. Optimised Bab slice streaming won't test itself, after all. And then there's wrapping up the Bab grant by implementing a storage backend that can store multiple subslices of a single string, and append to those slices concurrently. We now have a website that promises parallel downloads from multiple peers, after all, so the implementation better catch up.

Here's some music I improvised today:

Compared to the miniatures from last week, I think you can tell that I stabilised quite a bit. So I'm optimistic I'll get some worm-blossom work done in the near future. And with Sammy taking a break, I better should.

~Aljoscha

2026Week 1724 Apr

“Fun? What's that? Let me find a paper for you”

A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

This week I have been working on the website for Bab. This effort has finally taken the turn where the initial psychological lift has been done and now I am looking forward to each session of working on it (i.e. I have reached the point where I am making drawings for it).

I've also taken on the duty of curating and stewarding the p2p tent at this year's DWeb camp in Alte Hölle. If you're interesting in presenting, holding, or just meeting up, please get in touch.

There is no comic this week, but there is a terrifying dependency chart skill tree for our Willow related efforts.

Slight feeling of biting off more than I can chew, but I've bitten now!!!

~sammy

A drawing of Aljoscha, fingers interlaced, with short hair!

These past weeks have been a confluence of stuff happening. I didn't manage to write a single line of code this week. So, uhm, have some music at least.

Also, it looks like looping beepbox miniatures have become my default mode of self-expression. Huh...

~Aljoscha

2026Week 1617 Apr

“you started reading William Blake, it is time to seek help”

A four panel comic, titled 'Blossoquest'.

In the first panel, we see a train moving across the horizon. Above it is the text "That train's movin' fast".

In the second panel, we see a train track leading into the distance. A giant, wide structure stands on the horizon, a cloud looming over it. In the cloud is the text "and it's goin' nowhere good".

In the third panel, we see the Bottleneck looking down on Bonnet. She asks him: "Will you stay here, Bottleneck?".

"Or will you be with us when we get there?" she asks him in the final panel.
A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

Late last year I was sitting in on a session about a shutdown resistant chat app. We were told that the problem with other shutdown resistant chat apps is that their experience sucks, and so to avoid that this particular app was going to copy the interface of Signal as closely as possible. A few minutes later the peer-to-peer aspect of the app was introduced, as was the idea that a message sent by a user a week ago might only arrive today. I asked them how they squared this behaviour with their stated goal of being like Signal, where messages arrive very soon after being sent. No answer was given.

Chat interfaces create the expectation of messages arriving in a orderly, timely way. But peer-to-peer systems are meant to handle networks where messages are possibly exchanged long after they’ve been authored, and where users may have not seen each other’s messages and aren’t aware of what they’ve missed. Is it wise to re-use interfaces built around a completely different type of network?

So I hosted a very enjoyable session in Prague about rethinking interfaces for peer-to-peer applications, specifically resisting the dominant form of the chat app.

Every device in a peer-to-peer network will have a different ‘view’ of that network. Are there precedents for users understanding that they might have a different view of the same ‘thing’? Arguably email has set this precedent. Can we piggyback on the rich history of email client design?

There are interfaces which can ‘collapse’ many messages into an interface where order doesn’t matter: emoji reactions are like this. Many people may send reactions at different times, but they are reduced to a single count where order is hidden. Can we do something similar for more complex message types?

Different views of a network raise the question of archival: what’s the canonic state of a network? Does this imply that peer-to-peer systems are inherently ahistoric and thus resistant to archival? How can we take advantage of that?

By the end of the session, we had two wonderful emblems for the opposing structures of centralised and peer-to-peer messaging:

The linear, causal, ordered ledger of the centralised system; and the overlapping, scattered, unlinked pinboard of the peer-to-peer system.

~sammy

A drawing of Aljoscha, fingers interlaced, with short hair!

~Aljoscha