worm-blossom

worm-blossom

The endeavours of Aljoscha Meyer and Sammy Gwilym.

2026Week 316 Jan

“it’s really just a hassle for *us*”

A four panel comic.
        
        In the first panel, Sammy look serious. The text above reads: "sammy thought about this. The mines were supposed to be metaphorical, and thus empty.". Aljoscha looks confused in the background and Sammy thinks to herself.
        
        In the second panel, we see a wider view of the mines. The text reads: "More importantly, it was meant to be free of the moral implications of its tacit extractivism.
        
        In the third panel, Sammy is set in pale green against a spiralling background. The text below her reads: "But don't the stories we tell say so much about the way we see the world? What we wish we could do without guilt? Sammy asked herself what this choice of setting said about her inner world!!!".
        
        In the final panel, Aljoscha reappears next to Sammy and says: "Hey. I took a look. I think there's a child trapped down here". Sammy winces.

We're listening to...

Image
Sonic Youth

I (Aljoscha) rarely listen to music, but this song popped into my head multiple times this week. So I listened to myself (and the song) and tried to unwind a bit. I even had a cup of hot chocolate. This is a surprisingly good song for a cup of hot chocolate.

Image
Burial

The star here is Archangel, composed to evoke a walk home through the city after a night out.

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 my attention was once again fully occupied by the upcoming FOSDEM talk. After a run-through of a draft slide deck this morning, I felt dissatisfied, although everything was arguably there. Empty calories.

I am a person driven by dissatisfaction, and I work on things until I am happy with them. Aljoscha has the same malady. There is seemingly no cure. This makes the things we work on take a very long time. This website is a kind of compromise: we blog about our ongoing dissatisfaction on a weekly schedule.

So now I am restructuring the presentation for what is hopefully the final time. I mentioned last week that if you want to learn about what Willow is and how it works, we have a website. But why is Willow like it is? What kind of world made us feel such a thing was necessary? That is the kind of talk I think people will enjoy most.

It will be fun, maybe even insightful. If you have been reading worm-blossom.org for any amount of time, how this presentation turns out will not surprise you in the least.

~sammy

2026Week 209 Jan

“I had another thought while washing the dishes last night”

Another illustration of a underground scene. In the distant background, we see a jagged speech bubble coming from the rear of a cavern, crying "PLEASE". In the foreground, Aljoscha turns to Sammy (who is shuddering). The caption reads "I thought these mines were supposed to be purely metaphorical".
A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

My week was high-intensity but low-worm-blossom as I was single parenting. With the time I had between biking my kids to school through the snow, preparing meals, pleading with them to just open their mouth so I could brush their teeth... I mulled over my FOSDEM presentation.

I got stuck in a 'concept' phase. For me it would be a bit disappointing to just say what Willow is and explain it spec by spec. After all, we have a website for that. This presentation should act as a complement to it. But how? I sat around pondering. Thinking really hard. And guess what: it didn't help.

As soon as I switched to making — even with only a very vague idea — things began to progress. Being able to sketch, respond, tear down, iterate. Share an idea I'm excited about with Aljoscha, and he gets excited too. And now I can't wait to keep working on this thing.

And so the first thing we did was confirm with the devroom organisers that we would be able to play audio over the speakers.

~sammy

We're listening to...

Image
Vega Trails

I heard Closer on the first night of the year, and it sort of perfectly summed up how 2025 had felt for me (sammy).

Image
Michael Lückner

house full of time is just one of those tracks meant for cruising on your bike, gliding past people and lights and painted lines.

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

Rambly, reflective editorial incoming — creativity is fascinating (as is reading McGilchrist).

I was almost completely unable to make any progress coding this week, despite desperately wanting to. But whenever I opened the text editor, I would just be completely disinterested. I’d like to tell myself that I have the cognitive ability and trained skills to be able to work productively even when I’m not into it. But truthfully, the code I did force myself to write is so bad I will probably have to rework it at some point.

I usually struggle with creative output because I judge my own intuition too harshly and want to get things right. This website has been a good exercise in loosening up in that respect. But this week, the problem was of a different nature — I did not care enough to judge at all, leading to pretty bad code I normally would not let myself write. So where ideally there should be a healthy balance of generative creativity and quality control, this week had neither. And forcing things regardless just doesn’t work.

And then, out of nowhere, my brain delivered the solution for how to elegantly implement verification of incoming data streams in Bab. And suddenly, I felt like coding again, and have been happily coding since. This did not happen in time for finishing things by today, so I will detail this implementation approach next week.

Interestingly enough, while I was more or less unable to code, I did do some beepbox pieces I am not unhappy with after the fact — both of which I imagine working nicely in an RPG of some sorts, inspired by watching some Expedition 33 and Final Fantasy VII over the week:

And I did not dread the task of writing an editorial for this week either, despite having little idea what to write about before actually sitting down and starting to type. Not sure whether this says something about myself, or more about differences between writing, music, and coding.

~Aljoscha

2026Week 102 Jan

“agency over purity”

An illustration of a underground scene. In the distant background, we see two silhoutted figures, one looking towards the foreground. Close to us, we see a kind of monitor embedded in the rock, strange green waves on its screen. The caption below the illustration says "Did you hear something?"
A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

It was time to take a pause. In the last week I've spent time with family, ate, took my customary New Year's Eve bath, ate, and started a new journal for 2026. What little worm-blossom related stuff I did was thinking and sketching out my FOSDEM talk, specifically thinking about what you should say to an audience who have just seen twenty presentations on similar projects for the past several hours. Something high energy, controversial, more about vibes than details?

In honesty I am bracing myself for another intense year, perhaps harder than the last. So I am looking for ways to make it easier on myself. I found the practice of trying to make a funny comic each week a challenging one (with questionable results). I envy Aljoscha's free-form noodling in his little musical corner. So I'm going to have a little experiment with some more improvisational visual storytelling.

I hope you took some time to catch your breath over the last few weeks too, let's try our best!

~sammy

A teeny megaphone blaring out noise

No Announcement This Week

For the first time since launching the website, there is no announcement in the weekly update.

Wait a second...

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

Friday arrived so quickly, and I had not written an editorial yet. I have barely thought about the website update at all this week, actually. Does this mean I finally found a healthier attitude toward my work and this website, will I now lead a life of increased inner harmony and peace? Will my output dwindle as a consequence?

Nah, I simply spent most of the time learning and thinking about audio file formats and video file formats, in anticipation of the Cobweb and Rummager.

The number of file formats you would need to support to provide somewhat decent coverage for the media content on the web is intimidating, as is the staggering complexity of the individual file formats. So I have been pondering what a significantly more simple stack could look like. Only one file format per media type (images, sound, video), and simple formats at that.

The file formats would be pure delivery formats, no niceties for editing included. Further, the formats should support both lossy and lossless compression (which, sadly, rules out the Quite Okay Image format and the Quite Okay Audio format), down to extreme measures such as one-bit images or audio. And, ideally, the three formats would have identical or at least similar structure, and reuse the same underlying techniques. A nice challenge would be to fit all three formats (including their shared components) into 400 * 3 = 1200 lines of C code — the reference implementations of QOI and QOA take 400 lines of code each, after all.

A rough starting point for a unified approach to the three different file formats:

  • Regard each media type as a collection of channels, where each channel is a list of integer samples. For images, the four channels are three colour channels (YCbCr, specifically) and the alpha channel, and the one dimension is the row-wise content. For audio, each channel (one for mono, two for stereo, possibly more for more fancy stuff) is a stream of PCM data. For video, one channel encodes time-warying images, and the other channels are audio channels.
  • Channels are interleaved. Each channel has an independent frequency. For example, a video could encode images at 30 Hz but audio at 44.1 kHz. And an image could use lower frequencies for the colour channels (aka chroma subsampling).
  • Before encoding, quantise all signals to a channel-specific bitwidth. This is the (only) part where lossy compression enters the picture. The bitwidth of a channel can even be zero — say, for an alpha channel that has the same value everywhere. The simplemost quantisation scheme is simply to right-shift each sample by some amount, though for audio it is tempting to go with a non-linear quantisation scheme (because using fewer bits for high frequencies compared to low frequencies neatly exploits properties of human hearing).
  • On the quantised signals, use some form of Lempel-Ziv compression. When a value cannot be reconstructed from the dictionary, it is not encoded verbatim, however, but as a residual from a prediction (see next bullet point).
  • Each file format has a (different kind of) predictor, which tries to predict the next sample from the previous samples. We never encode samples verbatim, instead we encode the difference to the predicted value (the residual). With a good predictor, this means we will usually encode only small numbers, a property we can then exploit for more compact encodings. For images, the prediction is probably simply the previous sample. For sound, I’d tend toward reusing the LMS of the QOA format. And for video image frames, the pixel of each frame would be predicted as being the pixel at the same position of the previous frame, offset by a frame-specific x and y value (i.e., simple motion compensation, I'd also consider simple forms of Dolly and camera rotations if they admit simple integer-based implementations).
  • Finally, encode the residuals or the LZ compression stuff nicely and compactly. The easiest approach here would be to define a bit-based encoding and simply encode every sample separately, but it would also be nice to have a byte-based encoding — and grouping multiple samples together can be more efficient than encoding them individually. So there is a lot of design space here. Further, there are concerns around seeking: how can you efficiently jump to an arbitrary point of time in a time-varying signal? First, there probably needs to be a balance between absolutely encoded samples and relatively encoded samples. You'd use the absolute samples for seeking, and then do a linear scan across the remaining relative samples. The original anti-monotone binary linking scheme would be a really neat way for providing (only a bounded number of) forward-pointers between absolute samples. But still, with interleaved channels of different frequencies and bit-depths, and LZ compression, finding a good mechanism here is tricky.

There are further concerns such as container formats, and accessibility features like subtitles. But this would be the general idea for building an image format, an audio format, and a video format that all work in essentially the same way. It might not be unrealistic to use identical subsampling, quantisation, LZ compression and final encoding steps for all three formats. Aside from working out the specifics, the big remaining question is whether the lossless compression yields acceptable file size reductions compared to raw data, and whether the quantisation step (which is the only form of lossy compression in the whole approach) yields sufficient quality for the selected degree of compression. The two Quite Okay File formats make me fairly optimistic in that regard, however.

~Aljoscha

2025Week 5226 Dec

“potential”

Happy new year from worm-blossom.org!

An illustration of Aljoscha and Sam in mining gear, deep underground, surrounded by lava yet looking up towards the viewer with confidence.
A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

About two years ago, we published willowprotocol.org, and our server took a million requests on the first day. Soon we were getting emails from companies, universities, weird individuals, and it felt like we were really making something that made people sit up and take notice. We received our funding to build a rust implementation of Willow, and then… things stalled. It got quieter and quieter, until I imagine to the outside it seemed like nothing was happening at all. A lot did happen, of course, just not in a way we’d choose to have things happen again.

The end of 2025 brings the close to a weird era of willow, an era whose weirdness I’d like to reflect upon as I think I can recognise some of the ways it went differently than we’d hoped.

Our first mistake was maximalism. Willow Confidential Sync was the newest member of the Willow family, with the most novelty and flash. Range-based set reconciliation. Privately determining common interests between peers. Teeny tiny privacy-preserving message encodings.

As a poster child of privacy and efficiency, Confidential Sync radiated complexity onto everything around it. Willow storage needed to be able to efficiently fingerprint and partition ranges of local data for range-based set reconciliation. Storage needed special push-centric APIs so that new data could be broadcast to peers. Ufotofu needed a menagerie of encoding traits to be able to accommodate the private encodings which Confidential Sync uses to mitigate man-in-the-middle attacks.

We started to recognise the weight of this complexity early on, and specified the Willow Drop Format just a few months after the launch of willowprotocol.org. It’s a vastly simpler spec which asks far less of its dependencies. But at that point our funding goals and our destination were locked in: Confidential Sync was where we were going. And that path led us straight into the swamp.

At the same time, we were refactoring willowprotocol.org to use a new version of Macromania, the system we use to author the entire site (and this one!). Aljoscha made a big effort porting all the existing content to the new version, and it was so much nicer to author pages with that we couldn’t really go back to doing it the old way. And now we had a branch of the website with the new, not-quite-ready system and new content all mixed together. This interdependency congealed into a kind of seal which stopped us from making small updates to willowprotocol.orguntil everything was ready in one giant mega-update.

So willowprotocol.org was not getting updates. Which not only made it look like we were inactive to the outside world, but it also hurt our morale because we felt like we were digging into a bottomless pit with no recognition for our efforts. The longer the silence became, the worse our perfectionism got: we wanted to have something to show for ourselves after that long period of silence.

So we found ourselves working in frustrated silence amidst a myriad of interlocking dependencies. It was gruelling. In a struggle to keep the project on track, I ended up breaking personal connections which I treasured. There was also life outside of Willow which insisted on happening.

So what did we learn? Avoid complexity. Deliver frequently. Focus on doing as little possible to achieve your goal. Communicate often. How often have you heard these things? How many dozens of blog posts and presentations have you seen stressing these very points ad nauseam? I had certainly heard those hard-won lessons countless times — but found out I had to win them the hard way for myself, nevertheless.

I also have anti-takeaways: we were led into a swamp, but it was a very fruitful swamp. We used lots of time to make difficult, foundational design decisions, decisions which are already giving us increasingly stable foundations to work upon. It was long and tedious but it was also instructive, and we have the opportunity to actually use that experience.

worm-blossom.org is our antithesis to the era described in this editorial: frequent, ad lib, joyous. That’s what I want to bring more of to 2026!

~sammy

A teeny megaphone blaring out noise

Basic Store Trait

We have published version 0.6.1 of the willow-data-model crate. This update marks the beginning of our implementation of proper Willow storage, adding a basic trait for abstracting over stores, and a non-persistent MemoryStore implementing that trait.

Things are pretty basic right now; you cannot even implement synchronisation on top of the current trait, because it does not support receiving and verifying payloads incrementally. This will be the job of a subtrait. In general, the idea is to provide a hierarchy of traits, adding more advanced functionality such as Bab-related methods, the ability to summarise data for range-based set reconciliation, and so on.

We are also not building Willow’25-specific versions of this functionality yet, we want to first see how these new traits feel, and how they fit together with other pieces such as the Bab implementation. But still, this release is the first step toward the major missing building block before we (and everyone else) can start building actual applications. And now we have something to expand upon, to bring things into shape in time for FOSDEM!

Fuzzer Poetry

To the cargo-fuzz maintainers. We can look back to a year of productive testing, thanks to you.

I

There was a time when testing meant to me
Enumerating inputs and to pray
They'd cover everything there was to see
And missed no corner-cases on the way.

Then quickcheck promised instant peace of mind
Its elegant design had me enticed
Invariants and randomness combined
But sample sizes never quite sufficed.

A fuzzer proved to be the missing link
Ensuring every corner-case was checked
Allowing me to tentatively think
My code might actually be correct.

Now mind you writing tests is still a pain
But worth it for the confidence I gain.

II

I have been deeply in love
twice
no matter how long things go well
they can always shatter.

When I start the fuzzer I
wait
wait and wait
wait and wait and wait
pacing
wondering
if this will be the run that does not fail.

As the fuzzer hums and heats
reluctantly I let myself believe
but never know for certain.

How do I shield myself from disappointment
yet
allow myself
the hope that keeps me going
allow myself
to burn?

III

INFO: Running with entropic power schedule (0xFF,100).
INFO: Seed: 1658404568
INFO: Loaded 1 modules (74852 inline 8-bit counters): 74852 [0x562332a29c30, 0x562332a3c094),
INFO: Loaded 1 PC tables (74852 PCs): 74852 [0x562332a3c098,0x562332b606d8),
#2 INITED exec/s: 0 rss: 43Mb
NEW_FUNC[1/102]: 0x562332596b00 (BuildId: 4cf9597d4ab63dec5b3d24323cb8d9d0a41408a7)
NEW_FUNC[2/102]: 0x562332597000 (BuildId: 4cf9597d4ab63dec5b3d24323cb8d9d0a41408a7)
#5123 NEW cov: 726 ft: 727 corp: 2/51b lim: 53 exec/s: 0 rss: 46Mb L: 50/50 MS: 1 InsertRepeatedBytes-
#5124 NEW cov: 727 ft: 728 corp: 3/101b lim: 53 exec/s: 0 rss: 46Mb L: 50/50 MS: 1 CopyPart-
#5130 NEW cov: 727 ft: 748 corp: 4/151b lim: 53 exec/s: 0 rss: 46Mb L: 50/50 MS: 1 ChangeByte-
NEW_FUNC[1/11]: 0x5623325a6c60 (BuildId: 4cf9597d4ab63dec5b3d24323cb8d9d0a41408a7)
NEW_FUNC[2/11]: 0x5623325a8700 (BuildId: 4cf9597d4ab63dec5b3d24323cb8d9d0a41408a7)
Meanwhile though, our increasingly impersonal societies rend ever deeper wounds into our hearts.

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

What a year. Sammy looked back, and I will lay out some future plans — specifically, plans for Willow. By the end of January, we want to have a simple persistent storage implementation and a Willow Drop Format implementation. Obviously, we have many more plans. Here they are, roughly in the order in which we want to make them real.

With Meadowcap, persistent storage, and the Willow Drop Format implemented, it will be possible to build actual sneakernet-based applications. While it is tempting to add more powerful data synchronisation as the next step, we should probably revisit end-to-end encryption of entries and payloads first. The earlier we implement this, the fewer moving pieces we will need to change based on our learnings. The concrete scope of this will consist of specifying specific techniques and primitives as part of Willow’25, and then to augment our storage implementations with the ability to encrypt and decrypt data on the fly. Ideally, this work will introduce only small changes to our public APIs — but ensure that most everything going forward is encrypted by default. Realistically speaking, a whole bunch of data is going to end up on the servers of large cloud providers, after all.

Before I can describe the next endeavours we have planned, I need some slightly abstract groundwork: the distinction between pull-based and push-based APIs. In a pull-based API, the entity that wants data needs to actively ask for it. In a push-based API, a data provider can proactively send data to its subscribers as new data arrives. Push-based APIs have several advantages, but they are also significantly more complex to get right. To a large degree, I blame the subpar progress we have made in the past on prematurely trying to nail the perfect push-based APIs: the Willow Confidential Sync protocol for replicating data, as well as ambitious subscription-based APIs for interacting with local data stores. This is why we have adopted a pull-based-only approach for now; this decision has played a significant part in our frequency (and hopefully quality) of recent releases.

Keeping this push-vs-pull distinction in mind, the next logical step in making Willow more “real” is a synchronisation protocol over the internet. The Willow Confidential Sync protocol is deeply push-based, so it is not an option. Instead, we have concrete plans for a more HTTP-inspired protocol of relatively simple requests and responses. This is the Willow Transfer Protocol (WTP), and you can read a draft version here. Completing the WTP specification and implementing it will be the next larger chunk of work, and one I am looking forward to!

Once we have a WTP implementation, we will have a complete (pull-based) Willow stack. It will then be time to slowly move to more efficient push-based components. The first step there will probably be local APIs. More specifically, a Bab implementation that allows subscribing to new incoming data, and then a Willow storage implementation that allows subscribing to change events. This will be crucial for more advanced applications that want to dynamically maintain custom indexes for more efficient queries than simply interacting with a Willow storage directly. In the long term, I actually hope that Willow stores will be used for replication and as a foundational source-of-truth exclusively, whereas any data access would go through more appropriate special-purpose indexes. Subscribable stores are the key stepping stone for moving toward that world.

Another step we will need to take eventually is to provide a more efficient storage backend. Right now we are building simplistic, linear-time storage that simply scans through all data and filters out unwanted results. But in the mid-to-long-term, we will need a proper three-dimensional data structure to provide access in logarithmic time. I have thoughts already around a three-dimensional generalisation of zip-trees (and, possibly, G-trees). But these details are a topic for a different time.

Speaking of improved local data access: we currently provide only embedded Willow storage, but long-term we would like to move to an actual database server with which multiple applications can interact concurrently (using capabilities to scope access rights, of course). The basics of this are fairly straightforward, but the challenging part is making push-based APIs work. Ideally, we'd like applications to be able to maintain persistent subscriptions even across several sessions. This would require the database server to maintain an event log, and applications would supply an offset at which to resume their subscription upon connection. When an application falls too far behind, it would catch up via set reconciliation. This sounds nice in theory, but the actual specification and implementation process will probably be rather thorny.

And after all of that, there is Willow Confidential Sync. I assume that we will learn enough from the WTP to warrant reworking the confidential sync spec. And then we will need to implement it, of course. Which feels a bit daunting right now, yet also exciting — confidential sync is were so many of our design decisions converge into a package that will likely be greater than the sum of its parts. I want to live in the world where you can integrate that package into your application designs with miniscule effort.

Other Willow-related plans include benchmarking suites, transactional APIs for our local data storage, a peer-sampling service (possibly in concert with confidential sync), and implementing the Willow URIs spec. Then there are application ideas — Rummager, the Cobweb, perhaps libraries of common UI components. And beyond Willow, we still want to release Macromania, and we have too many ideas for other side-projects to even start enumerating them here.

This is already far more than we can hope to accomplish in 2026. And we’ll doubtlessly accumulate even more ideas along the way. 2026 will be a busy year, but also a fun one, and one brimming with potential.

~Aljoscha

ImageJoni Mitchell — I Had a King
ImageThe Mothers of Invention — Hungry Freaks, Daddy
ImageCrosby, Stills, & Nash — Guinnevere
ImageThe Velvet Underground — Heroin
ImageKing Crimson — Lament
ImagePatty Smith — Birdland
ImageGentle Giant — Aspirations
ImageKate Bush — The Man with the Child in His Eyes
ImagePrince — I Would Die 4 U
ImageArthur Russel — Wonder Boy
ImageDavid Bowie — Looking for Satellites
ImageMarvin Pontiac — Bring Me Rocks
ImageSonic Youth — Expressway To Yr. Skull
ImageMr. Bungle — Quote Unquote
ImageNeutral Milk Hotel — In the Aeroplane Over the Sea
ImageJeff Buckley — Mojo Pin
ImageSleepytime Gorilla Museum — Ambugaton
ImageHobson's Choice — We Dream, We Are
ImageKing Gizzard and the Lizard Wizard — Mr. Beat
ImagetUnE-yArDs — Gangsta
ImageLouis Cole — Your Moon
ImageBuke and Gase — Sleep Gets Your Ghost
ImageMingjia & The Tortoise Orchestra — I Had a Mouth Once
ImagePunch Brothers — Familiarity
The worm-blossom advent calendar of 2025 has concluded.

2025Week 5119 Dec

“damn, we need more romanticism right now”

A three panel comic, introduced by the text "Deep inside Aljoscha's lair...".
      
In the first panel, we see Aljoscha sitting at his desk, his face glowing in the light of a computer. In the background are wires, pipes, screens, papers strewn over the floor. The text in this panel reads: "Fuzz tests... passing. Readme... complete. Publishing...".

In the second panel, we see a close up of Aljoscha's glasses, reflecting a warning sign captioned with the text "error". Aljoscha mutters to himself: "impossible".

In the third panel, there is cursive text surrounded by Christmassy doodles. The text reads: "This holiday, give the gift of revoking your friends' keys so they can't spend their Christmas revoking a novel image format!"
A teeny megaphone blaring out noise

Willow URI Specification

We have published the specification of a willow:// URI scheme for denoting Willow Entries and Areas.

Image

We had this specification lying around for a while, but there was so much going on all the time that we never properly published and announced it. Publishing it on this exact week is of course a deliberate and well-calculated decision, nothing else is going on here.

A teeny megaphone blaring out noise

Updated Drop Format

Also this week, we have updated the Willow Drop Format specification to support storing partial entry payloads — powered by Bab, so everything stays verifiable.

We further removed drop encryption from the specification, leaving that up to applications. We still highly recommend encrypting drops; our own applications will make encrypted drops the default and unencrypted drops opt-in.

And finally, Willow’25 now specifies parameters for the drop format. All of this in preparation for our upcoming implementation. We plan on having persistent data storage with drop format import and export within the next two months!

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

I want to share an application idea I have been dreaming up: the Cobweb. The Cobweb is a hypertext system with four distinguishing characteristics:

First, it has a built-in operator for transcluding multiple documents at once. Think like an iframe in HTML, but for a whole directory, or even multiple directories accross domains. With this functionality, you can replicate design patterns such as microblogging timelines or nested reply threads, while requiring neither ad-hoc server-side logic nor ad-hoc client-side logic. Everything simply arises from the document markup. I have written a blog post exploring this approach more thoroughly here.

Second, by virtue of being built on Willow — hyperlinks take the form of Willow URIs, of course — the Cobweb provides mutability without link rot. We need to normalise such systems.

Third, extreme simplicity. The complexity of the web necessarily concentrates power in the hands of browser vendors. I consider it imperative for alternative systems to pursue a contrary approach without compromise. The markup of the cobweb is essentially a subset of markdown, together with a bulk transclusion operator. The only inline media beyond text are images; only a single, simple image format is supported. Arbitrary files can be added as attachments at any position in the text, but without any special rendering logic beyond, effectively, a download button.

Fourth, efficiency. Markup is stored in a computer-friendly format (you might edit things as markdown, but that markdown would be saved in a binary format) — possibly even designed so you can mmap documents into memory and then work with them directly. The image format employs a form of interleaving that allows resource-constrained clients (or clients in low-bandwidth environments) to download only prefixes of images, which can then be rendered as low-quality versions of the final image. Bab ensures the integrity of the prefixes. Compression everywhere, though always preserving simplicity and thus ease of implementation.

The end result would be fairly constrained compared to the www, yet still intriguingly powerful — especially with bulk transclusion. And I envision extremely performant and low-memory-footprint Cobweb browsers, which would still manage to make everything really slick: great typography and customisable styling, inline-editing and authoring, real-time updates of transclusions, excellent UX around access control, storage size management, and so on.

Inspiration includes Gopher and Gemini, Man Pages and IETF RFCs (seriously, just imagine having something like the Cobweb instead of these — it keeps getting better the longer you think about it!), Mastodon, TiddlyWiki, and Joplin.

~Aljoscha

A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

Last Sunday we received the news that our proposed talk for FOSDEM had been accepted. I’ll be presenting “Willow - A family of peer-to-peer storage protocols” in the Local-first, sync engines, and CRDTs track. I’ll do an overview of the Data Model, Meadowcap, etc, but I’d like to also get into some of our motivations, and maybe we’ll have some surprises too.

The last time I did a talk like this was in the Summer of 2023, at Offline in Berlin. We’d just started working on Willow, and we thought it wouldn’t be too long before we had a working implementation. Innocent days.

Looking back two and a half years later, without complete implementations of everything, is bittersweet. I recognise all the cul-de-sacs we wandered down, the missteps we made, and how I’d do things differently if I could go back in time. But in order to become the me who can recognise my own failures today, I had to go down all those dead ends first with my own two feet. There was no other way available.

So in the end I’m just grateful I get the opportunity to still do the things I wanted to do, even if it’s taken longer than I’d hoped. On many levels.

However, I do not want to get up on a stage and talk about Willow again, and then tell interested folks that they need to wait a bit longer: we’re aiming to have implementations of persistent Willow storage and the drop format completed by the time I head to FOSDEM.

ngl it’s going to be a little crunchy. To have persistent Willow data storage, we need persistent Bab, so this week I implemented a filesystem backend for Bab, implementing Bab’s ByteStorage trait over a thin wrapper over async-fs’s File. Aljoscha did a great job of designing this trait, I needed to know next to nothing about how Bab works to get this thing working. Ignorance truly is bliss.

Ping me If you’d like to meet up at FOSDEM!

~sammy

Shout-out!

Image

Danmw has struck again, contributing elastic queues to Ufotofu. When buffering many items, these queues grow their internal storage, but once load decreases, they reduce their memory consumption by moving the buffered data to smaller allocations. While this increases the worst-case time complexity of both enqueing and dequeing to O(n), the amortised time complexity stays in O(1).

As with Dan’s previous contribution, this was an absolute pleasure to review. Thank you, once again!

A friendly computer waving his mouse, beckoning you to come contribute!!!

Contributions Welcome: Implementing Willow URIs (Rust)

https://codeberg.org/worm-blossom/willow_rs/src/commit/73a2cbafed747ccfcd455d90e033ccc8b5366096/willow_uri/src/lib.rs

We are trying something different this week. So far, we have always asked for contributions to specific issues with a straightforward path to follow. With the Willow URI specification, we can offer a more self-directed approach. Implementing the specification is a mid-scale undertaking, ideal perhaps as a holiday project?

We have created a very basic starting point here: a bare sketch of a data type for entry URIs, and a whole lot of comments pointing in the general direction of a more complete implementation. We warmly invite everyone who would like to contribute to gather in the worm-blossom Discord, so we can figure out together how to tackle this implementation.

Sammy and I will stay focussed on implementing Bab and the Willow Drop Format for now, but will be happy to join any discussions, review code, help getting used to our APIs, etc. All of this is meant as a fun experiment, as a more creative opportunity for contributing to the willow-in-rust stack. If there is no interest, then we will simply implement Willow URIs once we need them ourselves. But until then, we encourage everyone to give it a go!

Image

~Aljoscha

what's with the name?!

Image

why is this website called ‘worm-blossom’? In this series we will explain why in a concise manner.

Part 4: Homesick

It is 2003. A teenager places a disc emblazoned with Phantasy Star Online Episode I & II into the open disc tray of a Xbox. A thin, grey-green cable worms up from a bulky controller held limply in one hand, up into a headset wrapping lightly around the back of her head.

This teenager’s name is Sammy. A recent immigrant from London, now living in a small city in the Netherlands, she really doesn’t want to be here at all. She’s waited months for the release of Microsoft’s new Xbox Live service (which came in a physical box), and pestered her mum daily to sort out the prerequisite broadband connection.

Broadband internet is still a novelty: “what do mean it’s always connected? Why would you need that?”.

To Sammy the answer is obvious. Faraway friends gathered on the glowing deck of a spaceship, actually talking to each other with voice chat. Teleported out of her living room, out of herself. Phantasy Star Online is repetitive and straightforward, but sharing it with others makes it happy comfort food.

Unlike our previous protagonists, Sammy has no grand vision for reordering the world with systems. She is fourteen. The only vision she has is to grind her character up to level 80 so that she can play the game on Ultimate difficulty.

She plays with her friends online, and because they are in the UK and she is in the Netherlands, they are always making arrangements like “five my time, six your time”. In time, they make a joke out of using the time prominently displayed in beats in the corner of the game’s UI. It turns out to be sort of useful. Eventually Sammy stops playing Phantasy Star Online, but beat time hitches a ride in her memories.

But why is worm-blossom called that? Next time we'll meet in the Pacific ocean.

2025Week 5012 Dec

“sanity — we at least gestured toward preserving it”

A two panel comic.
        
In the first panel, Aljoscha ask Sammy: "How was the conference? Get any insightful feedback on our work?". Sammy is eating a sandwich.

In the second panel, we see a huge smile on Sammy's face as she recalls a happy memory, a tear in her eye. In a thought bubble above her, she has beautiful hair and coyly looks back as someone says to her "girl you are rocking those bangs".
A teeny megaphone blaring out noise

WebTT — A Spec for Tooltips on the Web

If you ever looked at the Willow specifications, you may have noticed we like tooltips quite a bit. In fact, pretty much the only thing we like even more than tooltips are specifications. In a surprise to absolutely nobody, we have written a specification for a tooltip system: WebTT (Web Tooltips).

WebTT is a lightweight way of adding nestable, cross-domain tooltips to any website, with no server-side logic required. All you need to do is to include a spec-compliant javascript file, some CSS styling, and then any HTML element with a data-tooltip-anchor attribute pointing to a tooltip page opens that tooltip page in an iframe on mouseover. Like this:

<a data-tooltip-anchor="https://willowprotocol.org/previews/Path.html?def=Path">look a tooltip!</a> turns into look a tooltip!

See the WebTT page for more information. And expect more tooltips on this website soon!

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

Implementing Meadowcap over the past weeks has been quite draining, so I decided to take things more slowly this week. And then I didn’t — oops! We want to make progress toward a usable Willow implementation as quickly as possible, and the next step there is persistent storage. Which, in particular, means payload storage, which in turn means working on Bab.

We want to support pluggable storage backends with our Bab implementation: in-memory for testing, filesystem-backed for portable persistent storage, kv-store-based storage for an easier time getting transactionality working in a kv-based Willow store, etc. So what should the common interface of all these backends be? I wanted to keep this interface as simple as possible, and push as much Bab-specific logic as possible into higher-level components that can be shared between all implementations.

What I ended up with is a ByteStorage trait that is essentially a highly simplified, asynchronous Vec<u8>. You specify a total capacity up-front, you can read and write bytes within that capacity, and you can increase the capacity (if absolutely needed). That is all the funcitonality you will need to provide for a custom Bab backend.

The gap to Bab is bridged by treating such an array of bytes as an implicit representation of the Bab Merkle tree. Representing trees as flat arrays is a pretty standard maneuver, though we get some extra “fun” by allowing to specify that the bytes only store Bab chunk data starting at a certain offset (and up to a certain length, but that part is basically free to implement) — the whole point of Bab is partial verifiability, so it would be quite pointless if the underlying storage would have to store complete strings. As a result, I wrote a whole bunch of non-trivial recursive functions for various kinds of index computations (here’s an example). I’ve included a scan of the notes I took on the first day of work below.

Image

Next, I found a neat way of implementing all the various optimisations for verifiable streaming (all centering around omitting as much data as possible while preserving the ability to verify things within a parameterisable, bounded amount of memory): the SliceProgressIterator takes the desired optimisations as an option, and sequentially emits all the Merkle tree nodes that are part of the verifiable stream, annotated with information on which data can be skipped. The nice thing about this approach is that it is fully decoupled from any particular storage backend, and can even be used to guide both producing and consuming a verifiable slice stream.

The final bit of work was to use this iterator in a function that actually produces a verifiable slice stream by reading data from a storage backend. Thanks to those earlier abstractions and helpers, that function looks quite tame, despite being a surprisingly tricky beast internally. Yay!

The next steps will be a function for consuming a verifiable stream and placing the received data in a byte storage. This one is more complex, because it needs to verify data as it arrives. Next, we will need a way of populating a byte storage by feeding in raw string data, which internally is then augmented with all required verification metadata. And finally, we need a nice way of exposing access to all of this: the storage trait only provides storage space, but does not track which parts of it have already been written to. Efficiently keeping track of that is the final unsolved design question in this stage of the work (I already know how to tackle this conceptually, but the details need more thought still).

~Aljoscha

A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

This week I was attending Splintercon. This conference was focused on the notion of the ‘splinternet’, the fragmented result of the internet being regulated and censored by nations and corporations alike. Who benefits, and who suffers? Was this outcome inevitable?

There was always something maniacal about the late-90’s vision of the internet breaking down all distances, physical or cultural. I remember various graphics of world maps being collapsed into a single point, eradicating any notion of space and geography. This belied the reality that the internet is inescapably physical, a sprawl of cables and humming machinery forced to creep over the surface of the earth.

The idea that the very real differences between places would simply yield to the internet’s border-dissolving power was naïve at best. No technology survives contact with a culture. The car — another distance-collapsing technology — has found its way to every corner of the earth, where every place has developed their own regulations and culture around driving.

So perhaps the ‘splinternet’ was inevitable. Today that splintering comes from the top, from new legislation over the storage of personal data, to the outright censorship of information.

But why should governments and corporations be the only ones able to decide how networks are run? If the internet is splintering, we should all be able to get in on the fun. Maybe the problem is that the internet isn’t splintering enough.

~sammy

2025Week 4905 Dec

“surprise! spiritual growth!”

A comic with three panels.
        
In the first panel, Sammy is looking at a laptop, and asks: "What is it about 'Spotify Wrapped' that gets people so excited? Aljoscha, watering a plant in the background replies: "It's a fun way to summarise the year?"

In the second panel, Sammy asks: "But what about the people who don't use Spotify? How are we supposed to learn something meaningless about ourselves?". Aljoscha is in the foreground, his glasses glinting in the light.

Before the next panel, the word "LATER".

In the third panel, we see a print out in a monospace typeface, like a console. In ASCII art, it is titled "RUST UNWRAPPED". Below, it says:

- 14480 compiler errors raised
- 95483 warnings ignored
- 45 panics in production
- Ran cargo clean 98 times
- Target directory grew to 107 gigabytes
- You cried out of frustration only once this year
- The borrow checker cried out of frustration 
  only 37 times this year

AND BEST OF ALL…
- Time spent on garbage collection: 0 seconds
A teeny megaphone blaring out noise

Capabilities in willow25!

We've added a batteries-included version of the meadowcap capability system into willow25. The batteries in question are made of the secure and efficient ed25519 signature scheme.

use rand::rngs::OsRng;
use willow25::prelude::*;

// Randomly generate a new keypair.
let mut csprng = OsRng;
let (subspace_id, secret) =
  randomly_generate_subspace(&mut csprng);
let namespace_id =
  NamespaceId::from_bytes(&[17; 32]);

// Create a write capability.
let mut cap = WriteCapability::new_communal(
  namespace_id.clone(),
  subspace_id.clone(),
);

// We have a capability and the secret
// for its receiver. Time to write an
// authenticated entry.
let entry = Entry::builder()
  .namespace_id(namespace_id.clone())
  .subspace_id(subspace_id.clone())
  .path(path!("/ideas"))
  .timestamp(12345)
  .payload(b"chocolate with mustard")
  .build().unwrap();

let authed = entry
  .into_authorised_entry(&cap, &secret);

// It worked!
assert!(authed.is_some());

With access control in place, willow25 is ready for the next step of implementing persistent entry storage.

Shout-out!

Image

Thank you to danmw, who has contributed new Path::create_slice and Path::create_suffix methods to willow_rs and willow25.

This could have been a very simple task, but the resulting code would not have been optimally efficient. So Dan went ahead and adjusted our Path struct, which required delving into the least-readable parts of our codebase. Didn’t seem to be a problem though, everything runs great — and in O(1) time instead of the O(n) solution we would have expected. Thank you for this effort!

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

Two weeks ago, @Soapdog asked me whether I could put the weekly songs on this website into a playlist. And thus, a slow descent into madness began. The tool in which I write these tracks has absolutely no support for that kind of thing — but it is open source and can be forked. And thus, I proudly present the official worm-blossom playlists page. Featuring not only a playlist of all weekly songs (automatically populated by Macromania, obviously), but also a bonus playlist of a bunch of tracks I have accumulated in a plaintext file over the years.

The original tool for creating this music is Beepbox, but I have been using a “mod” of it, which really just means a fork. Or rather, a fork of a fork [...] of a fork. There’s a family tree of how these mods have evolved, and I decided to fork pretty close to the leaves, at the 2024 mod AbyssBox (primarily because it has a ring modulation effect). The codebase is a succession of mostly independent, mostly undocumented spaghetti modifications to an undercommented core. So everything took longer than expected, but eventually I succeeded in adding my own underdocumented spaghetti modifications!

The codebase I started from had no notion of multi-song containers at all. The first thing I had to add was code that would allow the synthesizer to emit a notification once it had finished playing its song. Next, since many of the songs are infinite loops, I added support for slowly fading out a track after some number of repetitions (fun fact: you need a logarithmic decrease in volume, not a linear one). Finally, at the start and end of playback there would always be an audible click sound, so I added a miniscule exponential ramp-in and ramp-out. I cannot unhear that click in other beepbox mods now!

As a bonus, since we have full control over the audio player now instead of merely embedding it, I adjusted the colour scheme to be even more worm-blossomy — and I could finally remove some vertical spacing that had been bothering Sam! All of this was a fun side-quest, but now I am completely done with it and will almost certainly not add any additional features such as a playlist-to-zip-of-mp3s export, earwig compatibility, or even custom sound synthesis to it.

~Aljoscha

A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

For this week's comic, I composed an image to look like console output, printed it out, cut it out and glued it to the rest of the comic before scanning it back in and putting it on the internet. I hope somebody out there appreciates that. I make the original Rust Unwrapped image available here.

There's only one month left to do all the things I really wanted to do in 2025. Which means this week I got curtain bangs and started prototyping a video game. Will it be any good? If you never hear about it again, you will know the answer (the bangs look great though).

~sammy

A friendly computer waving his mouse, beckoning you to come contribute!!!

Contributions Welcome: Add Meadowcap default values (Rust)

https://codeberg.org/worm-blossom/willow_rs/issues/21

In Willow’25, we specify a well-known keypair to use as a default, effectively enabling public access to any data “secured” by this keypair. But we have not added it to the Rust implementation yet. This issue is a nice opportunity for getting familiar with the new Meadowcap API.

It has been really nice to see pull requests come in for our contribution-welcome issues. For Willow, there are only a few left. We have had less luck with our ufotofu issues, but who knows, maybe this’ll be the week?

2025Week 4828 Nov

“some of our ducks are on a train in the wrong direction”

A five-panel comic.
        
In the first panel, we see Sammy sitting at a desk. She says "So I'll just keep working on this URI stuff. How about you?". A voice crackles from her monitor: "well...".

In the second panel, we see Aljocsha on the screen, his hair blown about. The background shows a blue sky with clouds. He says "these encodings are taking a bit longer than I'd hoped, but I think Mea-".

In the third panel, Aljoscha continues speaking: "Hang on. Plane." He looks up at a plane soaring above him in the sky.

In the fourth panel, we see Sammy sitting at her desk again. She asks: "Aljoscha, are you in the sky?". Her monitor replies: "Oh! Did I not tell you?".

In the final panel, we see Aljoscha riding an enormous bird-like thing, laptop in front of him. Aljoscha says "This week I've implemented a bird. It has a principled design and requires only O(1) space."

what's with the name?!

Image

why is this website called ‘worm-blossom’? In this series we will explain why in a concise manner.

Part 3: The Sega Dreamcast

It is 1999. Isao Okawa, the chairman of Sega, is presiding over the release of what will be Sega’s final home console, the Dreamcast. Okawa has long advocated for Sega to give up on hardware and become a third-party publisher, but has supported the Dreamcast for one reason alone: it is the first home console to include a built-in modem.

Okawa believes so steadfastly in the power of the internet that he has personally paid for every Dreamcast in Japan to be bundled with a year of free internet access. He has instructed Yuji Naka, head of Sonic Team, to begin working on a flagship online game for the Dreamcast.

But Sonic Team has never built an online game before. They’ve spent the last few months working on ChuChu Rocket! just so they could learn more about designing networked games. But ChuChu Rocket is a 2D puzzle game, and what they have in mind is more ambitious: a 3D action roleplaying-game inspired by Diablo.

Beyond the technical complexities, connecting thousands of players from across the world brings its own design challenges: how do players speaking different languages communicate with each other when all they have is a gamepad? And how can they coordinate play when a party’s members are spread across the globe, across timezones?

Coincidentally, a deal between Sega and Swatch has been brewing. Okawa is one of the primary donors of MIT’s Junior Summits, and would have been present when Negroponte and Hayek Sr. announced Swatch Internet Time one year earlier. This is likely how he came to announce a new business alliance which will see Swatch Internet Time built into Sega’s software, including Sonic Team’s new online title.

One year later, Phantasy Star Online sees players from across the globe banding together to defeat enemies in real time, transcending language barriers using Symbol Chat, and coordinating play sessions using beats. It is introduced by a bombastic trailer which claims that the game allows players to do “whatever you want, 1000 beats a day, 365 days a year”. It is also a game where you play as a mercenary for a civilisation which has colonised a planet and in doing so inadvertently awakened a universally corrupting evil.

Phantasy Star Online is warmly received as a genre-defining release for home consoles, but Sega’s Dreamcast is discontinued just a few months after its launch, outflanked by competitors. Sega transitions from hardware manufacturer to third-party publisher, and Phantasy Star Online outlives the platform it was built to elevate.

But why is worm-blossom called that? Up to now, we've occupied ourselves with people who believed they could change the world with their systems. Next week we get to the part where I don’t have to do as much research to write these.

A teeny megaphone blaring out noise

meadowcap crate

We have released the meadowcap crate, a Rust implementation of our Meadowcap specification. Yay!

Image

Meadowcap is the bespoke capability system we recommend for usage withWillow. You can use it to authorise the writing of new entries, and to determine whether a peer is allowed to request certain entries from you. Access can be scoped to arbitrary Areas, and anyone with access to some area can delegate that access to anyone else, optionally restricting the scope to an even smaller area.

This update also implements a spec change we did this week: our notions for denoting sets of potential entries based on their coordinate in the three-dimensional subspace_id-path-timestamp space must always denote nonempty groupings of entries now. Aljoscha’s editorial tells the story of how that change came about.

We did not manage to update the willow25 crate with a plug-and-play instantiation of Meadowcap this week — stay tuned for that update!

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

Equality is a notion we often use without paying close attention to it, and sometimes that can burn us. Take for example the notion of ranges in Willow. We implicitly assume a syntactic notion of equality: two ranges are equal when they have equal start and end points. An alternate definition might be a semantic one, where two ranges would be considered equal if they describe the same set of values.

If you allow ranges to be empty (i.e., ranges whose end value is less than or equal to their start value), you get a mismatch between the two definitions: the ranges [2..2) and [3..3) (and [7..4), for that matter) contain the same sets of values (none at all), but they differ syntactically.

Up until Thursday this week, we allowed empty ranges. After days of painful debugging during the reworking of our Meadowcap implementation, we do not anymore.

The trouble started innocently, while I happily ported our implementation of the EncodeAreaInArea encoding. This encoding works with an inner range of timestamps which must be included in some outer range of timestamps — i.e., the inner range must describe a subset of the timestamps described by the outer range. The encoding encodes the start time of the included range by adding some positive number to the start of the outer range. This works great for non-empty ranges, but not when trying to encode the empty range [3..3) inside the outer range [5..8). Oops.

A simple and efficient fix would be to normalise empty ranges when encoding: when encoding an empty range within the range [start..end), you could always encode it as [start..start). The problem: we have strict notions of what constitutes a valid scheme for encoding values in Willow. In particular, equal values must yield equal encodings, and nonequal values must yield nonequal encodings. Normalising empty ranges during encoding would violate those basic (and sensible) axioms. We have mixed syntactic and semantic notions of how to compare ranges in the same encoding, and the result was pain.

In our old implementation of ranges in Rust we forbade empty ranges, which is why this issue never materialised in actual test failures. Frustratingly enough, most of the time spent in updating our implementation of all entry groupings was in making sure that empty groupings would be handled correctly. And now we had to flat-out remove all empty groupings from the spec, which meant I had to refactor all that code again, mostly undoing the finicky work I did two weeks prior. Diagnosing what was going on and then refactoring all groupings took me several (unpleasant) days. And that is why the revisited Meadowcap took so much longer than I expected.

I hope I learned my lesson and will pay closer attention to (implicit) notions of equality in the future. I was partially aware of this issue already; we have always defined only nonempty intersections of areas, for example. But for ranges I somehow hadn’t spotted the issue. On the bright side, our principled definitions of the semantics of both groupings and of encoding functions meant that we did catch this issue after all — in fact, it resulted in actual failure of a property test. This is exactly the kind of specification problem that has the potential to cause a whole lot of pain years down the road. So as much as the delayed Meadowcap release annoys me, I am glad we now fixed the issue.

~Aljoscha

2025Week 4721 Nov

“it’s only five functions”

A piece of paper with various drafts of worm-blossom logos, including the final one that made it into our site header.
A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

This week I made good on my threat to build an Interactive Willow Data Model. It's a toy version of Willow which lets you view and add entries and 'sync' between three 'peers'.

Image

Is having something to play with going to be the missing piece of our documentation story? I don't know. It's made me want to write documentation which intersperses theory with lots and lots of mini-toys, but who has the time?

Most of the work on this was sunk into two spots:

  • Making it so you could get an instant preview of what your changes would look like before committing them. This was something Aljoscha considered a must-have after looking at the first version, and unfortunately I had to agree with him.
  • Squeezing a lot of (variably sized) information into a snug three column layout. Lots of overflow to deal with.
Image

The most unsettling part of working on this by far was how powerful I felt working with TypeScript and (P)react again. After a year plus of working in Rust, I thought I'd be wringing my hands at the sheer barbarity of it all, but instead I was completely at home in the filth.

~sammy

A teeny megaphone blaring out noise

compact_u64 crate

We have released compact_u64, a crate implementing the variable-length encoding of unsigned 64-bit integers we use throughout the Willow specs.

use ufotofu::codec_prelude::*;
use compact_u64::*;

let mut buf = [0; 3];
let mut con = (&mut buf).into_consumer();

cu64_encode_standalone(258, &mut con).await?;
assert_eq!(buf, [0xfd, 1, 2]);

let n = cu64_decode_standalone(
  &mut producer::clone_from_slice(&buf[..]),
).await?;
assert_eq!(n, 258);

This release is an important step toward our implementation of Meadowcap. We had hoped to release the full Meadowcap implementation this week already, but turns out porting all the encodings it relies on simply takes too much time. By next week we will hopefully be done.

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

On Variable-Length Integer Encodings

Most competent programmers are aware that linked lists are inefficient data structures, simply because they do things which modern hardware struggles with. Having indiviual elements point out to the next element (if any) is much less efficient than simply grouping elements next to each other in memory and prefixing them with the number of elements. The opening paragraph of the wikipedia article on linked lists points this out. The documentation for linked lists in the Rust standard library points it out in CAPS.

Here is a hot take: LEB128 — the most widely-used variable length integer encoding — is the linked list of varints. The basic idea is to represent an integer as a sequence of bytes. The first seven bits of each byte encode information about the integer, and the final bit indicates whether the end of the encoding has been reached or not.

For computers, this format is a pain to decode. Modern hardware wants to operate on multiple bytes simultaneously whenever possible (even without SIMD, processing still happens at words of multiple bytes), but an LEB int must be processed byte by byte. And for every single byte there is a branch which is highly difficult to predict.

Analogous to data structures for sequences, a much better design is to provide the length up front and to then have the actual data in a contiguous chunk. CBOR got this right, I humbly offer the VarU64 spec and the way we encode integers in Willow (which is essentially a generalisation of the CBOR design), other people have proposed similar designs. The exact choice between any of these barely matters. But they are all significantly more efficient than LEB128. And as a bonus feature, comparing their encodings lexicographically coincides with numerically comparing the encoded integers, which can be helpful when working with kv-stores — but is not the case with LEB128.

For the most part, this does not really matter. Very few systems will critically suffer from the performance penalty implied by LEB128. I just find it curious that so many competent engineers simply accept the very first design they stumble upon. And where is the warning in the first paragraph of the Wikipedia page on LEB128? Why does that page only provide links to “related encodings” that replicate the same design choice, instead of discussing alternatives?


Oops, looks like this week turned into a week of rants for me. Oh well, going forward I will at least be able to link to these pieces instead of repeating the same rants over and over in different discussions.

~Aljoscha

A Rant From the Ivory Tower

Image

Kids these days. Keep on yapping about CRDTs, as if they found the best thing since sliced bread. Even though everyone knows the best thing since sliced bread is actually blockchain the internet of things artificial intelligence service-oriented architecture!

To recapitulate, an operation is called commutative if the order of its arguments does not influence the result. Quite obviously, if you only use commutative operations, then the order of your inputs does not matter. What else is new? Sure, “operation-based CRDT” sounds moderately impressive, but all you are doing is to apply a commutative (and associative) binary operator.

Technically, when you also add idempotence, you get a semilattice (operation-based CRDTs and state-based CRDTS are technically equivalent, after all). But let’s face it, most operation-based CRDTs in the wild are ad-hoc commutative operators that do not derive value from any underlying algebraic structure.

Did you know that you can turn any non-commutative operator into a commutative version by consistently permuting its arguments? For example, while subtraction is not commutative, an operator that takes two numbers and always subtracts the smaller number from the larger one is commutative. You can also make any operation idempotent if you require the arguments to carry a unique id, and keep track which ids you already processed.

So when a data structure expects all operations to arrive in sequence and has to store them all forever, so that they can be deduplicated and sorted, so that they can be made idempotent and commutative, so that the whole thing becomes an operation-based CRDT, forgive me if I am not particularly impressed.

Now, state-based CRDTS, on the other hand — or semi-lattices as they have been called for decades prior — are great. They provide sufficient algebraic structure to be actually useful, allowing for universally-applicable optimisations such as delta CRDTs (that paper also provides a definition of the concept of irredundant join decompositions, which is central to most interesting CRDT questions). Here are a couple more ideas of what could be explored:

If your application state is a join-semilattice, then you can represent it by building a tree whose leaves are the elements of the irredundant join decomposition of your state, and whose internal nodes store both the join over all children of the subtree and a hash of that join. You could use that tree to power a tree-based set-reconciliation algorithm (whether based on history-independent trees or not), giving you logarithmic-time state updates and the ability to synchronise them efficiently. More generally speaking, generalising set-reconciliation to join-decomposition-reconciliation seems quite promising (see, e.g., arxiv.org/pdf/2505.01144).

Image

How do irredundant join decompositions relate to information-theoretic bounds on synchronisation traffic in general, and to compression of CRDT updates in particular? Lattices admit a clean representation theory (see Davey and Priestley chapters five and eleven) in terms of irredundant decompositions. Thus, it should be possible to define encodings purely in terms of these abstract representations, which could ideally lead to provably optimal encodings of updates irrespective of the details of the particular lattice.

The CALM theorem states that conflict-free updates must monotonically progress in some partial order. This does not mean, however, that the amount of information you need to track has to increase monotonically as well. Willow provides an example, where a single newer (i.e., greater-in-the-CALM-relevant-order) entry can overwrite many older entries, thus deleting information. What does this look like from a more general lattice-theoretic perspective? When some join-irredundant element is in the irredundant join decomposition of some state, then no other element less than it can be part of that decomposition. In other words, join-irredundant elements “high up” in the partial order of the lattice enable deletion. Can we design for the existence of such join-irredundant elements? Which algebraic properties hold in lattices where we have upper bounds on the length of maximal chains which do not involve join-irredundant elements? Can we leverage such upper bounds for efficient encodings of updates? Do bounds on the largest anti-chains factor into this as well? What are good formalisms to quantify the “deleted information” of join-irredundant elements (a good starting point might be to define a a non-lossy lattice which carries around all elements which have been join-irredundant at any point; there should be a natural lattice-homomorphism from any lattice into its non-lossy counterpart that would serve to compare the two at any point in the original order)?

In a network where multiple peers synchronise state via a CRDT, which optimisations can we enable if we get to assume that peers cannot lag too far behind? How do we properly define that in the first place — maximum distance between greatest and least state when interpreting the partial order as a directed graph? And once we have a suitable notion of such bounds, how can we automatically determine which information we can now drop? In other words: whereas join-irredundant decompositions are defined in terms of the full lattice, which meaningful (and practically useful) notions of decompositions can we enable by strategically cutting off elements from the bottom of the lattice (here, it can be really useful to work with semi-lattices instead of proper lattices, because in a join-semilattice you can remove minimal elements without jeopardising the semi-lattice axioms)? The braid folks have been exploring similar thoughts in their antimatter algorithm (and in general, they have been doing some nice abstract CRDT work, see, for example, their time-machine theory for unifying Operational Transformation and CRDTs).

How come barely anyone is talking about sublattices? A lattice within a lattice is exactly what you need in order to support fine-grained access control while still keeping things synchronisable. Same for setups where peers are not interest in all the data ever but merely in a subset of the data. If that subset forms a sublattice, then you know that you can synchronise that data with confidence. It is no coincidence that access control and partial sync work out really neatly in Willow: every Willow Area induces a sublattice. Since, arbitrary lattices can have exponentially many sublattices, formalisms that assume that arbitrary sublattices can be expressed are probably inapplicable in practice. But when restricting the set of sublattices that can be expressed (like Willow restricts access control to the granularity of Areas), what makes for a good set of sublattices? You probably want some closure properties, maybe something filter-like? I don’t know, I’m not even a mathematician, I just want more people to ask these questions! Because it feels scary to merely intuit that Areas are a good choice for Willow without knowing why.

Image

Can we formalise a notion of intent-preserving lattices? When a text CRDT merges two overlapping edits in a deterministic but possibly unintuitive way, the intent of the author of either edit might be violated (and possibly even the intent of both authors). Is there a formal concept behind this human-centric notion? Staying with this example, instead of doing an arbitrary resolution, the join of the two edits could instead be a state which preserves both edits (“intents”?), at the cost of not mapping to a unique string of text. As long as such states can still be merged (accumulating ever more conflicting intents), you still have a CRDT, after all. That is basically what the Pijul vcs does. Again, can we formalise what it means that such a system preserves intents? Perhaps the existence of certain homomorphisms into the non-lossy lattice version sketched a few paragraphs earlier might be meaningful here?


All of these thoughts are merely starting points and semi-low-hanging fruits, but all of them have the potential to bring about actual engineering benefits for distributed systems. So I am all for CRDTs. But it feels to me like an increasing fraction of the usage of the CRDT terminology refers to ad-hoc variations of the brute-force approach of making arbitrary operations commute by applying updates in a consistent order (i.e., by sorting their arguments). Of course it is nice if a bunch of local-first applications speed up because somebody made a“JSON-CRDT” 10% faster. But that is not how we will make any qualitative progress on the road to ubiquitous peer-to-peer software. As uncomfortable as it may be, that will require actual math, not just repeating “CRDT” often enough to turn it into a buzzword.

2025Week 4614 Nov

“more than 80 bytes per week”

A four-panel comic.
      
      In the first panel, we are zoomed in on a hand holding a phone. On the screen there is a social media post reading "i've recently seen 3 nlnet funded projects all write their own static site generator for their own documentation. nerds with no oversight are just gonna nerd".
      
      In the second panel, we see Aljoscha and Sammy, both looking at the phone held in Sammy's hand.
      
      In the third panel, they look up at each other.
      
      In the fourth panel, Aljoscha say: "Technically our system can generate any kind of string". "Stop", say Sammy.

what's with the name?!

Image

why is this website called ‘worm-blossom’? In this series we will explain why in a concise manner.

Part 2: Swatch Internet Time

It is 1998. Two centuries after the French revolutionaries tried (and failed) to metricise time, Nicolas Hayek, the CEO of Swatch, and Nicholas Negroponte, director of the MIT Media Lab share the stage at the MIT Junior Summit ‘98 to jointly announce “Swatch Internet Time”, which divides the day into 1000 beats.

Hayek is a businessman who has successfully reordered ailing Swiss watchmakers into the Swatch Group. It is safe to assume that for him, Internet Time is just business, a new marketing campaign. But Negroponte is a true believer: “Internet Time is absolute time for everybody. Internet Time is not geopolitical. It is global. In the future, for many people, real time will be Internet Time," says Negroponte, an unabashed techno-optimist who regularly asks readers of his column in Wired to “move bits, not atoms”.

He is not alone in his belief that the internet will bring about a liberal utopia. And perhaps that is why for a minute (or a beat) it looked like internet time might actually happen. Watches flashing the time in beats flew off the shelves in Japan. CNN displayed the time in beats in the corner of their news feed, 24/7. Ericsson phones displayed internet time. ICQ, a then popular instant messenger used it too.

But after the initial fervour, Swatch Internet fell into disuse, just as it had two centuries prior. But unlike France's decimal time where the fifth hour corresponded with noon, 500 beats could mean anything depending on where you were in the world. 500 beats could mean noon — if you were in Biel, where Swatch’s headquarters are situated — or it could mean morning, or midnight elsewhere. Swatch Internet Time asked us to abandon the real world as our frame of reference and replace it with the internet instead.

But why is worm-blossom called worm-blossom? If we turn the camera away from the stage at Junior Summit ‘98 and zoom into the audience, we can see Isao Okawa, the then chairman of Sega Enterprises. We’ll meet him again in our next instalment.

A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

worm-blossom: now on Fridays. We have moved the publishing day because having a Monday deadline put a lot of pressure on the weekend. On Thursdays I'd think I have so much time to write about the French revolution, handily forgetting that I never have the opportunity to use a computer on the weekends. Then Monday would turn into a very stressful day. Now Fridays can be a very stressful day instead, which is much better.

We sadly had our proposal for Rummager rejected this week. It's one of those ideas that is very hard to convey, and I'm starting to think we'll just need to build out a prototype of it in our spare time to be able to do that.

It's a bit similar to something else I noticed this week: no matter how much documentation we write — loaded with illustrations and references and sidenotes — you just can't build up the same kind of understanding as if you actually got your hands on it. I'm thinking of building out some interactive tools for the Willow Data Model page because of that.

In any case, we have no shortage of (funded) plans. Keep muddling through out there.

~sammy

We have opinions

...on 'fancy' CRDTs

Willow uses a last-write-wins strategy for resolving conflicts. With so many sophisticated alternatives out there, why would we do that?

Systems which automagically merge data tend to fall into two camps:

  • Good at merging self-contained units of data (e.g. merging multiple edits of text into a single coherent edit)
  • Good at merging collections of data (e.g. merging separate timelines of posts into a single timeline with everyone’s posts in it)

Intelligently merging self-contained data like text is undoubtedly an impressive technical (and scholarly) feat! But most of the connected applications we use day-to-day don’t need it: microblogging, chatrooms, forums, messaging, media sharing, issue trackers. All applications where people author data alone before contributing it to a common pool of data.

Fine-grained CRDTs have a hard limit on how much they can be optimised. And they often require lugging around a complete history of changes, forever.

Which is why Willow focuses on merging potentially huge collections of interleaved data. You get a system which can serve a broad spectrum of connected applications, and one which we know we can make efficient.

A teeny megaphone blaring out noise

Ufotofu-Based Encoding and Decoding

We have released a new minor version of ufotofu, adding traits and helpers for encoding and decoding values.

use ufotofu::codec_prelude::*;
use ufotofu::codec::endian::U32BE;

let mut buf = [99; 5];
let mut con = (&mut buf).into_consumer();

// Encode a u32 into big-endian bytes...
U32BE(258).encode(&mut con).await?;

assert_eq!(buf, [0, 0, 1, 2, 99]);

// ...and decode them back into a u32.
let mut pro = buf.into_producer();
assert_eq!(
  U32BE::decode_canonic(&mut pro).await?.0,
  258,
);

In addition to traits for conventional encoding and decoding, we also have a module for encoding and decoding relative to some context shared between encoder and decoder. These traits allow for techniques such as delta encodings. Many of the encodings of Willow rely on this to encode information significantly more compactly than if everything was encoded in isolation.

In general, the codec traits of ufotofu map directly to the notions of (relative) encoding relations we use throughout the Willow specs. This does not mean that ufotofu takes on an opinionated formalism applicable only to Willow. On the contrary, it simply means that the concepts on which we build encodings in Willow are useful general-purpose abstractions.

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

The codec traits we have added to Ufotofu today have been part of older ufotofu versions for quite a while now. And they have been exceptionally useful throughout our Willow-in-Rust journey. In fact, they alone already justified ufotofu as a whole for us. What makes them so special?

Not just any value that can be turned into a bytestring gets to implement our Encodable trait. No, there are precise formal requirements that must be fulfilled by all possible encodings. Every possible value must have at least one encoding. No two nonequal values must have the same encoding. No valid encoding must be a prefix of any other encoding. That sort of thing (see the docs for the precise list). But still, how is that supposed to be incredibly productivity-boosting?

The answer lies in the humble codec::proptest module. It provides functions such as assert_codec. You call that function with a bunch of values, and the function panics if and only if the arguments are a counterexample to any of the formal criteria an encoding must fulfil.

We use this to let a coverage-guided fuzzer try to find counterexamples. And if it cannot, then we can be pretty darn confident that our implementation is correct. Which only few of our initial implementation attempts are. But we just press a button, and the computer gives us a set of failing inputs. No matter how many encodings we implement (and Willow alone has quite a few), we always get an almost ideal test setup and debugging aid for free.

And even better, this process has uncovered plenty of faulty definitions of encodings in the Willow specs as well. Some of our encodings are quite fiddly, and it is easy to lose track of some detail and specify an encoding which cannot be uniquely decoded, for example. But if the fuzzer cannot find a counterexample after 20 minutes, we can be highly confident that the encoding we specified is free of bugs.

Now that we have properly released ufotofu and its tooling around encodings, we will return to the Rust implementation of Willow proper. If everything goes according to plan, we will release a cleaned-up, well-documented, and ergonomic-to-use implementation of Meadowcap next week. And internally, it will be powered by ufos and tofu!

~Aljoscha

2025Week 4510 Nov

“Do geese see god?”

2025Week 4510 Nov

Image
A teeny megaphone blaring out noise

ufotofu

We have released the ufotofu crate, a Rust library for working with streams and sinks, which will power our Rust implementations of Willow. Because ufotofu will be a quite foundational building block, we put a lot of effort into documentation, including a somewhat over-the-top website. Please check it out!

Rust already has a de-facto standard library for async sequence processing, so we do not necessarily expect wide adoption for ufotofu. But we believe that some of its designs are genuine improvements over many libraries out there across all programming languages. So we hope that awareness of our little crate will spread, and that it will influence the design of other libraries in this domain. The ufotofu website goes into all design choices in great detail, and is interesting not only to Rust programmers but to API designers in any programming language.

Special thanks go to Glyph, for suggesting to use a palindrome as the crate name, and for early implementation work.

Have you opened the ufotofu website yet?

A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

Last week I was chasing funds for the next generation of worm-blossom projects. One of these proposals is a general-purpose GUI application called Rummager. In our original application I breathlessly wrote about "organically expressing your own applications through the act of nesting free-form data". But what does that actually mean, you (and potential funders) ask? A picture's worth a thousand words, and I've combined several into a single image below which is a lot of economy.

Image

We also put together the new ufotofu site. Ufotofu provides a fairly low-level abstraction, and I think we'd be forgiven for providing a link to a README and calling it a day. But to be earnest for a minute: right now we have funding which gives us a lot of freedom. We probably won't have that funding forever. When it's over, I want to look back at what we did with this opportunity and be satisfied we did our best.

That means we want to describe ufotofu really well. We want to make that description pleasant to read. And we want to add touches that make a reader want to stick around. Those final touches may seem frivolous (we made it so that when you scroll it looks like a UFO is beaming up stuff), but I think (hope) readers see frivolity and recognise it as care.

~sammy

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

I got into distributed systems through Secure Scuttlebutt (SSB). A lot of what happened during my first years in that community has stayed with me until today. In many cases, that is a good thing. In other cases, things are more debatable. My stance on libraries for streams and sinks might be one of those latter cases.

SSB was created by Dominic Tarr. He was well-known in the early node-js community, amongst other things for his pull-streams library. Node-js had its own set of stream abstractions, but Dominic judged he would be more productive if he built SSB on a custom library. And he was probably right. In the early years, people where pretty happy to code against a sane API, it was only years later that the non-standard streaming API became an obstacle for new contributors.

Then, there was André Staltz, who had created cycle.js and later also xstream and callbag. And when Cryptix and Keks implemented SSB in Go, the first thing they did was to build a custom streams library. Patchwork was built on a custom UI framework, built on custom observables.

So for some reason, I ended up absorbing knowledge from a bunch of people who were not afraid of rolling and re-rolling their own stream libraries. And over the years (starting, probably, around 2017), I began forming my own opinions on this matter. At the last few iterations of p2p-basel, I always ended up frantically scribbling on whiteboards to convey the isomorphism between a good stream abstraction and regular grammars. When we started Rust work for Willow, I somehow managed to convince Sam to use part of that funding for writing a new library of stream abstractions in Rust. We also managed to convince NLNet to fund this work. I submitted an essay about stream APIs to Onward! 2024 (which got rejected, but with some very helpful feedback), which has formed the basis of the ufotofu webpage.

I’m rambling. Ufotofu has been simmering for almost ten years now. Over that time I have changed as a person, as a computer scientist, as an engineer. The designs of ufotofu have also changed, but the core principles have always remained the same. I think what I am trying to say is that I am proud of this thing.

I’ve seen many people criticise Dominic for implementing SSB on top of a custom streaming abstraction. Calling it inaccessible, accusing him of not-invented-here syndrome. But I think Dominic made a pragmatic choice: he built his own tools, because he could make them better than the tools that were already around, and the task ahead of him was sufficiently large to justify the initial overhead. We are limited by the quality of our tools, after all. And I feel similarly about ufotofu. Which is why I am ignoring the lessons I perhaps should have learned from SSB. Willow in Rust is implemented on ufotofu, and as of now, I fully believe that to be the right choice.

~Aljoscha

2025Week 4403 Nov

“existence is inefficient”

what's with the name?!

Image

why is this website called ‘worm-blossom’? In this series we will explain why in a concise manner.

Part 1: The French Revolution

France, 1792. The monarchy has been abolished and the French Republic established. Anything seems possible, and everything is up for reconsideration. After all, if you can overthrow a centuries-spanning monarchy, what can’t you do?

Revolutionary ideals, scientific vigour, and a bureaucratic need to govern a fledgling republic have brought about a metric revolution. Variable, localised, and unpredictable systems of weight and measure are being replaced by the gram and metre. These easily divisible units lubricate France’s scientific, military, political and economic progress.

Could the same by done for time? Jean-Charles de Borda, a military engineer and scientist thought so. By dividing the day into ten hours, the hour into 100 minutes, and each minute into 100 seconds, he argued that timekeeping itself could be revolutionised.

By 1793, the National Convention had formally adopted this system. Decimal time was used in all public records, and a number of decimal clocks were prominently displayed in a few French cities.

But mandatory use of decimal time was suspended just two years later. It didn’t seem to offer much of a benefit, nobody was going to enforce its use, and a lot of people felt that a decimal hour (equivalent to 2.4 regular hours) was just too long for an hour. Not to mention how annoying it was going to be to replace every watch and clock in France.

Decimal time would remain largely forgotten for another 203 years, which is where this series will be continued. See you next time!

A teeny megaphone blaring out noise

New willow25 Crate Version

We have released version 0.2.0 of the willow25 crate. This release adds the groupings module, which implements our groupings specification (who would have thought!)

There is a type for each kind of grouping: WillowRange for ranges, Range3d for 3d ranges, Area for areas, and AreaOfInterest for areas of interest.

As with the previous release, we tried to make working with these groupings as convenient as possible. For example, you can use the same method on entries to check for membership in any kind of grouping (a marvel powered by the Grouping trait):

use willow25::prelude::*;

let namespace_id = NamespaceId::from([0; 32]);
let subspace_id = SubspaceId::from([1; 32]);

let entry = Entry::builder()
    .namespace_id(namespace_id.clone())
    .subspace_id(subspace_id)
    .path(path!("/blog/ideas"))
    .timestamp(1793)
    .payload(b"Temps décimal! Très bien!")
    .build().unwrap();

// Range creation from arbitrary Rust ranges!
let time_range = TimeRange::from(..=2025.into());
assert!(entry.is_in(&time_range));

let area = Area::new(
    None, path!("/blog"), time_range,
);
assert!(entry.is_in(&area));

See the new groupings tutorial for more examples of what these APIs feel like, or read the API docs.

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

I accidentally published an incredibly nerdy but surprisingly useful Rust crate this week: order_theory.

The Willow specs often refer to minimal and maximal values (such as SubspaceIds or Paths). Sometimes we also need a notion of successors on these types. So I defined some traits capturing these concepts. And then I moved them into their own crate, because I needed them in several places. And since you cannot very well publish a trait claiming to provide order-theoretic concepts without traits for lattices, I added a corresponding hierarchy of traits as well.

Then I needed our NamespaceId and SubspaceId types to implement SuccessorExceptForGreatest. So I added a generic implementation of all order-related concepts of our new crate for arbitrary arrays, and simply delegated to those from willow25.

Next, I wanted to convert arbitrary Rust ranges (which feature both inclusive and exclusive bounds) to Willow ranges (which are less flexible). Turns out that you can do those conversions exactly if the type in question is totally ordered and has a notion of predecessors and successors. Good thing I had recently defined traits for exactly that!

All Willow groupings support a notion of intersections — the intersection of two groupings is the grouping which contains exactly the entries contained in both original groupings. Why, that sure sounds like a LowerSemilattice. How convenient I had a trait for that concept.

Finally, implementing intersections (and other order-related methods) is not always easy. So I added utilities for property testing to the order_theory crate. And these then found several errors in our previous Willow groupings implementations.

Math! Yay!

~Aljoscha

A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

Not much to report this week as I was travelling. The coming week is going to be busy as November is here, and 2026 is starting to rear its fuzzy little head. I think it's going to be a good year, but only if I write all the proposals and applications like I'm supposed to.

~sammy

Image

2025Week 4327 Oct

“ideally, do the max”

A comic.
      
In the first panel, Anubis, Egyptian God of the Afterlife, addresses a man: "Imhotep! Do you bring anything with you into the afterlife?"

In the second panel Imhotep holds his arms out wide and declares: "Only my retinue of 50 servants, my lord". Said servants are in the background looking unhappy. Anubis replies: "Very well".

In the third panel, Anubis, Egyptian God of the Afterlife, addresses Aljoscha: "Aljoscha! Do you bring anything with you into the afterlife?" 

In the fourth panel Aljoscha holds his arms out wide and declares: "Only my 50 unreleased programming projects!". Behind him, unhappy spectres beg for release.

In the fifth panel, Anubis grimaces as Aljoscha further explains: "I only need to write the docs".
A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

The Big Giant Willow Website Update

willowprotocol.org, the online home of our peer-to-peer storage protocols, has been updated to reflect all of the work that we’ve done on Willowfrom April 2024 until now (work funded by our friends at NLnet! Hartelijk dank!). That’s eighteen months of work in a single serving! Argh!!! So what are the highlights?

ImageFirstly, we’ve reworked Willow Confidential Sync(neé W.G.P.S) to be man-in-the-middle attack resistant. In the old protocol, if two peers who did not know each other before were to sync over a compromised network (e.g. a network with a router modified to intercept traffic), an attacker would have been able to access all sorts of Willow-stored data! As Confidential Sync is expressly designed for secure syncing between untrusted peers, we've redesigned Private Interest Overlap to mitigate this kind of threat. You can learn more about the security properties and threat model of this design here.

The Willow Drop Format (neé Sideloading protocol), which encodes a user-selected subset of Willow data into a single binary (ready to put on a USB key or send via Signal), now is able to add entries without their payload. We’ve also made the encoding more efficient so you can squeeze even more Willow data into a smaller number of bytes.

We’ve added a new page for Willow’25, a recommended set of efficient, secure parameters for all the Willow specs. This way, nobody needs to witness the nightmare of generic types we’ve created for ourselves unless that’s what they really want.

There’s a new Rust section which has tutorials for the crates we’ve released so far. Not only does this section have a completely gratuitous illustration of Willowverse mascots Alfie and Betty riding a giant crab, but we’ve also integrated rustdoc support into willowprotocol.org’s referencing system. This means that our tutorials can reference things like structs and their methods, and link readers directly to up-to-date Rust API documentation. We will be regularly updating this section over the coming weeks and months!

There are new illustrations, diagrams, and comics pretty much everywhere. The Drop Format spec. The Private Interest Overlap Page. Encrypting Entries. The ‘More’ section. The About us page.

Another page which has lots of new illustrations is the front page, which we completely redesigned to make answering the question of “what is Willow?” a bit easier. It also has a big fun illustration for desktop users.

All of this work got stuck in a kind of mega branch comprised of 67,020 additions and 14,590 deletions over 330 commits with the sole description of ‘do not merge yet, wip’. No comment.

~sammy

Shout-out!

Image

Many thanks to Miaourt for their contributions to Bab — which in turn made it into today’s willow25 crate release! Miaourt and I (Aljoscha speaking) did a pair coding session on Friday, which was quite fun. And a good reminder that I am merely an academic dabbling in coding, whereas there are people who have a lot more practice and are actually good at it. I was really impressed with how quickly they grasped everything, and they made several choices that resulted in much cleaner code than what I would have written.

A teeny megaphone blaring out noise

willow25 Crate Release

We have released the first version of our Rust implementation of Willow’25. This initial release implements the full Willow Data Model, with the parameter choices recommended in the Willow’25 spec.

Highlights include

  • an optimised immutable Path struct with zero-copy prefix creation,
  • a Timestamp struct allowing for convenient math such as some_timestamp - 3.seconds(), and
  • the Entry struct which will sit at the core of all Rust Willow applications.

We put a lot of care into making everything as ergonomic as possible. Look at the following snippet — isn’t it neat? We think it is neat!

use willow25::prelude::*;

// Create an entry for a specific payload,
// timestamped with the current time.
let entry = Entry::builder()
    .namespace_id([0; 32].into())
    .subspace_id([1; 32].into())
    .path(path!("/vacation/plan"))
    .now().unwrap()
    .payload(b"See many sights!")
    .build().unwrap();

// Create a new entry based on the previous
// one, with greater timestamp and a new payload.
let updated = Entry::prefilled_builder(&entry)
    .timestamp(entry.timestamp() + 5.minutes())
    .payload(b"See *all* the sights!")
    .build().unwrap();

// Assert that the new entry would overwrite
// the old entry if both were inserted in a store.
assert!(updated.prunes(&entry));

Accompanying this release is a brand-new Rust section on the Willow website with tutorials for our Willow crates. Check it out, even if only for the fun illustration!

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

There might exist gifted programmers who can write non-trivial code and get things right the first time. I am not among them. But still, there is no way around implementing everything for a first time.

Over the past year, we have implemented most everything we specify on the Willow website in Rust. For the first time. Technically, that code does what it is supposed to do. But a lot of it is cumbersome to use, and there are several structural problems I really don’t want to get into (here). Long story short: we are reworking that code, and re-releasing it over the coming weeks. And from what we can tell so far, the new code is pretty enjoyable to work with!

We started this week by releasing an implementation of the Willow data model. Here is the plan for what will follow:

  • Groupings (a requirement for any non-trivial Willow usage),
  • Meadowcap (at that point, you can start sketching out access-control for Willow-based applications),
  • traits for (persistent) storage for entries and payloads (at that point, you can write full application logic against the traits),
  • simple in-memory and persistent implementations of the store traits (at that point, you can write and run full offline-only applications), and
  • the Willow Drop Format (at that point, you can write and run full sneakernet applications).

Once these are done, we will probably slow down a bit, and play around with our own application ideas, end-to-end encryption, network-based synchronisation, and anything else that might come up. In addition to working on Bab, Ufotofu, and Macromania, that is.

~Aljoscha

Shout-out!

Image

Some weeks ago, Joakim, emailed us to point out that UNIX timestamps have less than great behaviour when it comes to leap seconds, and recommended International Atomic Time (TAI) as a better alternative. We have indeed updated the recommended interpretation of the timestamp in Willow to be a TAI timestamp.

And then over the past days, Joakim supplied us with many valuable details around the specifics of choosing a reference epoch (i.e., “which instant does the timestamp zero denote”), saving us from making a non-standard choice due to some confusing (and possibly buggy) documentation and code in the Rust hifitime crate. This might even help those crate developers to fix some independend bugs of their own. Quite a journey — thank you for your help!

2025Week 4220 Oct

“visceral ambivalence”

A comic with four panels. 
        
The first panel's text reads: "When it feels like you're getting nowhere, it can be tempting to try and progress through sheer force of will". Underneath in a drawing of a figure staring down at an empty plant pot, waves radiating from his head.
      
The second panel's text reads: "But most of the time the best thing to do is just back off and let the solution come to you". Below is a drawing of the same scene, except the figure is sunning on the ground in the background, and there is a tiny sprout in the plant pot.

The third panel reads: "Aljoscha often tells me he's going for a walk. When he gets back, he's figured out some novel data structure or mapped out the possibility space of some given premise." Above is drawn a kind of fish-eye lens drawing of Aljoscha walking through the park, the sun shining down on trees and flowers, abstract shapes radiating from his head.

The fourth panel reads: "I have ideas also". Below Sammy is shown showering with a speech bubble above her head. Inside, it shows the phrase "cloud storage" with the word cloud crossed out and replaced with 'crowd'. Sammy smiles with satisfaction and is muttering "genius...".

The Surfers’ Review

Image
“fantastic site wow!!”
“Always excited to see what yourself and Aljoscha are up to, often outside my ability to understand but always filled with such an incredible feeling of hope”
“The humor and art work are also very pleasant.”
~ moid

We thank you for your warm reception to the first instalment of worm-blossom.org.

A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

This week I put a lot of time in non-willow projects. The biggest timesink was Ufotofu, which is a library for working with lazy sequences (“sinks” and “streams”) in Rust. Specifically, I wrote tons of documentation, tests, and documentation tests in preparation for a proper release. This crate has served us well for our async needs while implementing Willow in Rust, and I am giddy about getting to share it more widely soon!

I also put some time into Macromania, our typescript-embedded templating language we use to generate willowprotocol.org in all its hyperlinked and tooltippy glory. We have accumulated a couple of breaking changes we wanted to do to the core of Macromania, and those are now all implemented. Sadly, this means that all the fancy macros powering tooltips etc do not work with that new version of Macromania.

As for the current state of Macromania: the core library is now stable and has pretty solid doc comments (the curious can look at the jsr release), but we still lack high-level documentation for onboarding folks into the madness. We will start working on a proper website featuring this high-level documentation soon-ish (for me, ufotofu has higher priority at the moment). That website might turn out fairly unimpressive initially, because we still lack all the really powerful macros — not only did we break their foundation, but we have also accumulated a bunch of breaking improvements we want to roll out for most of our core macros. Nevertheless, the new version of Macromania already builds this website, and does some fun stuff such as generating an RSS feed and highlighting code snippets. See the source and tinker around if you are feeling adventurous!

This was a highly productive week from my side for ufotofu and Macromania, yet I feel twinges of guilt. Both of these projects are fairly computer-centric, not human-centric. Well, ufotofu in particular; it tries to apply some mathematical principles to streamline a programming experience. Programming for programming’s sake, as if the world could seriously be improved by making computers and programming more efficient and thus even more entrenched... But still, I cannot deny that this kind of work is extremely fun and satisfying to me. In weeks like this, I carry with me some cognitive dissonance centered around escapism and indulgence, counteracted by telling myself that all of this keeps me motivated and will ultimately further Willow (not only by keeping me engaged, but also because our Willow implementations rely on ufotofu and our web presence relies on Macromania).

I want to end on a more light-hearted note, so, erm, have some borderline hyperactive sketches for two pianos (loosely inspired by listening to some Meredith Monk pieces):

Also, *how* is “instalment” correct spelling? ~_~

~Aljoscha

A teeny megaphone blaring out noise

Frugal Async Rust

A whole lot of the Rust code we write for Willow is async, and async Rust comes with some pitfalls. We have decided to make some simplifying assumptions to avoid the worst stumbling blocks. And those assumptions, we have now put on a webpage for others to reference. Frugal Async Rust describes how we forego certain features but in exchange avoid whole worlds of pain.

And we have released a frugal_async crate with some basic utilities adhering to the frugal async principles, for example, an awaitable Mutex:

use frugal_async::mutex::*;
use core::ops::{Deref, DerefMut};
use core::time::Duration;
use smol::{block_on, Timer};

let m = Mutex::new(0);
block_on(futures::future::join(async {
    let mut handle = m.write().await;
    // Simulate doing some work.
    Timer::after(Duration::from_millis(50)).await;
    assert_eq!(0, *handle.deref());
    *handle.deref_mut() = 1;
}, async {
    // This future is "faster", but has to wait
    // for the "work" performed by the first one.
    let mut handle = m.write().await;
    assert_eq!(1, *handle.deref());
}));

We encourage everyone who enjoys the details of async Rust to look at this issue around creating a macro for repeatedly calling several async methods in a loop and acting on the results as they become available.

A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

This week I have been mostly single parenting, so I did not get much programming done. Most of my creative energies went towards cooking meals.

ImageFor breakfast I often turn to the humble porridge. I like to put big chunks of apple in my porridge which cook just long enough to get soft but still keep their structure, kind of like in an apple pie. I like to cook my porridge low and slow, which really annoys everyone else.

ImageYou can't go far wrong with a curry. I break up a cauliflower into little florets and roast them in the oven until they start to caramelise at the edges. I could eat them just like that, but in a curry sauce they're even better. I make a curry sauce from scratch and it is completely inauthentic.

ImageSalmon is great when you're low on time because you just smack it in the oven for a bit. In the colder months I like to do a roast vegetable mix alongside it: potato, beetroot, sweet potato, red onion, topped with some capers. The purple of the beetroot and onions look so good next to everything else.

ImageAnd when it all became too much for me I just ordered pizza. hashtag winning

~sammy

And then, inspiration struck...Image

2025Week 4113 Oct

“we've done the first 99%, now we only need to do the other 99%”

Spindly tunnels wind down deep underground, branching off to aborted ends, and at the very bottom, a cavern with two twits inside. One asks the other: Why are we down here, anyway?
A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

A New Era Dawns

For the past year, we have been pretty busy here at worm-blossom. We have also been pretty quiet. But that is about to change. From now on, you can expect weekly updates about our progress on this very website. As you can probably tell already, we will restrict our reports to the minimal technical details, the bare necessities, the austere metrics of productivity. Nothing else.

Throughout our work on Willow, we increasingly had to take on technical debt in order to meet deadlines. Combined with our inclination of keeping work to ourselves until it is just perfect, we ended up accumulating a vast body of work we stayed fairly quiet about. Not only did this make us look stagnant, it also made us unhappy. But now, with our previous grant obligations fulfilled, and the next grant hopefully leaving us more breathing room, we decided to take action. Going forward, we will

  • identify minimally useful subsets of everything we do and publish these as early as possible,
  • apply this policy not only to future work, but also to our backlog of pretty cool accumulated things,
  • emphasise the exuberance and creative spark that drives us to do our work,
  • invite and mentor contributions to our projects, and
  • share our progress in the most joyful way we can on this website.

And what better way of holding us accountable to these lofty resolutions than to boldly and pinkly announce them on the world wide web?

~Aljoscha

A teeny megaphone blaring out noise

bab_rs Crate Release

We have released the first version of our Rust implementation of Bab. Bab is a family of hash functions inspired by BLAKE3, specifically designed for content-addressed storage systems. Read more about Bab here.

The bab_rs crate provides both a fully generic implementation (the Bab specification is parameterised over choices such as digest sizes) and a ready-to-use implementation of WILLIAM3 (our recommended default parameterisation).

While we have plans — and funding — for some sophisticated features around verified streaming, for now the crate provides the basic API you would expect for incremental hashing: you create a Hasher, repeatedly feed it with bytestrings, and eventually call finish to obtain a digest.

use bab::Hasher;

// Create a hasher.
let mut hasher = Hasher::new();

// Feed data into the hasher.
hasher.write(&[0, 1, 2]);
hasher.write(&[3, 4]);

// Obtain the WILLIAM3 digest.
let digest1 = hasher.finish();

assert_eq(digest1, [
    57, 194, 179, 8, 66, 14, 197, 192,
    5, 56, 128, 49, 249, 78, 60, 119,
    231, 114, 96, 94, 138, 132, 41, 172,
    54, 42, 99, 229, 59, 43, 122, 225,
]);
// Yay =)
A drawing of Sammy, arms crossed with a pencil tucked behind her ear.

This week we renamed two Willow specs: Willow General Purpose Sync and the Sideloading Protocol. I think it's important to sit up and notice when every time someone hears the name of your project, they say "like GPS??? Huh???". And at this point the term 'sideloading' has such a toxic association with locked down mobile platforms that it felt like a completely unforced error.

We renamed them to Willow Confidential Sync and Willow Drop Format, respectively. Not only are these nicer names, but it also meant I could redo the emblem for the WGPS.

A photograph of some sketches of the new Confidential Sync emblem

The WGPS was the only spec which didn't have a nature-inspired emblem, and this has now been remedied. I also remedied the total absence of pink from the lineup. It's a grapevine, because with confidential sync you "heard it through the grapevine". Isn't that precious?

~sammy