How Penny is built
Penny is a personal-finance SaaS I designed and built end-to-end as a portfolio project - from the Claude Design mock-ups to the database schema, billing and AI assistant. It started life as OWL, a private tool I use for my own transactions, and was rebuilt as a multi-user product with a public demo. - Ramiz, Senior DevOps / Cloud Engineer, Mississauga ON
Open the live demo - no sign-up needed. It runs on generated sample data.
Stack
- Next.js 16 (App Router, React 19 Server Components, Server Actions, Turbopack) with strict TypeScript
- Tailwind CSS 4 design system recreated from my Claude Design file (glass cards, pill controls, striped data marks)
- Clerk for authentication; Supabase Postgres (Canada region) for data, in a dedicated
pathschema - Stripe Checkout, Customer Portal and signed webhooks for the Pro subscription
- Anthropic Claude tool-use agent for Ask Oreo, streamed to the browser
- Voice: Web Speech API push-to-talk and hands-free mode, with ElevenLabs speech when configured
- Twilio SMS bill reminders sent by a daily Vercel Cron job
- Zod validation on every write, deployed on Vercel
Architecture decisions
- Multi-tenant by construction. Every table carries the Clerk
user_id; every query in the server layer filters on it, and writes that reference another row (e.g. a transaction’s account) check ownership in the same SQL statement. The schema has row-level security enabled with no public policies, and the Supabase Data API roles are revoked - only the server can reach it. - Demo mode without a backdoor. The recruiter demo is a signed cookie that swaps the data source for a deterministic generator (seeded PRNG, dated relative to today). Demo edits are applied optimistically in the browser only, so the demo can’t touch real data or be vandalised.
- Every integration is optional. Feature flags derived from environment variables let the app build and run with zero secrets;
/api/healthreports which integrations are live. - Grounded AI coach. Ask Oreo receives a compact JSON summary (monthly totals, category spend, budgets, upcoming bills, goals) and can search transactions by merchant, date or amount through a tool - never raw bank descriptions. It proposes edits through typed tools; the server checks every id belongs to you, and nothing changes until you confirm the card (or say “yes” in voice mode). If the model is unavailable, a rule-based answer engine responds instead. Usage is metered per user per day and gated by plan.
- Idempotent imports & billing. CSV rows are hashed (account, date, amount, normalised description, occurrence) behind a partial unique index, so re-importing a statement never duplicates. Stripe webhooks are recorded by event id before processing.
- Soft deletes + audit log. Deleted transactions can be restored (the Undo toast), and every mutation writes an audit row with the actor (web, AI, import, Stripe, system).
Features
- Home dashboard: 6-month cash flow, earnings and expenses with period comparison, spending by weekday and category, budget pace, cash calendar, goals, recurring bills
- Transactions: search, filters, bulk re-categorise/delete with undo, CSV import for Canadian bank formats, CSV export
- Budgets with pace markers and suggested limits from your 3-month average
- Recurring bills with SQL-based detection from transaction history, and a bill picker that pre-fills amount, schedule and account from your past payments
- Bill reminders by text: 1 week, 3 days and 1 day before by default, customizable per bill, with a live preview of the messages. A secret-protected cron route sends them, and a unique send log makes re-runs safe
- Accounts: net-worth trend rebuilt from transaction history, 90-day balance sparklines and an asset mix
- Goals and loans with projected finish dates
- Insights: rule-based findings (spending jumps, unusual charges, savings rate) plus the AI assistant
- Free vs Pro plan limits enforced on the server
What I’d build next
- Bank connections through an aggregator (read-only OAuth), replacing CSV import
- Email reminders alongside SMS, and two-way replies (“paid” marks the bill done)
- An MCP server so Claude or other assistants can read and edit Penny data with the user’s consent