- Three separate logins, three separate data silos, three separate UX paradigms.
- No way to ask cross-domain questions ("does sleep affect lifting volume?", "do high-spend weeks correlate with skipped training?").
- Existing apps optimized for daily entry, not for trend analysis or behavioral feedback.
- No product analytics on my own usage — no signal for which features actually got used.
IronLog Analytics Ecosystem
Three-app analytics PWA suite (IronLog, FinanceLog, NutritionLog) on a unified Supabase backend — SSO, real-time sync, and product analytics built solo end-to-end.
The problem
Tracking workouts, finances, and nutrition meant juggling three disconnected apps with no shared data layer, no shared identity, and no way to see how the three correlate over time.
- One identity, one data layer, three focused front-ends — each PWA-installable.
- Real-time sync so the same data shows up across devices and apps without refresh.
- Trend & cohort analytics built in, not bolted on.
- Self-instrumented from day one — every app emits product analytics that feed the next iteration.
System design
Three independent React + Vite PWAs, each deployed as its own Vercel project, all backed by one Supabase instance that owns identity, data, realtime, and storage.
┌──────────────┐ ┌──────────────────┐ ┌────────────────┐
│ IronLog │ │ FinanceLog │ │ NutritionLog │
│ React/Vite │ │ React/Vite │ │ React/Vite │
│ PWA │ │ PWA │ │ PWA │
└──────┬───────┘ └────────┬─────────┘ └────────┬───────┘
│ │ │
│ Vercel edge (per-app deployments) │
└────────────┬───────┴──────────┬───────────┘
│ │
┌───────────▼─────────┐ ┌─────▼──────────┐
│ Supabase │ │ Cloudflare R2 │
│ · Auth (SSO) │ │ · Media blobs │
│ · Postgres │ │ · Exports │
│ · Realtime │ └────────────────┘
│ · Row-level policy │
└─────────────────────┘
- Single identity: one Supabase auth user is the same user across all three apps — no separate signups, no account drift.
- Shared schema, scoped by domain: each app owns its own tables, but all live in one Postgres so cross-domain queries (training × nutrition × spend) are a single SQL away.
- Realtime out of the box: Postgres logical replication powers Supabase Realtime — UI subscribes to row changes, no custom WebSocket layer.
- RLS at the data layer: Row-level security enforces ownership in the database, not in app code, so each PWA gets the same guarantees for free.
- Focused UX: a workout-tracking flow is nothing like a budget flow — separate apps keep each surface honest.
- Independent release cadence: shipping a finance feature doesn't risk regressing nutrition.
- Per-app PWA install: each app gets its own home-screen icon, its own offline scope, its own service worker.
- Smaller bundles: users only download the surface they're using.
Stack & infrastructure
- React 18 — concurrent rendering, Suspense for async data.
- Vite 6 — fast dev server, ESM-native build.
- PWA — service worker, web manifest, installable, offline-aware.
- Charts — trend, cohort, and distribution visualizations.
- Supabase Auth — SSO across all three apps via shared project.
- Supabase Postgres — one database, app-scoped schemas, RLS policies for ownership.
- Supabase Realtime — logical replication powers cross-device sync.
- Cloudflare R2 — S3-compatible blob storage for media and exports.
- Vercel — three projects, one per PWA, each with its own preview deploys per branch.
- Custom domain — ironlog.space on Vercel edge.
- Per-PR previews — every branch ships a live preview URL automatically.
- Session-level monitoring — each app emits session events for engagement analysis.
- Feature-effectiveness tracking — features are instrumented at definition time so adoption is measurable.
- Error capture — front-end exceptions roll up to a single dashboard.
- Self-feedback loop — analytics from one iteration shape the next.
Data model — shape, not detail
One auth tenant; per-app domain tables scoped by user_id; an analytics layer that observes all three.
- auth.users — Supabase-managed; one row per person across all three apps.
- ironlog.* — workouts, sets, exercises, programs. Owner =
user_id. - financelog.* — accounts, transactions, categories, budgets. Owner =
user_id. - nutritionlog.* — meals, foods, intake entries, targets. Owner =
user_id. - analytics.* — session events, feature usage, errors. Joined back to
user_idfor cohort analysis. - RLS policies — every read/write checks
auth.uid() = user_idat the database layer.
Deployment flow
Pull-request-driven, preview-first, zero-touch production.
- Push branch → Vercel builds a preview URL for the affected app.
- Schema changes ship as Supabase migrations checked into the repo.
- Preview URL hits a non-prod Supabase environment so destructive tests are safe.
- Merge to
main→ production deploy on Vercel edge; migrations applied to prod Supabase.
- Rollback: Vercel atomic deploys — instant revert to the prior good build.
- Migrations: forward-only with explicit reversals for anything destructive.
- Secrets: Vercel env vars per environment; nothing in the repo.
- Cost: serverless edges + a single Supabase project — fixed, predictable, low.
Verify it yourself
The fastest proof is the live site — install it, sign in, watch the realtime sync work across devices.
- Live: ironlog.space — open on two devices, sign in with the same account, watch a write on one appear on the other without refresh.
- Install as a PWA: use the browser's install prompt — confirms the manifest, service worker, and offline scope are real.
- SSO: sign in once, navigate between IronLog / FinanceLog / NutritionLog — same identity across all three.
- Inspect network: requests to
*.supabase.cofor data,*.r2.cloudflarestorage.comfor media, served from Vercel edge.
Artifacts
- TODO — install screenshots for each app.
- TODO — Lighthouse PWA score for each app.
- TODO — anonymized product-analytics snapshot.
- TODO — exported architecture diagram replacing the ASCII topology above.
What's next
- Cross-domain insight surfaces — the whole point of one backend is to make these queryable.
- Native push notifications via the PWA push API for habit nudges.
- Export pipeline to land long-term history in a warehouse-shaped store for offline analysis.
- Open the analytics layer as its own embedded surface so feature-effectiveness data is visible inside each app.