Building a ledger that reconciles mobile money sales even when the vendor never had signal.
A local-first Flutter app and Go sync service designed to let small vendors record sales offline all day, then reconcile automatically the moment connectivity returns.
Vendors reconcile by memory, not by record.
Small vendors using MTN and Airtel Mobile Money have no easy way to check mobile money transactions against actual stock and sales. Most tools assume the vendor has constant data — most vendors don't. At the end of a market day, reconciliation happens by memory, or not at all.
The app has to be fully usable for an entire market day — recording sales, updating stock, calculating totals — with zero connectivity, then sync cleanly whenever a signal appears for even a few seconds. Kampala's markets have patchy indoor coverage; "offline-first" here isn't a resilience feature, it's the default operating condition the whole design has to start from.
Local-first client, sync queue, source of truth.
// Every write lands locally first. The sync queue drains opportunistically — no user-facing "connecting..." state ever blocks a sale.
Last-write-wins over a full CRDT — for v1.
I'm planning last-write-wins conflict resolution over a full CRDT for v1. A CRDT is the theoretically correct answer, but it would roughly triple the timeline for a conflict pattern most vendors would trigger rarely — a single phone is normally the only writer for a given stock line. The scenario where this creates real data loss (two staff phones editing the same item within the sync window) is scoped as the first thing to fix in v2, likely with per-field version vectors rather than a general-purpose CRDT.