SHIPPEDapp

Stagger

Live scoring for the Jäger Bomb, a private annual golf tournament — an installable web app that survives the back nine with no signal and a drinks economy that is an append-only ledger.

CREATED AUG 14, 2026 · UPDATED SEP 22, 2026 · stagger.zckr.dev

PROBLEM

Ten players, one weekend, and a paper card that is illegible by Sunday. Every app that could have replaced it wanted accounts, an app store listing, and a cell signal — and the back nine has none of those.

HYPOTHESIS

Invite-only Google sign-in, add-to-home-screen distribution, and an offline queue make a scorecard that ten people will actually use, and a leaderboard that updates on every phone at once is what makes them keep it open.

ARCHITECTURE

Supabase Postgres with row-level security on all nine tables and two security-definer helpers; the app never bypasses it. Scores, rounds and chat are in the realtime publication, so standings recompute from raw rows and push over a websocket — no polling. Every write tries the network first and parks in an IndexedDB queue on failure, last-write-wins per hole against the unique constraint that the offline upsert already targets. The drinks economy stores no balances: it folds an append-only log of purchases, assignments and consumptions, and a database check constraint makes 'drinks you were forced to take never bank' unfakeable rather than merely enforced in code.

STACK

Next.js 16React 19TypeScriptSupabasePostgresTailwind v4

RESULT

Shipped and played on. The scoring rules still change; the module they live in was built expecting that. September reshaped the event page around what just happened, made WAR a property of the round rather than the weekend, seeded the eighth Jäger Bomb with the three Legends courses off their official cards, and made releases cut and tag themselves once CI is green.

MEASURED

metricvaluebaseline
tables with RLS enabled9 / 9—
runtime dependencies5 · outside Next and React—
app store submissions0 · distribution is add-to-home-screen—

LEARNINGS

  • Deriving a balance from an append-only log gives you the audit trail and undo for free, and makes the one rule that matters a database constraint instead of a promise.
  • Supabase Realtime rejects the newer publishable key and dies silently — the live leaderboard needs the legacy anon JWT.
  • Designing the offline path first is cheaper than retrofitting it, because it decides the shape of the unique constraint.

TAGS

golfpwarealtimeoffline-first