Case study №03

Sehat: built for my parents

A Hindi-first, voice-first health companion — local-first and free to run

Role
Solo builder
Timeline
2026 · Personal
Scale
2 apps, 1 codebase

Context

Sehat (सेहत) is the one project on this site where I made every decision — product, design, architecture, copy. It's a health companion for Indian parents aged 50–70 and the adult children who look out for them: parents log meals, water, medicines, mood and sleep; their kids see a live dashboard with trends and alerts.

The problem

Three constraints shaped everything. The users: elders won't use tiny-button English apps — so the parent app is Hindi-first, voice-first, with huge tap targets; you say “aaj do roti aur ek katori dal khayi” instead of filling a form. The network:connectivity can't be assumed — the app must be fully usable offline. The budget: ₹0/month — no paid SMS, no paid hosting, no paid APIs.

The architecture

Local-first, with a strict repository layer. Every screen talks to one data interface; behind it sits IndexedDB (via Dexie) that always works, and Supabase that takes over sync, realtime and server-side alert rules when configured. There are zero direct Supabase calls outside the db layer — the UI is provably backend-agnostic.

ONE REACT CODEBASE · TWO EXPERIENCESPARENT APP — HINDI · VOICEFAMILY DASHBOARDREPOSITORY LAYER — ONE INTERFACEDEXIE / INDEXEDDB — ALWAYSSUPABASE — OPTIONALFULLY OFFLINE · NO CONFIG NEEDEDPOSTGRES + TRIGGERSREALTIME + PUSHTRIGGERS MAINTAIN DAILY_SUMMARIES — DASHBOARD NEVER READS RAW LOGSSAME UI EITHER WAY · SYNC MERGES WHEN A BACKEND APPEARS
Fig 1 — local-first: the backend is an upgrade, not a requirement

On the server, Postgres triggers maintain a pre-aggregated daily_summariestable on every write — the dashboard never scans raw logs, so it stays fast at any history length. The offline client mirrors the same recompute so a parent's streak is correct with no network. A long-press SOS inserts an alert that takes over the family dashboard in realtime and pushes to every child's device.

Key decisions

Chose local-first over backend-required

The honest MVP is one household on one phone. The app is fully functional with no config — IndexedDB is the database. Supabase is an upgrade path, not a dependency, and the repository layer means adopting it changed zero UI code.

Chose family code + PIN over phone OTP

OTP costs money per SMS and assumes elders keep one number. Instead: children authenticate normally; parents join with a 6-digit family code and a 4-digit PIN, and a Supabase edge function mints a custom signed JWT with a 1-year expiry — parents never get logged out.

Chose Postgres trigger pre-aggregation over client-side aggregation

Dashboards read only the trigger-maintained summary table. Queries stay O(days), not O(logs), and the rule logic lives in one place — the server — instead of drifting across clients.

Chose family-alert-only SOS over fake ambulance integration

v1 SOS alerts the family — realtime takeover plus push. Hospital dispatch would be theater without a real integration, so it's explicitly out of scope and the UI copy says so. Honesty is a feature.

Outcomes

100%
functional offline
₹0
monthly infrastructure
2
apps from one codebase

Every tradeoff above is documented in a DECISIONS.md written alongside the build — the reasoning, not just the result.