- Status
- Live
- Role
- Designed, built, and run it solo
- Timeline
- Oct 2026 – present
- Stack
- Next.js 16, TypeScript, PostgreSQL (Neon) via postgres.js, Vercel (Blob, Cron, Queues), Resend + React Email
How it works
Tenant isolation
Postgres row-level security per household. Each request runs in a transaction that sets the household with set_config(…, true), and every policy reads it. The app connects as a role without BYPASSRLS — the migration refuses to run otherwise — and the owner role is confined to four listed call sites.
Auth
Email sign-in codes: six digits from a CSPRNG, stored only as a SHA-256 hash, 10-minute expiry, dead after five wrong guesses, and compared in constant time. Rate limits per email, per (hashed) IP, and a global daily cap. Sessions are a signed JWT in an httpOnly cookie, 30 days with sliding renewal.
Reminders
An hourly Vercel Cron tick sends each household’s reminders at its own local send hour; a Vercel Queues topic handles delayed wake-ups. 14 React Email templates.
Files
Statements live in a private Blob store and are served only through an authenticated route; documents upload straight from the browser.
Migrations and import
18 hand-written SQL migrations with a small runner. Both legacy households were imported from the old app’s database in a single transaction at launch.
Verification
14 verification suites (~790 checks) run against a Neon dev branch: RLS isolation, identity, bills, cron, the import, and HTTP.
Admin
The operator console reads only through SECURITY DEFINER functions that never return dollar amounts.