Family budget app: FastAPI backend, Next.js frontend, PostgreSQL (asyncpg) on Railway. Plaid is the source of truth for linked banks, transactions, categories (PFC), recurring streams, liabilities, and investments.
🎥 Watch the 5-minute walkthrough on Loom — live demo + code tour.
📺 Live sandbox demo (no Telegram bot): see
DEMO.mdfor the URL, login, and Plaid Sandbox walkthrough.🧠 Engineering decisions log:
docs/engineering-decisions.md— the design calls behind the codebase, with the trade-offs.
Authoritative specs live in docs/:
docs/architecture.md— modules, data flow, Railway envs, encryption-at-restdocs/api.md— HTTP APIdocs/data-model.md— tables and invariantsdocs/plaid.md— Plaid products, sync, access-token encryptiondocs/reports-math.md— income/expense classification rulesdocs/insights-math.md— insights feed math
Legacy V1 docs: docs/archive/v1/ (reference only).
Working with this codebase via Claude / agents: see CLAUDE.md.
| Layer | Technology |
|---|---|
| API | Python 3.12, FastAPI, Uvicorn |
| Web UI | Next.js 15, React 19, TypeScript, Tailwind CSS 4, TanStack Query |
| DB | PostgreSQL, asyncpg |
| Hosting | Railway (FastAPI hosts the in-process Telegram bot too + Next.js + Postgres) |
-
Python: create a venv, install deps, configure
.envfrom.env.template(database URL,ADMIN_LOGIN/ADMIN_PASSWORD, optional Plaid keys).# Runtime only (what ships in the Docker image): pip install -r requirements.txt # Runtime + test / CI tooling (pytest, httpx, coverage): pip install -r requirements-dev.txt
-
Run API (watch only
web/so edits undertests/do not restart the server and re-run migrations):
PYTHONPATH=. uvicorn web.main:app --reload --reload-dir web --host 127.0.0.1 --port 8000- Frontend (
frontend/):npm install, setNEXT_PUBLIC_API_URL, thennpm run dev.
Run V2 API tests and auth tests before merging:
pytest tests/v2 tests/test_auth_routes.py tests/test_auth_users.py -qPlaid and domain logic should be covered in tests/v2/ only.
The project hosts two environments:
| Environment | Branch | Purpose |
|---|---|---|
production |
main |
Real Plaid, real users; auto-deploys on merge |
demo |
demo |
Plaid sandbox + reviewer login demo / demo_pass |
Per service:
- FastAPI:
DATABASE_URL=${{Postgres-DB.DATABASE_URL}}(templated — required soPOSTGRES_PASSWORDrotations propagate). SetADMIN_LOGIN,ADMIN_PASSWORD, session-related vars perdocs/architecture.md. Plaid:PLAID_CLIENT_ID,PLAID_SECRET,PLAID_ENV. Encryption:PLAID_ENCRYPTION_KEY(generate once withFernet.generate_key(), never rotate — seedocs/plaid.md#access-token-encryption-at-rest). Optional:PLAID_SDK_TIMEOUT(default 90s),PLAID_WEBHOOK_URL. - Telegram bot (optional):
TELEGRAM_BOT_TOKEN,TELEGRAM_WEBHOOK_SECRET,OPENAI_API_KEY(for receipt OCR). Bot runs in-process inside FastAPI — there is no separate worker. Seedocs/bot.mdforsetWebhookinstructions. - Next.js:
NEXT_PUBLIC_API_URLpointed at the FastAPI public URL.CORS_ORIGINSon FastAPI must include this origin (cookies + credentials).