STStephen TioluwaniAll work

Pama product · 2026

Local-first productivity · Web + mobile

Founder & product engineer

Track

A timer for my “Sin Hours” turned into a local-first systems project.

Track started as a personal attempt to make wasted time visible. Wanting it to work without the internet pulled me into durable local state, mutation journals, reconciliation, record-level sync, native alarms and all the places where two copies of reality can disagree.

The thesis

“Offline-first is not a cache strategy. It is deciding what truth means when the network disappears.”

7.76 ms

Sync V3 scale gate

A local scale-gate measurement from the Track validation suite, used to catch whole-dataset regressions.

19.48 ms

Notes scale gate

Measured after moving Notes toward record-local persistence and bounded hydration.

68.34 ms

IndexedDB scale gate

A synthetic local validation measurement—not a production latency claim.

01 · Origin

I wanted an uncomfortable number on the screen.

Track came from wanting to know how many hours I was wasting instead of being productive. I called the unfinished portion of a scheduled Track “Sin Hours”. A little harsh, yes, but the product was meant to make avoidance visible rather than comforting.

Version one only needed Tracks, durations, recurrence and a timer. The architectural requirement that changed everything was simple to say: it should still be useful offline.

02 · Sync V3

Every local mutation became an atomic journal entry.

Whole-document persistence made small changes expensive and made failure boundaries fuzzy. Sync V3 introduced record-level IndexedDB and SQLite storage plus an append-only mutation journal with globally unique mutation IDs, device sequence numbers, field-level updates and tombstones.

The journal is written in the same transaction as the local change. If the journal cannot be made durable, the mutation cannot pretend it is safely queued.

03 · Server authority

The server computes accountability; the client proposes intent.

For Tracking itself, online commands are staged and sent to Convex before canonical state becomes durable locally. The server derives sessions, active timers, daily outcomes and Sin Hours instead of trusting a client to submit its own accountability totals.

Retryable connectivity failures can fall back to durable offline intent using the exact same write identity. Deterministic rejection keeps the last confirmed state instead of manufacturing a local success.

04 · Notes durability

Closing an editor should wait for disk, not the internet.

Notes moved away from collection snapshots to record-local deltas and paged hydration. The editor journals its latest draft locally immediately, while canonical online state remains server-authoritative.

Closing waits for the exact latest draft token to be durable. A failed IndexedDB or SQLite write keeps the editor open and retryable; a later successful note cannot mask another note's failed durability barrier.

05 · Native alarms

A reminder needed OS guarantees, not a JavaScript timer.

Track alarms use AlarmKit on modern iOS and Android AlarmManager/setAlarmClock on Android, with push delivery as an independent fallback. Android persists recurrence metadata so alarms can be restored after reboot, timezone or clock changes, app replacement and exact-alarm permission changes.

That feature is a good summary of Track: the user sees a time picker; the engineering problem lives several layers deeper.

What stayed with me

Track made me much more suspicious of software that says “offline supported” without explaining how it reconciles reality afterwards.

Next.jsExpoConvexIndexedDBSQLiteTurborepoAlarmKit / AlarmManager

Next case study

OATGRAPHIX

A motion designer’s portfolio that the client can actually operate.

↗