Jonathan Mellette
← All work

In active development · Open source

OpenSteward

Zero-based envelope budgeting that stores your books as plain files in a folder you choose. No server, no account, nothing uploaded. Two people can work the same budget at the same time on different machines and converge — without a lock, a server, or agreeing what time it is.

The hard problem

Two people, two machines, no referee

Multi-device sync normally means a server. The server holds the truth, hands out ordering, and resolves conflicts. Take the server away and you have to answer a genuinely hard question: when two devices each recorded a change, which happened first?

The obvious answer — timestamps — fails. Wall clocks on two machines disagree, sometimes by minutes. A clock that drifts backward can make a later event look earlier. And two events in the same millisecond tie, with nothing to break the tie.

The solution

A Hybrid Logical Clock: each event carries physical time, a logical counter, and the device’s own identifier. Physical time keeps the ordering roughly aligned with reality. The logical counter preserves causality — if this device saw your event before recording its own, the counter guarantees the order survives regardless of what the clocks say. The device identifier breaks remaining ties deterministically, so every device computes the same order independently.

Each device appends only to its own log file, so two devices writing simultaneously cannot corrupt each other — there is nothing to contend over. Opening the folder folds the union of all logs into one state. Convergence without coordination.

Why it matters beyond budgeting

The reason this project is on the site

This is the piece of work I would most want to talk through in a technical interview. It is not a library call. It is an event-sourced, append-only design with a correctly implemented distributed-systems primitive underneath it, and I can defend every line of the reasoning: why a plain timestamp fails two of the three properties the system needs, why the counter is necessary and not merely nice, and what the design gives up in exchange for having no server.

Three months of a church operating budget side by side, showing balances carrying forward

A church operating budget across three months — and the carry-forward is verifiable by eye, which is the point of showing three columns instead of one. Health Benefits is underspent by fifteen dollars a month, so its balance walks 15 → 30 → 45. Building Maintenance walks 87 → 174 → 261. The month cards do the same thing one level up: May’s leftover $5,140 becomes June’s opening line. Nothing resets, because in envelope budgeting nothing is supposed to. Note also the categories — Pastor Housing, Property Insurance, Custodial, Grounds. That is a congregation’s books, not a household’s, and the fund-accounting work is aimed squarely at it.

Honest notes

Where it actually stands

Right now this is a close reimplementation of YNAB’s model, and that is deliberate. There is no IP being taken — envelope budgeting is a spreadsheet pattern older than any software that sells it — but the resemblance is real and worth naming before anyone else does. I build the known-good version first, because you cannot improve a process you have not yet understood from the inside. Reimplementing it is how I find out which parts are load-bearing and which are just what everyone happens to do.

The divergence is the actual project: fund accounting for congregations, where money is restricted by donor intent rather than merely allocated by choice, and a treasurer has to prove at year end that restricted gifts went where they were promised. No consumer budgeting app models that, because no household needs it. This is the same method as the game on this site — build a finished Arkanoid, understand the engine completely, then build the thing you actually meant to make on top of it.

Actively in development and not feature-complete — the README says so, with a checked and unchecked list rather than a marketing paragraph. It requires a Chromium-based browser, because the File System Access API is not universally implemented.

It carries a real limitation I document rather than hide: there is no background polling, so a device sitting on a stale screen can write an edit that overwrites a colleague’s newer figure. That is the honest cost of choosing no server, and the fix is on the roadmap rather than in the code.