Time and overtime tracking where your work data never reaches my server.
Every time tracker asks you to hand over a record of when you work, how long you work, and what you are paid for it. That is an unusually personal dataset to keep on somebody else's machine, and none of it needs to be there for the app to function.
So it is not there. OvertimeIQ keeps your shifts, jobs, rates and earnings in a SQLite database that runs in your browser through WebAssembly, and syncs that single file to your own Google Drive. The server never sees a shift. What it does hold is the minimum that genuinely cannot be local: who you are, whether your invite is valid, and whether you are on the paid plan.
| Where it lives | What lives there |
|---|---|
| SQLite in your browser, synced to your Google Drive | Shifts, jobs, hourly rates, multipliers, holidays, every earnings figure |
| Supabase (Postgres) | Account identity, invite tokens, waitlist, subscription state |
If you stop using the app, the database file stays in your Drive and stays readable by anything that reads SQLite. There is no export to request.
- Storage.
sql.jscompiles SQLite to WebAssembly and runs the whole engine client-side. The database is serialised tolocalStorageafter each change, so a reload is instant and works offline. - Sync. One file,
overtimeiq.db, on Google Drive. Modified-time checks before write, so a second device does not clobber the first. - Auth. Google OAuth with PKCE in the browser; the authorisation code is
exchanged server-side, then traded for a Supabase session with
signInWithIdToken. The Drive scope is requested at the same time, which is why sign-in and sync are one step rather than two. - Paid features. The server mints a short-lived ECDSA ES256 JWT; the client verifies it with WebCrypto against a public key shipped in the bundle. The entitlement is never written into the SQLite file, so copying the database to another machine does not copy the subscription.
- Earnings. A pure-function engine with no database access, which is what makes the awkward parts testable: shifts that cross midnight are split into two segments so a Saturday-night shift is not paid the weekend multiplier for its Sunday-morning half. Weekend and holiday multipliers are per job.
Next.js 16 (App Router) · React 19 · TypeScript · Zustand · Tailwind CSS 4 · sql.js (SQLite/WASM) · Google Drive API v3 · Supabase · jose · Recharts · jsPDF and SheetJS for export · deployed on Vercel
Invite-only while it is being built. supabase/schema.sql is the identity and
billing schema and carries the same rule in a comment at the top: work data lives
in SQLite on the user's Drive, never here.
I wanted the local-first architecture in something I would actually use daily rather than in a demo. Six architectures were considered before this one; the reasoning is written up at dev.to/deeshansharma.