RSSAmplifier

Blog

outl blog

Updates, technical deep-dives, and release announcements for outl — the local-first markdown outliner with provably correct tree-CRDT sync.

outl.appRSS feed ↗10 posts

Latest posts

A keystroke should never wait for the disk

Adding backlinks made outl slow to open and edit on a big workspace: 2800 pages, 211k ops, and every Esc stuttered. The fix wasn't one trick, it was a rule. The op log is the truth, so everything else (the .md, the sidecar, the backlink index, the plugin hooks) is a projection that can happen in the background. How I found the real cost, the numbers before and after, and why the user should never…

The snapshot I couldn't reorder

I imported four years of notes out of another app in one shot, sixty-odd thousand blocks and a couple hundred thousand operations, and the volume dragged a pile of latent move-op and p2p-sync bugs into daylight in a single week. Boot got slow, so I did the obvious thing: cache the materialized tree and skip replaying the op log, and when a device pairs, ship it the tree the other device already…

The status dot that broke sync

I wanted a green dot that said peer-to-peer was alive. The obvious way to know whether a peer answers is to dial it, so the status check stood up a small endpoint, dialed each peer, measured, tore it down. Thirty lines, read-only, can't hurt anything. It broke sync. Not the dot's report, sync itself: the relay keeps exactly one route per node id, my probe wore the device's identity, and…

Your notes never touch our relay

outl syncs your notes device to device with no server in the middle. Almost. There's exactly one server-shaped thing in the path, a relay, and it's the part that makes people nervous: 'wait, my notes go through a machine you run?' The honest answer is that the relay forwards bytes it can't read, and the reason it can't isn't a promise in a policy doc, it's the transport. This walks a single sync…

One node, two writers: how my split-brain fix doubled every title

Last month I gave every page root a deterministic id derived from its slug, so two devices that create the same day's journal land on the same node and merge instead of splitting. It worked. It also quietly doubled the title of every journal opened on two devices: '2026-06-25' became '2026-06-252026-06-25'. The Create op converged to one node exactly as designed, but each device also wrote the…

Two 'today' pages: debugging a bug I couldn't inspect

The journal on my phone flickered between two versions of the same day. I fixed the relay. Still flickering. I fixed a reload race in the frontend. Still flickering. I went to attach a debugger and the webview wouldn't let me. So I rendered the debug output onto the phone screen itself, and the numbers said the bug was in a layer I hadn't looked at once. This is the debugging story, the two wrong…

The op log is the source of truth, so stop keeping everything resident

Importing 80,000 blocks killed outl on the iPhone: iOS jetsam shot the app on open because every block kept a live Yrs CRDT document resident for the whole session. The fix was to treat block text as what it already is, a projection of the op log, and rebuild it on demand. Half a gig of RAM down to a few megabytes, why the rebuild is provably exact, and the replay peak nobody thinks about.

Private by default: outl now syncs peer-to-peer, end-to-end

outl dropped iCloud for iroh. Your notes now sync device-to-device over end-to-end encrypted QUIC — no server, no account, no company in the middle. And you give up nothing: same TUI, desktop, and iOS, same clean markdown, same offline-first UX. Privacy that isn't a downgrade.

outl beta: three clients, one tree

TUI, native desktop, and an iOS app on TestFlight — all reading the same workspace folder. Apple-first, same Rust core, zero cloud. Inside: how outl makes file sync converge using a tree-CRDT and an HLC-ordered op log.

Announcing outl 0.1.0

outl 0.1.0 ships today: a local-first markdown outliner, vim-style TUI, executable code blocks in five languages, and the tree-CRDT algorithm that makes offline sync provably correct.