Personal finance app based on the envelope budgeting method, designed natively for Indian users. Built for one person first. Extend later.
Money is divided into virtual envelopes (folders) tied to goals — emergency fund, retirement, monthly expenses, vacation, wedding, etc.
- On credit (income): money auto-splits into envelopes based on rules
- On debit (expense): waterfall depletion — money comes out in priority order, or user tags which envelope a transaction came from
- Goals feel alive: progress bars, projected completion dates, ahead/behind/on-track status
| Layer | Tech |
|---|---|
| Frontend | React + Vite + react-router-dom |
| State | React Context (in-memory, no backend) |
| Scripting | Custom DSL — lexer + parser + evaluator (pure JS) |
| Routing | / envelopes · /envelopes/:id · /transactions · /rules |
src/
lang/ DSL scripting engine
lexer.js Python-style tokeniser (INDENT/DEDENT)
parser.js Recursive-descent parser → AST
evaluator.js AST walker, returns {splits, remaining}
highlight.js Syntax highlighter (HTML spans for overlay)
index.js Re-exports + generateScript()
context/
AppContext.jsx Global state: envelopes, rules, transactions, script
data/
seedData.js Initial mock envelopes + income rules + transactions
lib/
budgeting.js previewIncomeAllocation, previewExpenseDepletion, applySplits
money.js formatMoney (Indian system), parseMoney
dates.js daysUntil, formatDeadline, deadlineStatus
components/
Sidebar.jsx Fixed left nav + total balance + Add Transaction CTA
EnvelopeCard.jsx Folder-row card (accent strip, balance, progress, status)
AddTransactionSheet.jsx Bottom-sheet: income/expense, live split preview
pages/
EnvelopesPage.jsx Home: summary strip + folder list
EnvelopeDetail.jsx Single envelope: big balance, history, Fund/Withdraw
TransactionsPage.jsx Full log with income/expense filter
RulesPage.jsx Visual rules + Script tab with code editor
Replaces dropdown rules entirely. Python-like DSL evaluated purely in the browser.
# Envelope variables are auto-slugged from names:
# emergency_fund → Emergency Fund
# monthly_expenses → Monthly Expenses
on income(amount):
emergency_fund.fill(amount * 0.10)
monthly_expenses.fill(45000) # exact amount
retirement_nps.fill(amount * 0.20)
europe_trip.fill(amount * 0.10)
wedding_fund.fill(remaining) # remaining = amount not yet allocated
on expense(amount):
if monthly_expenses.balance >= amount:
monthly_expenses.drain(amount)
elif monthly_expenses.balance + emergency_fund.balance >= amount:
monthly_expenses.drain(monthly_expenses.balance)
emergency_fund.drain(remaining)
else:
monthly_expenses.drain(monthly_expenses.balance)
emergency_fund.drain(emergency_fund.balance)
europe_trip.drain(remaining)Token set: number literals · identifiers · keywords (on if elif else and or not) ·
operators (+ - * / < > <= >= == !=) · .fill() .drain() · .balance .target ·
remaining (live computed: amount − allocated) · min() max() abs() · # comments ·
Python-style INDENT/DEDENT
Evaluator guarantees: .drain() clamps to available balance, remaining is a getter
(not a variable), all mutations return {splits, remaining} — same shape the rest of the
app uses, so income/expense logic is identical whether using visual rules or script.
Rules page tabs: Visual (dropdown %) | Script (code editor with live preview + Apply)
Envelope { id, name, description, balance, target, deadline, priority, color }
IncomeRule{ id, envelopeId, type: 'percentage', value }
Transaction{ id, kind: 'income'|'expense', description, amount, createdAt,
splits: [{envelopeId, amount, name, color}] }
All state lives in AppContext. Backend-ready: replace useState(initialX) with
useEffect + API call, keep applySplits and the evaluator untouched.
- Gmail API — parse bank email alerts (UTR ref for dedup), most reliable for India
- SMS webhook — Android app / Tasker → POST to backend (iOS impossible)
- Dedup strategy: extract UTR/ref number from source; fallback = hash(amount + ±60s window + type)
- CSV import — all Indian banks export statements; parse HDFC/ICICI/Kotak formats
- Kite Connect API (₹2000/month, well-documented)
- Pull: holdings, positions, historical trade book
- Compute: realized P&L, unrealized P&L, charges (STT, brokerage, GST, SEBI fee)
- Represent portfolio as a "virtual envelope" with live balance from Kite
- RBI AA framework — read-only bank data with user consent
- Requires NBFC-AA license — not a personal-project path today
- When licensed: replace Gmail/SMS parsing with AA data feed
- FastAPI + SQLite (same pattern as the
tutorproject in this repo) - Tables:
envelopes,income_rules,transactions,ledger_entries - Script stored as text blob per user, evaluated server-side for API consumers
- Timeline / calendar view of transactions
- "Month in review" — income vs spend vs saved
- Goal projection: if you keep this pace, you hit target on [date]
npm install
npm run devApp runs at the URL printed by Vite (default 5173).