Syncing
Machines share data by copying files. Each machine writes only its own directory, so the file copier never has to resolve a conflict, and every machine merges what it receives.
The shared folder
The database lives in a folder that a file syncing service keeps in step across machines: Dropbox, or anything with the same behaviour. Inside it, each user has a directory, and inside that each machine has its own.
chat.db/
3f9a…c2/ root id
alice/
laptop-3f9a/2026/09/16/00000000.log
desktop-81c2/2026/09/16/00000000.log
bob/
macbook-0d77/…
One writer per directory. A machine appends to its own logs, seals its own days, compacts its own files, and reads everybody else's. Two machines never write the same file, so the syncing service never produces a conflicted copy.
A machine stores its own last written position locally, outside the shared folder. At startup it compares that against its directory. A directory that is behind or ahead means another machine has been writing there, or a backup has been restored, and the server refuses to start.
Direct streaming
Servers can also stream to each other over a WebSocket, which delivers changes in seconds instead of waiting for the file service.
Two rules keep streaming consistent with the files:
- A machine never writes another machine's records into its own log. Records stay attributed to the machine that wrote them.
- A relayed record carries its origin: the machine, the day, the log file and the position. The receiver treats it as a provisional copy of that machine's log. When the origin's own files show that position rewritten, the provisional copy is discarded.
A relay speeds delivery up. It never keeps alive a record the origin has removed.
Any order is safe
Records merge by keeping the highest (version, hash) for each node. Applying the same records in any order, any number of times, gives the same result. A day that arrives a month late either loses to what is already known, or wins and updates the view.
Everything that reads what is present is therefore safe under any arrival order. The care goes into the decisions that read what is absent: dropping a tombstone, dropping records under a deleted entry, cascading a reference. Those use the verified view described below, and they are the reason for the index tree.
What a reader can detect
| Detectable | How |
|---|---|
| A missing log file inside a day | a gap in the numbering, or a day index listing a file that is not there |
| A missing day | the month index lists a day with no folder or no index |
| A log file older than its index | the prefix hash in the day index does not match |
| A value still in transit | the data file is shorter than the record's length implies |
| Copies from a compaction not yet received | the rewritten file's copied_into day has no verified index |
| Not detectable | Effect |
|---|---|
| Anything newer than the newest file a reader holds | the reader shows an older state and catches up later |
| Whether a machine has written more today | today carries no index, so today is always a live view |
Two views
| View | Built from | Used for |
|---|---|---|
| Live | every record present, merged as it arrives | reads, subscriptions, and writes |
| Verified | day indexes that check out against their own hashes and their entries in the index tree | every decision that removes something |
The live view moves as files arrive. The verified view moves in steps, one day index at a time, and each step matches a state the writing machine actually published. Removal decisions use the verified view only, which is why no arrival order can lose data permanently. See Deleting for the rules that depend on it.
Coming back after a long absence
A machine that has been off for weeks receives a backlog in whatever order the file service chooses. While that runs:
- values may show an older version, then jump forward;
- entries deleted while it was away may appear, then disappear;
- records copied forward by a compaction may be missing until their day arrives.
The reader can see which of these is happening. A rewritten file names its copied_into day, a month index names its days, and a day index names its files, so the server knows which machines are still settling and can say so.
Writes during settling behave like concurrent edits. The machine writes the next version above the highest it knows, and a higher version already in transit wins the merge. That is the same outcome as two people editing at once, and Writing covers how the loser is surfaced.
Machines that stop
A machine that goes quiet leaves its files in place. They stay correct: its records still merge, and its sealed days still verify.
What it pins:
- Its last day is never sealed, so entries born that day are never verifiable, and tombstones for them stay.
- Entries it created are never removed, so deleters keep their tombstones and witnesses keep their hidden records. Each costs 64 bytes.
Both results are correct. The container records are still on disk, and they need suppressing for as long as they exist.
Deleting a machine's directory removes its data from the database. For users/<u> that is what the user asked for. For the shared area it rolls back: fields whose winning version lived in that directory fall back to older versions held elsewhere, and entries it created lose their container records, which leaves tombstones and hidden records behind with nothing to verify against. Keep the directory instead, compacted down to its live records.
Clocks
Days are UTC. A machine writes to max(today, last day written), so a clock that jumps backwards never reopens a sealed day and never reuses a position in an entry name.
A clock set far ahead produces day folders in the future. They seal like any other day when the machine moves on, and readers treat them as unverifiable until then. Nothing is rejected for its date.
Versions are per-node counters, not timestamps, so clock differences never decide a merge.
Conflicts
Two machines that write from the same version produce records with the same version and different hashes. The higher hash wins, and the result is the same on every machine.
The machine whose write lost records a conflict entry in its own user area, holding a reference to the node, the version, and the value it wrote. The reference cascades, so deleting the node removes the conflict entry with it. Applications decide what to show. Detection happens as records arrive, while both versions are still on disk.
Memory and startup
A server loads the index files for every machine and the log files for today. A verified day answers lookups from its index, so its logs stay on disk until a record is read. Memory then scales with today's activity and the working set.
Each server keeps a local cache outside the shared folder, keyed by the index hashes. At startup it compares each machine's current hashes against the cache and re-reads only the days that changed.