A few notes about Lace

AI Use Disclosure

The ideas here are my own. I use AI tools in various parts of the writing process, including summarizing my notes and prompts, improving grammar, tightening wording, and checking whether an argument is clear. These posts are as much notes for myself as they are for anyone else who is interested.

2026-06-18 - terminology has become outdated; this post is left for posterity.

Well, have to keep blogging if I ever want to get into the habit. So today: a few notes about Lace.

Lace is me taking everything I learned from developing distributed systems over the years and trying to collapse it into one thing that is actually small enough to reason about.

The original problem is this annoying distributed systems shape where two sides need to figure out, at the same time:

And then every app grows its own little matrix of push, pull, list, get, write, permissions, source policy, target policy, auth rules, cache rules, and special cases. The configuration space explodes. You can make it work through blood sweat and tears, but its a full time job to understand the nuances of a configuration space well enough not to blow it up.

The thing Lace tries to do is merge that space into one question:

what is the set of accepted packets?

Not “am I pushing” or “are you pulling”. Just: these packets belong in this context. Then the exchange algorithm computes the difference between the two stores, moves the missing accepted packets, validates them, and repeats until it reaches the current fixed point.

That sounds almost too simple, but it changes the shape of the problem a lot.

Packets first

The durable unit is a packet. A packet is canonical bytes with a stable hash. There are blobs for raw bytes, plex packets with coordinates like Group, API, Key, and TAI, and seal packets that carry a Verifier and Mark over a plex.

So instead of trusting a database row because it came from a connection, Lace wants everything important to become inspectable packet facts after validation:

The important bit is that offers from the other side are only claims. They can say “I have packet P and it has field Group=team”, but that does not become truth until the bytes arrive and validate locally.

Lacegrams

A lacegram is a little Datalog program over those facts. That is the language part: select facts, derive more facts, then say which packets are in the selected set.

The mergeable lacegram model currently has two views of the same human rule:

LocalSelect(P) :- Have(P), Field(P,'Group',GI,'team').
OfferSelect(P,S) :- Offered(P,S), Offer(P,S,'Group',GI,'team').

LocalSelect runs over trusted local packet facts. OfferSelect runs over untrusted peer offer facts, just to decide what might be worth requesting. Same idea, different evidence worlds.

Then both sides contribute their active mergeable lacegrams. Lace deterministically merges them by intersection. If I say “team packets” and you say “packets marked by Bob’s verifier”, the shared selected set becomes “team packets marked by Bob’s verifier”.

After that, the merged program lowers into the exchange mechanics:

Serve(P) :- LocalSelect(P).
Want(P) :- OfferSelect(P,S).

Those names are internal machinery. The programmer-facing thing is still the selected packet set.

Interlace

Interlace is the exchange algorithm. Very high level:

  1. both sides announce their active mergeable lacegrams
  2. both sides merge them into the same selected-set program
  3. each side advertises packets it can serve from its validated local store
  4. each side evaluates which advertised packets it wants and is missing
  5. those packets are transferred
  6. received bytes are validated and become new local facts
  7. repeat, because the new facts may unlock more packets

That last step is the part I am most excited about. The algorithm naturally supports evidence closures.

Example: “I want to host everything for a group of people, but only if they have cryptographic proof they are members of this group.”

Another device can connect with the equivalent of:

I am a member of this group, here is my proof, and I am looking for everything marked by Bob’s verifier.

In Lace terms this can be modeled as packets plus rules. Membership is not a magical side channel. A membership packet is just another marked packet. The rules can select membership evidence first, and then use the validated membership facts to select more packets.

Roughly:

Member(V) :- Have(P), Field(P,'Group',GI,'team'), Field(P,'API',AI,'member'), Field(P,'Key',KI,V).

LocalSelect(P) :- Have(P), Field(P,'Group',GI,'team'), Field(P,'API',AI,'member').
OfferSelect(P,S) :- Offered(P,S), Offer(P,S,'Group',GI,'team'), Offer(P,S,'API',AI,'member').

LocalSelect(P) :- Have(P), Field(P,'Group',GI,'team'), Field(P,'API',AI,'chat'), Field(P,'Verifier',SI,V), Member(V).
OfferSelect(P,S) :- Offered(P,S), Offer(P,S,'Group',GI,'team'), Offer(P,S,'API',AI,'chat'), Offer(P,S,'Verifier',SI,V), Member(V).

So in round one you might fetch the membership proof packets. After validating them, Member(V) becomes true locally. Then the next evaluation can select the chat packets marked by those members’ verifiers. The system did not need a special “auth then sync” command. It just iterated the selected packet set until no more currently accepted packets were missing.

There is already a simpler test scenario for this shape: Bob contributes Group = team, Alice contributes Verifier = Bob, Bob starts with four packets, and only the two packets that are both in team and marked by Bob’s verifier transfer to Alice. That is small, but it shows the merge: neither side had to implement the cross product manually.

Why this feels like cracking the code

The big deal, to me, is not that Datalog is fancy. The big deal is that the weird distributed negotiation space becomes a local validation problem plus a repeated set difference problem.

Each side evaluates the same exchange program, but with different local facts:

Then a packet moves only when the sender currently serves it and the receiver currently wants it. After it moves, the receiver validates the bytes before believing anything about it. New validated packets can create new facts, which can select more packets, and the loop continues.

It is too early for me to claim it works as desired and can scale. But being able to define these requirements in the first place has really simplified my distributed reasoning.

Instead of building another protocol with separate push/pull/write/list auth paths, the question becomes: can I describe the accepted packet set? If yes, interlace can try to converge the local store toward that set.

There obviously needs to be a DSL on top to make this readable. Nobody wants to hand-write a screen of Field(P,'Group',GI,...) rules for every app. But the model as a whole seems reasonably reasonable.

First written: 2026-06-10
Last edited: 2026-06-18