worm-blossom

worm-blossom

The endeavours of Aljoscha Meyer and Sammy Gwilym.

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

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

“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.

For 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.

You 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.

Salmon 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.

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

~sammy

And then, inspiration struck...

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?

Firstly, 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!

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!

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 4403 Nov

“existence is inefficient”

Why is it called worm-blossom?!

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!

Next instalment →

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

2025Week 4510 Nov

“Do geese see god?”

2025Week 4510 Nov

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.

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 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.

Why is it called worm-blossom?!

← Previous instalment

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.

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 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'.

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.

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

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).

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.

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 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."

Why is it called worm-blossom?!

← Previous instalment

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.

Next instalment →

A teeny megaphone blaring out noise

meadowcap crate

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

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

Joni Mitchell — I Had a King
The Mothers of Invention — Hungry Freaks, Daddy
Crosby, Stills, & Nash — Guinnevere
The Velvet Underground — Heroin
King Crimson — Lament
Patty Smith — Birdland
Gentle Giant — Aspirations
Kate Bush — The Man with the Child in His Eyes
Prince — I Would Die 4 U
Arthur Russel — Wonder Boy
David Bowie — Looking for Satellites
Marvin Pontiac — Bring Me Rocks
Sonic Youth — Expressway To Yr. Skull
Mr. Bungle — Quote Unquote
Neutral Milk Hotel — In the Aeroplane Over the Sea
Jeff Buckley — Mojo Pin
Sleepytime Gorilla Museum — Ambugaton
Hobson's Choice — We Dream, We Are
King Gizzard and the Lizard Wizard — Mr. Beat
tUnE-yArDs — Gangsta
Louis Cole — Your Moon
Buke and Gase — Sleep Gets Your Ghost
Mingjia & The Tortoise Orchestra — I Had a Mouth Once
Punch Brothers — Familiarity
The worm-blossom advent calendar of 2025 has concluded.

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!

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 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.

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 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.

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!

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!

~Aljoscha

Why is it called worm-blossom?!

← Previous instalment

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.

Next instalment →

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

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

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...

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).

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 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...

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.

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 423 Jan

“all theory and no storage”

A three panel comic.
        
        In the first panel, Aljoscha and Sammy clamber towards a kind of monitor embedded in the rock. The monitor speaks: "Please"
        
        In the second panel, we close in on the screen of the monitor. It says: "You'll help me find mother..."
        
        The final panel is entirely taken up by the monitor's static and waves. In the centre of the noise, it says "won't you?".
A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

I’m writing this on the second leg of my seven hour train journey from the Hague to Basel. Like Aljoscha, I’m going to p2p-basel to listen rather than to speak. Tags: receptive, curious, critical. And frequently: confused. Plenty of leaning over to Aljoscha and whispering “what does that mean?”. I still remember him explaining monotonicity to me a few years ago with the patience of a saint (St. Numbers).

The FOSDEM talk’s shaping up rapidly. I’ve now updated its title and description to better reflect its contents: “Willow - protocols for an uncertain future”. This is going to be a very different take on Willow than we’ve presented before, so I hope it will be interesting even if you’ve been hanging around the project for a while.

This week, I want to start adding more random little one-off drawings to our weekly updates.

~sammy

We're listening to...

Sébastien Tellier

One of my favourite music listening moments is when you have the radio on, or are out and about somewhere, and you hear the beginning of something you can't quite put your finger on until it dawns on you that it's a remix or cover of a song you already love. Check out the Gilligan Ross remix.

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

I have been programming too much over the past weeks; it has been impoverishing my life. Case in point: tomorrow (I am writing this on Wednesday), I am leaving for p2p-basel, and I... have barely thought about that at all, being too busy doing computer things. I don't even have a presentation to give, whereas in the previous years my problem usually was having too many presentation ideas to choose from. Still, there is much to look forward to: I will be seeing old friends again, will meet new interesting folks (there'll be someone from the Pijul VCS, I might finally get closure on some question regarding this paper), there will be interesting discussions and fun evenings. And I'll be meeting Sammy in person for the first time in over a year!

Despite me programming too much, we will not get everything ready by FOSDEM that we had hoped to. Which makes me feel disappointed, despite the fact that I have actually been pretty productive: Bab slice verification is implemented, and I am close to having a simple storage backend that can store a single, contiguous slice. In terms of unlocking funding milestones, I am more productive than needed. Yet the self-imposed FOSDEM-deadline makes me feel bad about the current state of things.

Hence, I label the self-imposed deadline silly, and I will do my best to focus on everything I did implement instead of what is still missing. As usual, there is also the voice that wants Willow to finally become a real thing that people use. But if listening to that voice means wasting my time staring at a small rectangle all day, then I label that voice silly for now as well.

In less silly news, academia and macadamia have a Levenshtein distance of two. Hmmmm, macadamia... now that is something I can get behind!

~Aljoscha

2026Week 530 Jan

“we forgot to remember to express ourselves clearly”

A three panel comic, drawn with much less skill than usual.
        
        In the first panel, a bird says "Well, this is maybe a bit weird..."
        
        In the second panel, another bird continues "but Sammy couldn't do a comic this week..."
        
        The final panel consists of a badly drawn, grumpy owl concluding "and Aljoscha refused to draw anything but birds".
A bad drawing of Sammy, done in mspaint. It is bad because Aljoscha drew it.

Hello readers,

it is me, Samy Gwylim. Because I am focussing on preparing my presentation for fosdem this weekend, I cannot contribute to this week's update. I asked Aljoscha to handle things on his own. And for some reason I thought I wouldn't regret it.

This week, I was reflecting on Kirby Air Riding, the revolutionary home console game from the 90s that combined beat time with choppy jpegs of japanese cuisine. The game wonderfully captured the raw stuffs of human experience, and my assessment is not tinged by nostalgia at all. Maybe I should draw on this game for the fosdem presentation. No better way of standing out from the other presentations than with a healthy sprinkling of pixelated onigiri. Or, since I am low on time, salmon would be great? Genius.

~“samy”

Assorted Mottos

Each of our updates features a short motto in the header for that week. These are mostly snippets out of Sammy’s and Aljoscha’s conversations. Our buffer of collected snippets has grown a bit large by now, and it would be a shame to let good mottos go to waste. So here we go!

worm-blossom:

  • from quirky-but-bearable to completely insane
  • victory is all that matters
  • we are sorry
  • being-able-to-cross-a-moat-full-of-crocodiles-based access control
  • we didn’t have to do this, but we did it anyway
  • nobody had to do this, but we did it anyway
  • I feel like every time we try a crazy thing, we are rewarded for it
  • there's a certain amount of mania we just need to accept as part of your lifestyle
  • gardeners rather than architects
  • I’ve had another thought — oh no
  • look, we’re probably going to generate two more mottos over the course of this call
  • swedish-intention-coded dutch accent
  • why?
  • i *never* just get to state my beliefs as facts anymore, it’s so *unfair*
A bad drawing of Aljoscha, done in mspaint. It is bad because Aljoscha drew it.

So, P2P Basel happened. I had a nice and calm train ride there together with Andreas and Glyph from p2panda, where we avoided talking about p2p stuff for the most part, knowing the weekend would hold more than enough of that. I did get a bunch of music recommendations from Andreas.

The evening had a couple of workshop participants meet for lunch. I had a nice chat with Pierre from Pijul, who gave me some much needed reassurance that most everyone has moved on from their phd thesis topic by the time it comes down to writing, and also that most everyone hates publishing by that time. The key is to do it anyway, I guess. So for the first time in well over a year, I've actually been motivated to keep chipping away at this phd thingy.

Then Friday was the first actual day of the workshop. Except I didn't feel terribly well. I made it through the day, but ended up shambling back to the apartment with a fever. And then I spent the remaining weekend lying in bed, with Sammy bringing me the occasional meal from the venue.

I'm still recovering, so no implementation progress report this week. Though I am a bit concerned how long it has been since the last actual code release. I hope I'll get over the hump of releasing the first storage-backed-Bab version soon, so that things can get back to more regular, incremental feature releases.

~Aljoscha

We're listening to...

Kurt Vile

The first music recommendation Andreas gave to me on the train ride to Basel. I have a soft spot for this kind of repetition-heavy songwriting. Highlights for me are Hysteria and Mutinies.

2026Week 606 Feb

“blobjects”

A four panel comic, titled "SAMMY'S CONFERENCE OUTFITS!!!".
      
      In the first panel, Sammy is wearing a mesh top with high waisted jeans.
      
      In the second panel, Sammy is wearing a long sleeved striped shirt, turtleneck and midi skirt. A hand is thrust into the air behind her.
      
      In the third panel, Sammy is wearing a checkered skirt, dress shirt and scoop top. Someone says "help" in the background.
      
      In the final panel, Sammy is wearing a bolero jacket, scoop top, and houndstooth trousers. In the background, Aljoscha says "Sammy", "i'm dying".
A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

Back at last. And I owe the faithful readers of worm-blossom.org not one… but two editorials, as these last two weeks I was busy attending two very different events: p2p-basel and FOSDEM. The former is intimate and deep, the latter huge and sprawling. And I had a great time at both. Let’s start with Basel.

p2p-basel invites researchers and p2p practicioners to a weekend of lectures and talks at the University of Basel. This year there were only approximately 16 participants, and that creates a very intimate atmosphere for discussion. For example, the catering of the event is also peer-to-peer, with attendees participating in cooking and cleaning. Those moments where you stand shoulder to shoulder with someone while peeling carrots present wonderful opportunities for deep discussion.

A drawing of a conference room.

There are prepared presentations and a time dedicated to spontaneous unconference discussions, of which I moderated two this year.

The first was on ‘delay tolerant UIs', anticipating user confusion stemming from systems where authored data may only reach other users a week later. This might mean the arrival of a message divorced from its original context, or worse, placed into a new context which changes its meaning (e.g. a thumbs up emoji arriving during the discussion of something sad). Clearly we cannot simply lift the UIs of applications which presume near-instant data delivery and expect them to work.

a cropped photograph of a whiteboard with two columns: problems and mitigations. In the former column is a problem arising from delay tolerant systems in a UI, and the latter a suggested way to mitigate it.

The second was on ‘zero / low identity systems’, and I was really happy that so many others were also interested in this topic. Digital identity is often used to link different pieces of data together through a ‘digital double’ which can often be linked to a real person. The risk here speaks for itself, and I had a half-baked idea on how to reduce or eliminate identity in networks: random IDs for all data, with the option for the ‘owner’ of a random ID to derive a new one which everyone else can then verify is provably linked to the original ID. This forms a kind of ‘identity on demand’, or as someone else put it much more eloquently, ‘lazy identity’. It was pointed out that this is not conceptually unlike image board tripcodes (though I’ve since learnt these are apparently quite crackable). Maybe there’s something here, though? It would be interesting to plug such a system into Willow.

Huge thanks to p2p-basel’s organiser Erick, who has relentlessly improved the event year after year.

(CONTINUED BELOW)

~sammy

We're listening to...

Soichi Terada

While working late into the night I kept myself going by listening to the unreal jungle soundtrack of Ape Escape by Soichi Terada. No sooner had I mentioned this to our discord I was linked to this album by the same artist, which is just as jungle, just as fine, but a lot more chill and a lot less monkey. Thanks to rory k. for the recommendation!

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

Weekly updates and depressive episodes are not a good match.

I'm trying to take good care of myself, as much as I am able to. Even if that means taking a break in the exact week where FOSDEM has hopefully pulled a lot of fresh eyes toward us.

I often feel like Willow is coming into being too slowly. But even then, we are still doing meaningful and creative work, and putting it out for everyone to interact with. And that is something I can draw strength from, even when I am too off-balance to meaningfully act on that strength.

~Aljoscha

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

A handful of days after returning from Basel, I departed again for FOSDEM in Brussels. This was my first time attending, and I was a little overwhelmed by the size of the event. So many buildings, people, and cat ears. And for some reason I chose to wear heels the whole weekend.

Luckily I was met by friends of worm-blossom (FoWB) Miaourt and danmw, and we spent Saturday discussing blobjects, fictional Willow companion hardware / tamagotchis, radio-friendly Willow, and Miaourt bearing a seemingly inexhaustible bag of waffles. We had a lovely worm-blossom.org dinner that evening, and although I did not say it at the time because I wanted to appear cool and not overly sappy, I thought to myself that if these were the kinds of people worm-blossom is interesting to, then surely we are doing something right.

I spent Sunday in the Local-first, CRDTs and Sync Engines devroom. My favourite talk was from Modal Collective, in which they detailed their motivation for building a new secure, iPhone-quality OS with p2p at its heart. It shared a lot of themes with my own presentation and I was excited that this line of thinking is turning into action. It’s always lovely to see adz of p2panda, and lovely to meet Tobias Bernard of GNOME too.

Then it was my turn to present. I’ve been working on my talk since the start of the year, and it had been through several incarnations before reaching its current one, leaving me a week and a half to produce approximately fifty drawings. Fortunately I embraced the scrappy spirit of worm-blossom.org, and put together possibly the only talk of FOSDEM with an opening cutscene (music provided by Aljoscha, naturally):

(There is also a live recording if you’d like to experience the live vibes and Q&A portion).

Readers, four days later I am still glowing from the reception to my talk. I’m so, so grateful for all the kind things people said to me afterwards, and the lovely conversations it afforded later that evening.

As I took the train home, I thought about how the p2p community has grown over the years, and how the connections between us have matured alongside the theory and implementations. Perhaps we can do something really wonderful together?

~sammy

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

The music for the intro of the FOSDEM presentation is a beepbox piece, of course:

~Aljoscha

2026Week 713 Feb

“graceful_producer.slurp()”

A two panel comic.
        
        In the first panel, Sammy stands in front of a four panel comic of the same format as last week's. She says "So for this week's comic, let's invert last week's, and have you modelling t-shirts and I'm begging for your help?!"
        
        The second panel is introduced by the text: "Aljoscha's actual reply". Below, Aljoscha appears unmoved, and says "Sure, if you think that's funny". Sammy looks aghast in the corner.
A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

Shortly after last week’s update my body — sensing uncharacterstic relief — instantly succumbed to a flu that I’m still not completely rid of. So I spent a lot of time in bed reading The Rose Field and quietly feeling pleased with myself about my FOSDEM talk doing the rounds. Again, thank you for watching (and maybe even sharing) it, the response has meant a huge amount to me.

With no more slides to prepare, I returned to programming (shudder). It has been a while, and I feel clumsy and slow, especially in Rust. But I’ve started implementing the prerequisite encodings for Willow’s drop format. These use Willow’s private encodings, where items can only be successfully decoded with a bit of common knowledge. This might sound like overkill for a non-interactive method of exchange like the Drop format, but as Aljoscha reminded me: “turns out trying to omit as much data as possible results in short codes”. And what if that makes all the difference for someone to squeeze as much Willow data possible on their low-capacity USB key? That is what I tell myself as I port existing implementations to another, hopefully final incarnation of an Ufotofu-based encoding abstraction.

I’ve also been thinking about Discord, which will reportedly soon start requiring age verification to access all features. I belong to a few communities based on Discord which will sadly disperse if this ends up happening. And it doesn’t seem like there’s any persuasive answer for where to disperse to. Why is that? Is it because the pieces (protocols, people) aren’t there yet? Or is there a deeper problem in that even if we had all those pieces, fusing them into a monolithic whole requires an amount of coordination (and funding) we collectively can’t muster? I have included some links in this week's update to ponder.

~sammy

Shout-out!

Ufotofu now has combinators for chaining together two producers or two consumers, thanks to danmw. My favourite aspect of this contribution needs a bit of elaboration:

The BulkConsumerExt trait has a stage method, whose doc comment features the following paragraph:

This method only exists to preserve symmetry. We cannot even write a meaningful example for it. If you ever find a legitimate use, please let us know!

And as you probably already guessed: Dan's implementation of the chaining combinator for consumers uses stage! I added that method purely to preserve symmetry, anticipating that eventually some use for it would show up and that it would be really annoying if it was missing. Principled design, yay!

(The very fact that using this method is necessary in the first place showcases a problem with the current API design, but that’s a different story :>)

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

This week saw a fruitful discussion about Ufotofu, triggered by Dan’s implementation of chaining combinators. In a nutshell, the problem is that some methods irretrievably swallow up their arguments in case of an error.

There is an obvious fix, which is to have the error case return not only the actual error but also the original arguments. We should be able to apply this for bulk processors, but things are less clear for regular consumers: if consume returned the item it was supposed to consume in case of an error, then that would break the symmetry with the producer design.

For a terrible day-and-a-half or so, I was actually ready to break the symmetry here, in order to make the chaining combinator work without cloning (with a destructive consumer API, the only sensible workaround is to clone items before feeding them into the consumer, so that you still have the clone around after an error). But then I thankfully realised that everything works nicely for types that are Copy. So we can have “normal” APIs that require their items to implement Copy, and then do more expensive and clunky variants that require only Clone.

In any case, the discussion with Dan helped clarify a couple of muddy aspects around ufotofu, and has given me a new perspective on what makes Copy so special in the context of ufotofu items — Copy exempts types from linear typing, thus making them match my purely mathematical mental model of the design space.

~Aljoscha

2026Week 820 Feb

“be kind (non-hypothetically)”

An illustration of two playing cards, one slightly overrlapping the other.
        
        The two cards show illustrations of Aljoscha and Sammy, styled as face cards.
        
        Aljoscha's card shows him playing music, and coding seriously on a computer.
        
        Sammy's card shows her drawing, and working at a computer with some displeasure.
A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

Recently I’ve felt like a weird little frog who is finally finding their weird little microhabitat. The perfect temperature! Exactly the kind of slime I like! Other weird frogs like me! January put me in the path of lots of people who do and make things I’m excited about, and who seem excited about the work I’m doing too.

The only problem being that I actually have to do that work.

Our immediate goal with Willow is to implement the means to save and retrieve Willow data, and to have at least one way to exchange that data. For the latter, we’re going with the Willow Drop Format, which collects lots of Willow data into a single file that you can deliver with whichever way you like to send files.

Since I started working on private encodings last week, we’ve merged implementations of private path encoding, private area encoding, and private authorisation token encoding to willow_rs’s main branch. I had to get all these past the relentless scrutiny of our fuzz testing apparatus (and Aljoscha), so I’m fairly confident these new implementations are robust and well documented. Which feels great! There is definitely some kind of maladjusted satisfaction I get from merging code, which is something I should probably watch out for.

A drawing of Sammy in a frog suit.With that out the way I can get back to the important work of being a weird little frog. This weekend I’ll be heading to the Internet Archive’s new European Headquarters in Amsterdam for a little borrel, where I guess I’ll do borrel-y things like eat little cubes of cheese... and with any luck, meet some more frogs.

~sammy

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

I finally made good progress on the persistent backend of the Bab implementation. All the logic is in place; the remaining steps are to rearrange the public API, document it, and then to add more thorough tests.

And instead of writing a long editorial, I’ll continue programming now.

~Aljoscha

Why is it called worm-blossom?!

← Previous instalment

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

Part 5: Disconnected, but never alone

It is 2014. Dominic Tarr stands on the deck of his boat, surveying the horizon. There is nothing in sight but bright, blue ocean. Tarr is a sailor and programmer, one with a particular interest in distributed systems.

Tarr’s boat, alone at sea, has little to zero internet connectivity. But why should that stop the flow of data? What if a device could opportunistically send and receive data when a connection was available, storing it for later? It could even make entirely new kinds of networks possible, ones without middlemen dictating the terms of network usage.

This approach brings its own different problems, of course. How do two devices determine which data to exchange? How do they know the data they’re exchanging can even be trusted?

No problem is without a solution, however. Tarr has chosen the append-only log as the foundational data structure for his latest experiment. Append-only logs can be efficiently compared and merged, and the power of content-addressing makes their constituent data verifiable. There’s even some precedent: Git, the most popular system for decentralised version control, has content-addressing at its heart.

Tarr’s experiments coalesce into a new protocol known as Secure Scuttlebutt. Over time, SSB attracts thousands of users, each of their devices transformed into their own little boat all at sea, perhaps disconnected, but never alone. SSB’s users comment on each others posts, play chess, and chart a course into a new decentralised future together. And with content-addressing quietly powering it all.

Two years prior, Tarr had written that “The biggest problems facing humanity are really software problems”:

Currently, humanity stands at a crossroads like never before. Great power and great changes are looming on the horizon - genetic engineering, nano technology. Yet also great dangers - Climate Change, peak oil, and the fact that western governments are getting stupider and more corrupt. We need better software to solve these problems.

Perhaps Secure Scuttlebutt was Tarr’s own attempt to solve some of the world’s problems with better software, software which would bring about a new vision of the world through its superior design choices.

But the systems underpinning our software often bring about their own vision of the world whether we want them to or not. Any user of Git will wince at the thought of accidentally committing some secret key to a repository, and the subsequent scramble to undo it. Git’s view of the world is one of a canonic, immutable history, where every change can be tracked down and replayed, and even blame assigned. And with content-addressing quietly powering it all.

But why is worm-blossom called that? Next time, we’ll travel to Berkeley, California, where someone is starting to feel nervous about all this.

Next instalment →

The Surfers’ Review

“just finished reading (and listening to the soundtrack of) the latest Worm Blossom “graceful_producer.slurp()” [...] If this project is not on your radar, you are missing out cause they are doing amazing things!”
“such a lovely website, I really love how hand-made its stuff is. The music and artwork and everything [...] Wait a minute is 'worm-blossom' a pun on 'brainworms'”

worm-blossom.org thanks you for your continued patronage!

2026Week 927 Feb

“bread-first search”

A four panel comic, titled "Blossoquest (continues)".
      
In the first panel, we are closed in on a screen of strange green and grey waveforms and static.

In the second panel, we zoom out a little, seeing a monitor displaying this static fixed to the corner of a wall.

In the third panel, we pull back even more, seeing a strange figure with a wide-brimmed hat on sitting at a bar, alone. The monitor buzzes in the corner.

In the fourth panel, we are outside, looking at a lone building with the sign "LAST CHANCE" above it, and saloon doors. There is some kind of a platform and a railtrack behind it. In the foreground, a figure stands bearing a revolver held ready, looking at the saloon.
A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

When last week's update went up, I was at the Internet Archive Europe HQ in Amsterdam ('HQ' makes it sound like a big shiny building. It's really a cute little house by a canal). Remember last week when I said I felt like a frog finding their special frog habitat? Well this was more frogs. Many people who I'd been looking forward to meeting, and (somewhat amazingly to me) a fair number who'd heard of Willow before. After a lot of conversation, a sensible amount of cheese, and maybe a little bit of schmoozing, I left feeling energised. For years I've been trying to find a local network of people interested in this niche field, and either I was too much of a shut-in (likely), or this community in the Netherlands has finally reached a kind of critical mass and level of interconnectedness with the broader network.

Meanwhile, I worked on willow_rs. Unfortunately (and kind of intriguingly) my work finally presented a dead end in how well Rust is able to accommodate Willow's generic and parametrised ambitions. Aljoscha has (hopefully) done a write up on this below (don't leave me hanging aljoscha). I've started working on an implementation of the Willow Drop Format (WIP pull request here), where I am practicing liberal use of the todo! macro to implement as much as possible without letting little things like not having that dependency yet weigh me down. This is like taking a breadth-first approach where instead I've always taken a depth-first approach to missing bits and pieces.

~sammy

Why is it called worm-blossom?!

← Previous instalment

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

Part 6: Abuse Audit

It is 2018. A cloaked figure sits in their garden in Berkeley, California, typing away on a laptop. Upon their screen is Patchwork, an application for browsing and publishing on the Secure Scuttlebutt network. Cinnamon, a SSB regular, hits the publish buttion. Their latest post is titled “Useful idea: Abuse audit”. They propose to collectively pore through Secure Scuttlebutt “to find every single way it could be used to harm others and then go through that list one at a time and come up with ways (or list the ways that already exist) to stop or deter that behavior.”

Over the next two years, Cinnamon spearheads an abuse audit of Secure Scuttlebutt, eventually identifying 76 potential abuse vectors and compiling them to a now public spreadsheet.

Privately, Cinnamon faces resistance to their audit: this is the internet, you can’t have the benefits of open communication and expect to be sheltered from all its potential harms.

For example: “P2: Revenge porn”:

Person A publishes an image of person B without their consent.

The ideal resolution is to remove this content from the scuttle world completely, which isn't really possible.

or “P3: Doxxing”:

Person A publishes contact info of person B without their consent, with the intent to intimidate them or lead others to harm them.

The ideal resolution is to remove this content from the scuttle world completely, which isn't really possible.

Among worsening and inexplicable health symptoms, Cinnamon becomes frustrated and burnt out by the abuse audit and the apparent reluctance to — or outright impossibility of — addressing any of its identified issues. They work on an alternative safety-focused client for Secure Scuttlebutt called Oasis. But this still isn’t enough to stave off Cinnamon’s dissatisfaction with Secure Scuttlebutt.

In mid-2020, Cinnamon announces Earthstar:

I've been thinking hard about how to improve the user experience of #Oasis, and I kept finding deep things about SSB that I wanted to change:

  • You can't delete messages
  • You can't just download recent things first, you have to wait a long time
  • You can't re-use the same account on multiple devices

That's enough to keep most people from wanting to use SSB. When people say "why can't it be better?" the answer is "sorry, it just wasn't designed to do that ¯\(ツ)/¯"

These 3 constraints all arise from SSB's security properties as an immutable append-only log with hash backlinks. But what if we didn't need immutability? Is it appropriate for a social network?

By relaxing that security property, we solve all 3 problems:

  • You can delete or edit messages
  • You can download any subset of things in any order
  • You can use the same keypair on multiple devices. Now a keypair represents a person, not a device.

Earthstar addresses the key abuse vectors Cinnamon has identified over the past two years, and eschews big-world immutability for a plurality of small-world mutable networks. Cinnamon takes a pragmatic, productive approach to Earthstar, opting for the use of ‘boring’ technologies and a simple implementation which can run in a web browser.

But there’s one little problem. In rejecting the append-only log, Earthstar has adopted unlinked sets of items. In doing so, Earthstar has traded away Secure Scuttlebutt’s relatively simple and efficient synchronisation, with no obvious replacement. How to efficiently figure out what’s different between two devices when all you have is a bag of stuff?

Next time we head to Basel, where the imagination of a familiar bespectacled friend is captured by this puzzle.

We're listening to...

Nikolai Kapustin

I have not been this tempted to sit down and practice a piano piece in a long while (thanks, my dearest sister). But rest easy, I’m dead inside being responsible and will work on worm-blossom things instead (not that I have the required piano technique anyways). ~Aljoscha

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

What is going on in Blossoquest? Since when did it even have that name? Why are we suddenly in a Western? Does the crack in the wall have any meaning? How can Sammy claim dislike for Homestuck and unfamiliarity with Hussy’s work, and yet the third panel looks like Problem Sleuth and the name like a play on Bard Quest? What is going on?!

Anyway, apparently I am supposed to write about the limitations of the Rust type system instead of investigating these mysteries. So here we go!

The Willow specs are highly generic. So naturally, our Rust implementation is full of generics as well. Because this is rather cumbersome, we are also providing a non-generic implementation of Willow’25 for application developers who shouldn’t need to care about the flexibility offered by the parameterisability of Willow.

In our previous codebase, the willow25 implementation merely provided type aliases, with the consequence that compiler messages bombard developers with all those hidden generics. So in the reboot of the codebase, we went for proper wrapper types. We even have a wrapper! macro that automatically generates the structs that wrap the underlying generic types. So far, so good.

The first problem that stems from this arrangement is that of converting between references. Suppose our generic implementation has a function that requires a &Entry<Lots, Of, Generics>. To implement the willow25 version of that function by calling the original function, we need to convert &Willow25Entry into a &Entry<Lots, Of, Generics>. Reference-to-reference conversions are not exactly the most idiomatic Rust. But this was doable, with the `wrapper!` macro even completely hiding the required unsafe and #[repr(transparent)] shenanigans that make it work. And up until Sammy’s recent implementation work, this was good enough.

A tiny figure loses their balance atop a tower of haphazardly balanced blocks, bearing parameter names such as N, S, PD, AT... it'll make sense to Aljoscha, at leastThe real trouble started however once we needed willow25 wrappers for generic types made up from other generic types. For example, a generic AuthorisedEntry struct consists of a generic Entry struct and a generic AuthorisationToken struct. A Willow25 wrapper around AuthorisedEntry would internally still use generic components. But for some code, we would need it to consist of (or at least to be interpreted as consisting of) a non-generic Willow25Entry and a non-generic Willow25AuthorisationToken. But for this interpret-the-generic-components-as-being-non-generic-wrapper-types business, the generics must be instantiated with types that satisfy certain constraints regarding their memory layouts. The exact details are gnarly and do not really matter, the important part is that we ended up in a situation where there would be unsafe code galore. This in itself is bad but not a showstopper, but the real problem was that the safety of this unsafe code would depend on whether you instantiated the generics in a certain way.

The compiler has no way of enforcing any invariants here. And I have never seen any code whose safety depended on properly instantiating type parameters. Calling this situation unidiomatic would not do it justice, I’d rather reach for an unholy abomination whose creators should be barred from writing Rust code ever again.

So what now? We will keep the generic code base for now, but new work will only provide willow25 APIs. Where possible, those will still make use of the underlying generic implementation, but where that does not work we will simply duplicate the generic code and then rip out the type parameters. Long-term, we will probably phase out the generic code base completely, doing concrete willow25-based implementations directly instead.

This is a bit of a shame. The genericity of the specs is a decision I still stand by, and generic implementations do have a certain elegance. But it turns out that (idiomatic) Rust is just not particularly well-suited to it. And we want to keep the codebase accessible and high-quality. The decision to move to non-generic implementations for now stings, but everything else would lead to an idiosyncratic code base that would have made Dominic Tarr proud, and scared off everyone else.

(Also, writing docs for two almost identical versions of our APIs was a pain from the very start, so I cannot truthfully report that I am entirely unhappy.)

~Aljoscha

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

Contributions Welcome: ufotofu Limit adaptors (Rust)

https://codeberg.org/worm-blossom/ufotofu/issues/15

Sammy needs an Ufotofu adaptor that limits how many items a wrapped producer will emit. We had an open issue for this for a while. This is a great opportunity to dive into the ufotofu codebase: small scope, not too difficult, and there even is an implementation in an older version of ufotofu to use as a basis. It would be a shame if such a good first issue had to be wasted by us implementing it ourselves. So if anyone wants to give it a try, now is the chance!

2026Week 1006 Mar

“is there a homomorphism from your opinions to our opinions?”

A five panel comic, titled BLOSSOQUEST.
        
In the first panel, we see a kind of saloon, light pouring in through the open doorway and spilling onto the floor. A large figure sits hunched over the bar.

In the second panel, it's the same scene except now someone is standing in the doorway.

In the third panel, we close in on the figure in the doorway. They have a round head, a face hemmed in by some kind of bonnet. They say "I'm lookin' for The Bottleneck".

In the fourth panel, we see the figure at the bar close up, his face tilted downwards, his eyes closed under his wide brimmed hat. He says: "Gone".

In the fifth panel, it's the same shot except a revolver has now been raised against the large figure's head. He does not look pleased.
A drawing of Sammy, one arm holding up her head with a pencil in it, the other resting on a piece of paper.

Let’s start with nuts and bolts dev diary stuff: I started working on a new persistent store for Willow. It’s a straightforward port of the previous persistent store, which used sled under the hood. Except things were just different enough that I could justify switching to Fjall instead. Translating from sled’s APIs to fjall’s was pretty straightforward! This store is missing a few pieces still, as the Store trait needs to be updated with a few methods for storing and retrieving payloads, which depends on the Bab work Aljoscha has been feverishly boshing away at. So: we’re getting there!

While that bubbles away, what else to do but learn about Tauri, and seeing what I can glue together with our Willow Rust APIs? I got about as far as creating one button before my head filled with question about how we’re going to present all of this to human beings. Like the keypairs which are used to sign entries, for instance. they’re just not anything like user accounts on something like Discord, or even Mastodon.

Since the earthstar days, we’ve referred to the set of features keypairs confer as an ‘identity’. But to me, identity smacks of some kind of vague proving humanity rubbish. But that’s kind of exactly the opposite of what we want Willow’s keypairs to do:

  • they can be used to prove authorship
  • but you can have a lot of them
  • and they have as much or as little identifying information attached to them as you want.

So how about ‘persona’? Data can be verified to be written by a persona, you can have as few or as many as you like, and you can attach as much ‘identity’ information (e.g. display name, avatar) you want… or not. Miaourt suggested using little masks as the iconography for this, which I really love.

I'm not the only working on Willow GUIs right now, so expect more of this!

~sammy

We're listening to...

LOSTAGE

Sami (yay!) shared this video on our Discord server, and I’ve listened to it several times by now. In searching for the band on the web, I found this interview with the lead singer, where they primarily talk about the band’s unconventional decision to not sell their music via larger digital platforms. The philosophy behind that is a lot more nuanced than “Spotify bad!”, I greatly enjoyed reading this. ~Aljoscha

Gorillaz

And something completely different to Aljoscha's pick: animated mega-band Gorillaz. I remember seeing one of their animated music videos as a kid and being completely captured by Hewlett's style and the project's concept. As the years went by, it seemed that the animated aspect of Gorillaz became less and less pronounced. So last week's release of their new eight minute video was such a treat. As for the music: I have been humming the sweetly lilting flute melody from the album's initial track the whole month. And Black Thought's section in The Moon Cave imbues its animated adaptation with all its kinetic power. ~sammy

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

So much has happened this week! This calls for bullet points!

  • Good progress on the Bab implementation: we have well-documented public API for (persistent) single-slice-per-string storage with support for partial verification. Still requires better tests, but then it can be released. Before that, I’m creating a Bab-aware trait for Willow stores in the willow25 crate though, to unblock Sammy’s implementations of a persistent store and the Willow Drop Format.
  • I attended a DWeb preparation event on Saturday. Was nice to run into many familiar faces, and to get to know some new ones. Went home with renewed motivation to get Willow out into the world.
  • I met with Zelf to record an episode of her podcast. It will probably be a while until editing is finished and this episode is released, though.
  • On Tuesday, a bunch of us p2p nerds ended up in a bar with Olivia Jack of Hydra fame. Hydra is a super cool environment for creating 2d graphics — basically a synthesizer for graphics. I may have spent too much time playing around with it afterwards. It is so easy to get something fun going. I also may have started reading the source code and begun sketching a WebGPU-based implementation. The chances for that ever getting anywhere near useable are close to zero, but at least I had a very fun day of rest.
  • I have strong opinions on variable-length integer encodings. So I was happy when I read Brooklyn Zelenka’s new bijou64 spec, which is an interesting variation on my older VARU64 specification. Slightly more complicated, but also slightly more efficient, and arguably easier to detect non-nanonic encodings. An interesting read for the other three people who care about varints.
  • This week I finished a super secret side-project that I will reveal... later :>

~Aljoscha

Shout-outs!

Remember the fruitful discussion about error cases of bulk processors in Ufotofu? Danmw has implemented all resulting changes. Somebody should give them a medal or something! (Sammy, you can draw a mushroom person with a medal, right?)

And the commit messages deserve a special message. Sammy and I appear to have been the worst kind of role models. Nicely wrought, Dan.

We have a new first-time contributor: 🐈‍⬛ added default parameters to our Rust implementation of Willow’25, allowing Sammy to progress the Willow Drop Format format a bit further. And I could finally put the signature for the default entry into the specification.

I give rather pedantic feedback on pull requests to our Rust repositories, and I’m often scared I’m overdoing it. So thank you for putting up with my nitpicking. And thank you for contributing the code in the first place, of course!

2026Week 1113 Mar

“does that satisfy your little platonic heart?”

A three panel comic, titled BLOSSOQUEST.

In the first panel, the small figure in a dress is pointing a gun towards the viewer. She says: "One more time. Where is-"

In the second panel, she is looking upwards, a shadow cast on her. She mutters "the..."

In the final panel, we see an huge figure looming over her, with a peculiarly shaped neck. He says "I *am* the bottleneck. What do you *want*?".
A drawing of Aljoscha, fingers interlaced, eager to deliver some interesting news.

I’ve had repetition on my mind this week, most explicitly triggered by Lu Wilson’s writing on the subject, but also earlier through some reflection on the music I’ve been making. My commentary for last week’s background music track was simply I feel like I'm repeating myself too much with these =/. By now, I’m much more willing to embrace the repetition.

Later, I went to a free improvisation concert played by Fred Frith and Liz Albee, and the weakest passages by far were those where they did not feel comfortable to simply repeat themselves.

And later still, a book I’m reading had the following A. N. Whitehead quote:

[..] no rhythm can be a mere pattern; for the rhythmic quality depends equally upon the differences involved in each exhibition of the pattern. [..] A mere recurrence kills rhythm as surely as does a mere confusion of differences.

I don’t think this makes a lot of sense, at least not when applied to music. No sound happens in isolation, everything is shaped by the sounds that came before. Even when repeating a pattern verbatim, the effect on the listener does not repeat: the first time you hear it, it is new. The second time you hear it, the impression drastically changes, because it is not new. The third time, it is a repetition of something that was already familiar. And so on.

Each repetition has a different quality, because it was preceded by a different context. The quality of change arguably decreases (the difference between the first and the second repetition is more pronounced than that between the 50st and the 51st repetition), but it keeps changing. After 400 repetitions you get a completely different effect than after 32 repetitions. Just like you cannot step into the same river twice, you cannot truly repeat the same pattern.


Oh, wait, this is supposed to be a dev diary, right?

Our Bab implementation now has a well-documented public API, and Sammy has started coding against it! I will spend the upcoming week writing more thorough tests, and next Friday I think we will release it properly. Writing tests is well and good, but I need to accept that I’ll release buggy libraries anyway. So might as well put a disclaimer on the release and ask for bug reports.

~Aljoscha

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

Early this week I made a few wireframes for a kind of generic Willow management UI (with Penpot, which is nice).

Discord’s ‘server’ UIs have always mapped very cleanly to Willow’s namespaces, and it seems negligent not to appropriate their work. But as soon as you start wading into the more unfamiliar concepts like keypair and capabilities the difficulty begins to reveal itself.

What's a root pass? What's a path? What does it mean that this pass was granted by a root pass? Which chatrooms am I allowed to use?

One of the most beautiful ideas about systems like Willow is using a single namespace as a bucket for all kinds of data. What if your Discord server also stored a wiki, a webpage, or anything else its users wanted to put into it? What if you could easily inline your wiki pages into your chats, and weave your data together? And had decentralised permissions which could stay on top of this menagerie of data?

Do you interface with that data in a single mega interface, or many seperate ones? If the latter, does each interface manage its own personas and passes (keypairs and capabilities)? In that case, do you reveal those nitty gritty details like being granted access to /chat/general or something aware of its context, like “General discussion”?

We've bonked our heads against this many times over the years. We tried a reusable React component that you could drop into your (presumably React-powered) app. We tried a kind of web-based mini-OS with different interfaces running inside a managed UI frame. And now I am just starting to think that the solution is a set of agreed conventions, spanning from representation to UI. I uttered the cursed phrase “cultural API” earlier, but I suspect this might just be a rubbish way to say pattern language.

A screenshot of 'CinnamonOS', a web app which sandboxed mini-apps inside of it while providing the UI for managing keypairs and Earthstar shares.

Next week I’ll make an appearance at XPUB (a two-year master in experimental publishing in Rotterdam) to talk about p2p, Willow, and how we communicate our work. I’m really looking forward to this, and I’m challenging myself to find different ways to tell a story about peer-to-peer and Willow. For this one, it’s going to be a timeline, thirty years of lessons learnt the hard way. I feel like it would be irresponsible to only conclude we need to march on with grim hypervigilance. Surely we’re guarding something precious with that vigilance? It’s not just our safety, but something precious, joyful, and unrealised?

~sammy

2026Week 1220 Mar

“very organic, shaped by psychopathologies”

A four panel comic, titled BLOSSOQUEST.
        
In the first panel, The Bottleneck looms over the dressed figure who was just pointing a gun at him. "Well?" he asks.

In the second panel, everyone hears a distant "choo choo". The Bottleneck turns his head to listen.

In the third panel, a close up of the gun wielder's face. She says "There's a *train* a-comin'".

In the final panel, we see a train making it's way along a long straight track. Text on the background says "And I need you to be on it".
A teeny megaphone blaring out noise

Bab 0.5.0-alpha.1 Released

Finally, a new release announcement! There’s a new version of our Bab crate, exposing verifiable streaming and data storage. You can create new storage backends for strings of some known hash, and then feed data into those backends piece by piece. As long as that data is a prefix of the expected string, it gets added to the backend. If it isn’t a prefix, you get an error, and can later resume at that point.

This release is an alpha prerelease instead of a proper new version. The reason is that not all functionality is tested yet: everything to do with the k-grouping optimisation of Bab is untested and very, very likely to be broken. Also, the persistent storage backend is still untested. But in-memory incremental verification works!

By next week, we will test the persistent backend, and then we can move on to testing our new Willow store implementation. Fingers cross that we can release persistent Willow storage next Friday!

Shout-outs!

Ben Frye has added some helpful new adaptors to Ufotofu. And caused a bit of an oh-no-do-we-need-to-break-the-core-trait-designs-again moment in the process, but later found a non-breaking solution to the problem. Phew! And then Ben also stumbled upon an issue with some of the built-in consumers of ufotofu that will need fixing. So quite a fruitful contribution. Thank you!

Also on the ufotofu side of things, Danmw has implemented another pair of adaptors. This was the most recent in a series of contributions, all of which demonstrated a deep care for the codebase (not to mention our symmetry-pondering discussions on Discord). So I went ahead and gave Dan full access to the ufotofu repo and publishing rights on crates.io. Version 0.10.1 of ufotofu was published by Dan! bus_factor += 1 =)

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

This Monday I took the train to Rotterdam to go speak at XPUB.

Before we started, Doriane (who had invited me after we’d met at FOSDEM) gave me a quick tour of the XPUB facilities, way up on the top floor of a building overlooking the Nieuwe Maas. I was really enamoured with the atmosphere of their space, which had zine-filled shelves, boards crowded with posters, and students working away in cosy, personal spaces. It didn’t at all have the feeling of a curated display case, but rather a living, breathing space with work flowing through it. Nice.

And as for my part: we neatly avoided a sammy slideshow situation (thank you doriane) and instead had a kind of guided discussion for two hours, in which we hardly talked about Willow. It was great. I had a big chalkboard behind me on which we assembled a timeline of nearly three decades of peer-to-peer trials and tribulations, from Napster’s legal dismantling to the state-sanctioned deactivation of Apple’s Airdrop. Interjections from the rest of the room were frequent and always thoughtful, and when we finished at 19:30 it felt like we could have kept on going.

Thanks to Tommi for the photo.

Like I said last week, I couldn’t leave it on a downcast note. After all, what are we going to all this trouble for? I argued that we had an opportunity to remember that data is just a byproduct, and that the point is for it to change us somehow. Peer-to-peer protocols have some unique features which encourage us to learn to become conscious stewards — rather than hoarders — of data.

Anyway. Great time, great people, hope to be invited back again some day.


But that’s not all I got up to this week. I also implemented payload storage in the new Willow persistent store, using the Bab implementation Aljoscha has been working on. Streaming, verified storage! It’s a beautiful thing. Or it will be a beautiful thing once we’ve got all the bugs ironed out.

~sammy

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

Do I write a thoughtful editorial, or do I simply finalise this rather late update? While I have no idea about the identity of the BOTTLENECK in Blossoquest, I sure know that for this week’s update I was the bottleneck. But maybe there is time for sharing a few fun ideas still.

  1. A live-coding environment for shaping the sound of digital instruments. Basically, you edit a function that takes as inputs a frequency, a volume level, the time since the note was triggered, and the time since it was released, and you return a value between -1 and 1. This function is called 44000 times per second to produce sound. Oh, and the function has access to the samples it produced for the past x seconds, so that you can employ feedback and self-reference in shaping the sound. (shoutout to the best repo on github, but this would have a live-refreshable js interface instead of compiling rust programs into command line utilities)
  2. A theremin-like, mouse-based (or touch-pen!) UI for playing the live-coded instruments. Should have a toggle for switching between continuous frequencies and a mode that discretises into the 12 notes of well-tempered tuning. And while you move the right hand on the frequency-volume domain, the left hand can do key-presses: one key functions as a sustain pedal, another represents whether to produce sounds at all (pressing and holding that key is like pressing and holding a note on a piano).
  3. A language for writing down instructions for triggering the digital instuments. Basically, what happens if you take the good parts of Lilypond notation and free it from the complexities of score engraving. You end up with a language for structuring events in a flow of time. The language would “compile” into a stream of MIDI or OSC signals. And, the theremin-like UI has a recording function that saves live input in the form of that written language.
  4. Also, for something completely different: cellular automata (think game of life), but with a notion of continuous force fields. Cells can emit a force field, and the values of the field instantaneously propagates between cell updates, with a configurable decay based on distance to the emitter(s). Imagine a tree-drawing automaton where each cell of tree trunk acts as an emitter on the force field of treeness, and when picking a direction for growth, cells of low treeness are more likely to be picked.
  5. And how do I turn the oscillations of a cellular automaton into music?

ALSO: I have taken some work-in-progress drafts of other ideas of mine and moved them from obscure branches into a work-in-progress directory here at the deployment of worm-blossom.org. There is an incomplete but already informative spec draft of the media file formats I wrote about some weeks ago. There is a sketch of a bit-level variable length integer encoding for usage in the media formats. And there’s a draft of the website for Macromania. Without styling, and without most of the content, but the getting-started guide and the introduction-to-macros writeup are functional. You could start using Macromania today!

Oops, I suppose I did end up writing a full editorial.

~Aljoscha