Personal ledger · Karachi
One sentence in.
A real ledger
entry out.
Type “paid 4200 for petrol” and it becomes a dated, categorised row against the right account — in rupees, at the rate for that day. Same ledger for gold, loans and a transfer to a cousin.
Built for one person’s money. Being opened up so you can run your own copy.
One-line entry
Type the sentence. Skip the form.
Claude reads the sentence and calls the same server action a form would have called. There is no separate AI path and no special table — parsing is an entry method, not a feature layer.
- Backfill-native. “Bought gold on Eid last year” records against that date, not today.
- Nothing guessed silently. If the account or category is ambiguous, it asks before writing.
- Forms still exist. Every screen can do by hand what the sentence does in one line.
One row in transaction, one row in audit_log, same database transaction.
The core idea
Everything lands in one table.
Not one table per feature. Every financial event in your life is a row in the same universal ledger, and balances are read back out of it — computed on every request, never stored in a column that can drift.
- ExpenseRent, petrol, groceries — money leaving an account.
- IncomeSalary or a payment in, tagged to a source.
- TransferBetween your own accounts, across currencies if needed.
- Loan givenMoney out to a person, and every repayment back.
- Loan takenThe same shape, pointed the other way.
- Gold boughtGrams and a price, held against a holding.
Correct one row and every number that depended on it corrects itself — the account balance, the loan outstanding, the month’s total.
Gold · currency · loans
Rupees first. Gold by the gram.
- Historical FX, not today’s
- Amounts are kept in their own currency — PKR, USD, GBP — alongside a rupee equivalent converted at the rate for the transaction’s own date. Backfill a row from March and it uses March’s rate.
- Gold tracked by weight
- Grams and tolas, not a frozen purchase price. Holdings are valued against the current rate, so the number moves when the market does.
- Loans both directions
- Lent to a cousin, borrowed from a friend. Repayments are ordinary ledger rows, and what’s outstanding is arithmetic over them — never a field someone forgot to update.
Ingest
Forward the email. Drop the statement.
A bank alert forwarded to an inbox becomes a ledger row. So does a statement — a PDF or CSV, a hundred pages long, in a layout nobody documented.
Extraction is reconciled, not trusted. Parsed rows are checked against the statement’s own running balance column. If the arithmetic doesn’t close, the import stops and tells you where — rather than quietly filing plausible-looking numbers.
Imports are idempotent: forward the same alert twice and you still have one row.
Numbers you can trust
Boring where it counts.
- 01
Every change is written down
Each insert, edit and delete writes an audit-log row inside the same database transaction. If the audit write fails, the change rolls back with it. The whole history is reconstructible.
- 02
Nothing is hard-deleted
A delete sets a timestamp. The row stays where it was, out of every query, still there when you need to know what it said.
- 03
Money is never a float
Amounts are fixed-point decimals to two places, gold weights to three, exchange rates to six. No number in this app is the result of binary floating-point rounding.
- 04
One user, one ledger
Every read and every write is scoped to your user id and filters deleted rows out. There is no query in the codebase that can see across that line.
Open source · self-host
Built for one person’s money. Opened up for yours.
This started as a tracker for my own finances in Pakistan: rent, petrol, a transfer to a cousin, gold on Eid. It is a small Next.js app on Postgres — no analytics product, no dashboard for its own sake. Two people use it today; that is the honest number.
It is being prepared for release so you can read every line that touches your money and run your own copy against your own database.