Split expenses across groups and trips — multi-currency, exact to the rupee, with real authorization.
Highlights — multi-currency balances (never auto-converted) · greedy debt simplification · integer-exact money math · Postgres Row-Level Security as source of truth · unit-tested algorithms (Vitest)
Mobile-first expense splitter for friends, roommates, and trips. Built per the PRD in compass_artifact_wf-c445d463-0d2c-4d2d-bd57-18752d542bd5_text_markdown.md.
- Auth — email + password via Supabase Auth, session in HTTP-only cookies.
- Groups — create, invite via 7-day link, join via token, settings, admin remove with balance-warning gate.
- Expenses — integer-only amounts, 2-step add flow, deterministic remainder split (sorted by UUID).
- Repayments — debtor requests one or many; creditor accepts or rejects. No one-sided settle.
- Multi-currency — balances tracked per
(group, currency, user); never auto-converted. - Debt simplification — greedy max-creditor / max-debtor, run independently per currency.
- Notifications — in-app feed with unread badge; expense-added trigger fans out to participants.
- Profile — per-currency totals and a drill-down of who owes you / who you owe by group.
Next.js 15 (App Router) · TypeScript · Tailwind CSS · shadcn/ui-style primitives · Supabase (Postgres + Auth + Realtime-ready) · React Hook Form + Zod · Vitest.
npm install-
Go to supabase.com and create a new project.
-
In the SQL editor, run each migration in order:
supabase/migrations/0001_init.sqlsupabase/migrations/0002_rls.sqlsupabase/migrations/0003_rpc.sqlsupabase/migrations/0004_triggers.sql
Or use the Supabase CLI:
supabase db push --linked.
Copy .env.local.example to .env.local and fill in:
NEXT_PUBLIC_SUPABASE_URL=https://YOUR-PROJECT.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ... # server-only, never expose
NEXT_PUBLIC_SITE_URL=http://localhost:3000
npm run devOpen http://localhost:3000, sign up, create a group, invite a friend, add an expense, and settle up.
| Script | What it does |
|---|---|
npm run dev |
Next.js dev server |
npm run build |
Production build |
npm run start |
Run the production build |
npm run typecheck |
tsc --noEmit |
npm run lint |
next lint |
npm run test |
Run Vitest unit tests |
npm run db:types |
Regenerate src/types/database.ts (CLI) |
src/
app/ Next.js App Router
(auth)/ public auth pages
(app)/ authenticated app shell
groups/[id]/ balances · expenses · history · settle · settings
profile/ per-currency totals + drill-down
notifications/ feed with accept/reject for repayment requests
join/[token]/ invite redemption
auth/signout/ POST → sign out
components/ UI primitives + feature components
lib/
algos/ splitEqual, simplifyDebts, netBalance
supabase/ client, server, middleware
currency.ts symbols, formatting, integer step
validators.ts Zod schemas (shared client/server)
middleware.ts session refresh + auth gate
types/database.ts hand-maintained DB types (regen via `db:types`)
supabase/migrations/ schema · RLS · RPCs · triggers
tests/unit/ algorithm tests
Amounts are BIGINT integers, never floats. The split RPC and the client splitEqual use the same rule: integer division for the base, distribute the remainder one unit at a time to the first R participants in ascending UUID order. Both sides agree, so re-runs and verifications produce identical shares.
Balances are aggregated by the group_balances view (paid − owed + repaid − received), grouped by (group_id, currency, user_id). There is no cross-currency arithmetic — multi-currency groups show one balance row per currency.
Postgres RLS is the source of truth; the app layer is defense-in-depth. Every table has explicit policies (see 0002_rls.sql). Sensitive multi-row operations go through SECURITY INVOKER RPCs that re-check membership and admin role.
Tracked in §18 of the PRD: receipt OCR, recurring expenses, dark-mode toggle, push notifications, optional FX conversion (snapshot rate), 1:1 friends outside groups, splits other than equal, multilingual UI, native wrapper. Realtime notifications via Supabase Realtime are wired in the schema/RLS and ready to plug in client-side.
npm run testAlgorithms (splitEqual, simplifyDebts, netBalance) have full unit coverage. Add Playwright for end-to-end critical flows in Phase 2 (per the PRD).