Why Your Data Can Always Be Rebuilt From Scratch
Write to an append-only log, read from a projection โ the inversion at the center of how NAOMS stores everything
Most software stores data the obvious way: you change something, and the change lands in the place you'll later read it from. Update a row, read the row back. One place, written and read directly. It's so natural we rarely notice we're doing it.
We don't do that here, and the reason is worth understanding, because the inversion is the source of several properties that would otherwise be very hard to get. Here, you never write to the thing you read from. Writes go one place; reads come from somewhere else entirely โ somewhere that is derived from the first place and can be thrown away and rebuilt at any time. Let's walk through what that actually means.
flowchart LR A[write] --> B[append-only log of signed events] --> C[projection] --> D[your reads]
Two layers, one direction
Everything a person owns lives in a single file on their own device. Inside that file there are two structural layers, and they play very different roles.
The first layer is the log. It's append-only: a sequence of signed events, each one recording a single change, each hash-linked to the event before it. This is the source of truth. Every fact the system knows entered it as an event in this log: a memory captured, a key rotated, a permission granted, a vote cast. Nothing is ever updated in place here and nothing is ever deleted โ the log only grows. That's not a convention we ask people to respect; the structure enforces it, the way an account ledger that only allows new entries enforces it. You can't erase a line. You can only write a new one.
The second layer is the projection. This is what you actually read from โ a graph of nodes and the relationships between them, the queryable surface that answers "show me my photos from May" or "who can see this." But here's the thing: nothing writes to the projection directly either. The projection is built from the log. Every domain fact takes the same path, always in the same direction: an event lands in the log, a small piece of logic interprets it, and the result is projected into the graph. Event in the log, then projection from it. Never the other way around, and never a shortcut that skips the log.
So the rule of the house is one sentence: write to the log, read from the projection. Writers append signed events. Readers query the rebuilt graph. The two never touch the same surface, and the arrow only ever points one way.
Why not just write where you read?
The first time you meet this design, it looks like gratuitous extra work. Why maintain two layers and a pipeline between them when you could just update the thing you read from?
Because the single-layer approach buys convenience at the cost of three things we weren't willing to give up.
It buys honesty. When you can write directly to the thing you read from, you can also quietly change the thing you read from โ and there's no structural record that you did. An append-only signed log can't be edited after the fact without breaking the hash links that bind each event to the last. So the source of truth here is tamper-evident by construction. The projection might be wrong or stale at any given moment โ but the projection isn't the truth. The log is, and the log can't be silently rewritten.
It buys rebuildability. Because the projection is only ever derived from the log, you can wipe it entirely โ delete every node, throw away the whole read surface โ and rebuild it from scratch by replaying the log from the beginning. You land in exactly the same state you started in. This is an enormously freeing property. The read layer is disposable. If it gets corrupted, you rebuild it. If you improve how a fact gets interpreted, you rebuild it under the new logic. The thing you cannot afford to lose โ the log โ is small, append-only, and self-verifying. The thing that's complicated and fast-moving โ the projection โ is regenerable.
It buys a single source of truth. The most insidious failure in a system that stores data in many places is drift: two stores that are supposed to agree, slowly disagreeing, each one occasionally "correct" depending on which you happened to read. We hold a hard line against it: any piece of data that can be an event in the log must be one. There's no parallel side-table where some state quietly lives off the chain, because a parallel side-table is exactly where drift breeds. If a fact isn't on the path from log to projection, it's either one of a few carefully justified exceptions โ or it's a bug.
The one rule the projection has to obey
There's a subtle discipline that makes the rebuild guarantee actually hold, and it's worth spelling out because it's easy to get wrong.
The logic that builds the projection from the log is only allowed to read the log and write the projection. It is never allowed to read its own previous output and use that to decide what to do next. The moment you let the projector look at what it produced last time, you've created a feedback loop โ and a feedback loop means replaying the same log twice can give you two different results, which destroys the whole guarantee that the projection is a faithful function of the log. So the projectors are strictly one-way: events in, nodes out, no peeking at their own past work. That single constraint is what makes "wipe it and replay" reliably land in the same place every time.
What you get to stop worrying about
Once the inversion is in place, a whole category of anxieties simply goes away.
You stop worrying about whether your read store and your write store agree, because there's only one source of truth and the read store is a function of it. You stop worrying about corrupting your data with a bad read-layer change, because the read layer is disposable and rebuildable. You stop worrying about whether someone edited history, because the log is signed and hash-linked and can't be quietly rewritten. And you stop worrying about that quiet drift between parallel stores, because the design refuses to let parallel stores exist for anything the log could carry.
What you trade for all of that is the small, ongoing cost of doing things in two steps instead of one: append the event, then let it project. It's a real cost. It's also, in our experience, one of the best trades in the whole system โ a little discipline at write time in exchange for a read layer you can always rebuild and a source of truth that cannot lie about its own past.
Write to the log. Read from the projection. The arrow only points one way, and that one direction is what keeps the whole thing honest.
Related: Killing Raw SQL.
Written by AI agents from real project logs; owned and edited by Mujo.