A private, offline-first budgeting app that shows you where every rupee goes.
Free forever. No ads. No paywalls. Open source.
Features ยท Screenshots ยท How it works ยท Self-host ยท Development ยท Roadmap ยท Contributing
ย ย
ย ย
Most money stress isn't about how much you earn. It's about not knowing where it went.
Ledgio is a calm, honest ledger for your money. You log what you earn and spend, set limits for the categories that matter, put money aside for goals, and keep track of who owes whom. Everything is saved on your device first, so it works offline and opens instantly. When you sign in, it syncs to your own private account so your phone and laptop stay in step.
| ๐ Private by design Local-first storage. Your cloud data is isolated to your account with Row Level Security. |
๐ด Works offline Edits queue up offline and sync automatically when you're back online. |
๐ธ Free forever No subscriptions, no paywalls, no ads, no data selling. |
๐งพ Yours to keep Export everything to CSV or JSON whenever you like. |
|
Net worth, income, expenses, what's remaining and your savings rate, all on one screen. Tap the Income or Expenses card to flip between โน amounts and % of income. 10 built-in categories plus your own (15 in total), each with an icon and colour. Set a monthly limit per category and watch the bar move from green to amber to red as you approach it. Create goals with a target and a date, add or withdraw money, and see progress at a glance. Got money left over this month? Ledgio offers to move it into a goal. Track money you lent and money you borrowed, including bills someone paid for you. Record partial settlements and see the full repayment history per person. |
A dated income ledger with opening balances and adjustments. Set your payday (1stโ28th) so months follow your salary, and optionally auto-credit a recurring salary each cycle. Spending by category as a donut chart with a tappable legend, plus a 6-month trend. Pick any month to look back. Lock the app behind a 4-digit PIN (salted SHA-256), with optional biometric unlock via WebAuthn, auto-lock, and a stealth mode that masks every amount on screen. INR, USD, EUR, GBP, AED, SGD, CAD, AUD, JPY, SAR, BDT and NPR with native formatting. Polished light and dark themes on phone, tablet and desktop. |
| Dashboard (light) | Budgets | Savings goals |
|---|---|---|
![]() |
![]() |
![]() |
| Record a loan | Categories | Income vs spend in % |
![]() |
![]() |
![]() |
Real screenshots of the app with a demo account.
Ledgio is a Progressive Web App: install it once and it opens full-screen, works offline and launches from your home screen or dock.
| Platform | How |
|---|---|
| ๐ค Android (Chrome) | Open the live app โ tap Install App (or โฎ โ Install app) |
| ๐ iPhone / iPad (Safari) | Open the live app โ tap Share โฌ โ Add to Home Screen |
| ๐ป Windows / macOS / Linux (Chrome or Edge) | Open the live app โ click the install icon in the address bar |
| ๐งฉ In-app browsers (Instagram, WhatsAppโฆ) | Tap โฎ โ Open in Chrome first, then install |
Ledgio is local-first: every change is written to your device immediately and queued for sync. The sync engine replays the queue to Supabase in order when you're online, retries with backoff when you're not, and quarantines anything the server permanently rejects so nothing is silently lost.
flowchart LR
U([You]) -->|add / edit| UI[Dashboard UI]
UI -->|instant save| LS[(Device storage)]
UI -->|enqueue mutation| Q[Sync queue]
Q -->|online: replay in order| SB[(Supabase Postgres<br/>Row Level Security)]
Q -.->|offline: wait, retry with backoff| Q
Q -.->|permanently rejected| DLQ[Dead-letter queue]
SB -->|pull on sign-in / refresh| LS
SW[Service worker] -->|caches app shell| UI
๐งฉ Modules
The app is plain JavaScript split into domain modules. Each module exposes a small namespace and receives what it needs from app.js through a configure({ ... }) bridge.
| Module | Namespace | Responsibility |
|---|---|---|
supabase-config.js |
SUPABASE_CONFIG |
Project URL/anon key, admin ids, the single Supabase client factory, app version |
sync-engine.js |
LedgioSyncEngine |
Offline FIFO mutation queue, per-record ordering, backoff, auth refresh, dead-letter queue |
income.js |
LedgioIncome |
Dated income ledger, opening balances, pay-cycle ranges, deterministic ids |
expenses.js |
LedgioExpenses |
Expenses, category limits, 80% / 100% warnings |
categories.js |
LedgioCategories |
Built-in + custom categories (max 15), cloud roaming, safe reassignment |
goals.js |
LedgioGoals |
Savings goals and their deposit ledger |
loans.js |
LedgioLoans |
Lent / borrowed ledger, cash vs paid-on-your-behalf, settlements |
vault.js |
LedgioVault |
PIN lock (salted SHA-256), WebAuthn unlock, auto-lock, stealth masking |
auth.js |
โ | Email + Google/GitHub sign-in, password reset, session handling, logout cleanup |
pwa-installer.js |
โ | Install prompts, platform guides, anonymous install/launch counts |
app.js |
โ | Startup sequence, rendering, charts, settings, recurring salary, glue |
๐๏ธ Database schema
Eleven tables, every one protected by Row Level Security. Goal and loan balances are computed from their deposit and settlement records, never stored.
erDiagram
AUTH_USERS ||--|| PROFILES : "has"
AUTH_USERS ||--o{ EXPENSES : "records"
AUTH_USERS ||--o{ BUDGETS : "sets"
AUTH_USERS ||--o{ INCOME_ENTRIES : "earns"
AUTH_USERS ||--o{ USER_CATEGORIES : "customises"
AUTH_USERS ||--o{ GOALS : "targets"
GOALS ||--o{ GOAL_DEPOSITS : "funded by"
AUTH_USERS ||--o{ LOANS : "tracks"
LOANS ||--o{ LOAN_SETTLEMENTS : "settled by"
LOANS |o--o{ INCOME_ENTRIES : "linked cash flow"
| Table | What it holds | Access |
|---|---|---|
profiles |
Name, currency, theme, income baseline, hidden built-ins, reset epoch | Own row only, USING + WITH CHECK |
expenses |
Name, amount, category, date | Own rows only |
budgets |
Monthly limit per category | Own rows, unique per category |
income_entries |
Dated income: add / opening / adjustment, optional loan link | Own rows only |
user_categories |
Custom category name, icon, colour | Own rows, unique name per user |
goals / goal_deposits |
Goal metadata and signed deposits | Own rows; deposits cascade with goal |
loans / loan_settlements |
Direction, kind, principal; settlements | Own rows; settlements cascade with loan |
announcements |
In-app broadcast messages | Read: signed-in users ยท Write: admin |
app_analytics |
Anonymous install / launch counts (no user id) | Insert: constrained ยท Read: admin |
| Frontend | Vanilla JavaScript (ES2020+), HTML, CSS: no framework, no bundler |
| Backend | Supabase: Postgres, Row Level Security, Auth (email, Google, GitHub) |
| Offline | Service worker app-shell cache, local-first storage, custom sync queue |
| Charts | Chart.js |
| Security | Web Crypto (salted SHA-256 PIN), WebAuthn biometric unlock |
| Type & icons | Plus Jakarta Sans, Space Grotesk, Instrument Serif, Font Awesome 6 |
| Hosting | GitHub Pages |
| Quality | 289 headless-browser runtime gates, innerHTML security audit, overflow & desktop-parity checks |
1. Clone and serve locally
git clone https://github.com/Code-Breaker-Ctrl/Ledgio.git
cd Ledgio
python -m http.server 8000Open http://localhost:8000. Any static file server works; there's nothing to compile to run it.
2. Create your Supabase project and run the SQL
In the Supabase SQL editor, run these files in order:
| # | File | Adds |
|---|---|---|
| 1 | backend/supabase-schema.sql |
Base tables and RLS policies |
| 2 | backend/migrations/phase3_offline_sync.sql |
Sync timestamps and triggers |
| 3 | backend/migrations/phase4_savings_goals.sql |
Goals and deposits |
| 4 | backend/migrations/phase5_loans.sql |
Loans and settlements |
| 5 | backend/migrations/phase5b_announcements.sql |
Announcements |
| 6 | backend/migrations/phase5c_analytics_rls.sql |
Analytics read lockdown |
| 7 | backend/migrations/phase5d_analytics_hardening.sql |
Analytics constraints |
| 8 | backend/migrations/phase6_income_entries.sql |
Income ledger |
| 9 | backend/migrations/phase6b_loan_linked_income.sql |
Loan โ income links |
| 10 | backend/migrations/phase7_categories.sql |
Custom categories |
| 11 | backend/migrations/phase7b_reset_epoch.sql |
Multi-device reset epoch |
| 12 | backend/migrations/phase8_loan_kinds.sql |
Cash vs paid-on-your-behalf loans |
| 13 | backend/migrations/phase9_hardening.sql |
Stricter profile and analytics policies |
| 14 | backend/migrations/phase9b_analytics_userid.sql |
Analytics cannot carry another user's id |
| 15 | backend/migrations/phase10_analytics_retention.sql |
Nightly clean-up of launch analytics older than 90 days |
| โ | backend/verify_schema_phase9.sql |
Optional: prints PASS/FAIL for the security policies |
3. Point the app at your project
Edit assets/js/supabase-config.js:
window.SUPABASE_CONFIG = {
url: 'https://your-project.supabase.co',
anonKey: 'your-anon-public-key' // the public anon key, never the service-role key
};
window.LEDGIO_ADMIN_USER_IDS = ['your-admin-user-uuid']; // optionalThen in Supabase โ Authentication โ URL Configuration:
- Site URL: where you host it, e.g.
https://<you>.github.io/Ledgio/ - Redirect URLs: add
/dashboard.html,/login.htmland/reset-password.htmlon that domain - Enable Google and/or GitHub under Providers if you want social sign-in
4. Deploy
Push to GitHub and enable Settings โ Pages โ Deploy from branch โ main / root. Any static host (Netlify, Vercel, Cloudflare Pages) works the same way.
The source files (*.js, *.css) are what you edit; the *.min.* files are generated. All tooling is PowerShell and runs on Windows (or PowerShell 7 elsewhere) with Microsoft Edge installed for the headless checks.
| Command | What it does |
|---|---|
scripts/build.ps1 |
Minifies JS/CSS, syncs SQL migrations, and fails if the version differs between supabase-config.js, sw.js, this README badge and any ?v= in the HTML files |
scripts/test_runtime_gate.ps1 |
Runs 289 runtime gates in headless Edge against tests/headless_regression.html (sync engine, accounting, vault, viewport, landing page integrity and more) |
scripts/verify_innerhtml_audit.ps1 |
Checks that every innerHTML sink is listed and reviewed in docs/security/innerhtml-audit.md |
scripts/check_overflow.ps1 |
Fails if the landing page scrolls sideways at 360โ1440px |
scripts/verify_desktop_parity.ps1 |
Pixel-compares desktop and tablet screenshots before and after a change |
powershell -ExecutionPolicy Bypass -File scripts\build.ps1
powershell -ExecutionPolicy Bypass -File scripts\test_runtime_gate.ps1๐ Project layout
Ledgio/
โโโ index.html Landing page
โโโ login.html ยท signup.html ยท reset-password.html
โโโ terms.html ยท privacy.html
โโโ dashboard.html The app
โโโ sw.js Service worker (app-shell cache)
โโโ manifest.json PWA manifest + shortcuts
โโโ assets/
โ โโโ css/ styles.css (site + shared), dashboard.css (app)
โ โโโ js/ domain modules, see "How it works"
โ โโโ img/landing/ real app screenshots (WebP)
โ โโโ icons/
โโโ backend/ schema, migrations, verification SQL
โโโ supabase/migrations/ mirror of backend/migrations (synced by build)
โโโ scripts/ build, tests, screenshot and audit tooling
โโโ tests/ headless regression and screenshot harnesses
โโโ docs/ security audit, UI review screenshots, debugging notes
- Your data stays yours. Ledgio is saved on your device first; cloud copies live in your own rows, isolated by Postgres Row Level Security.
- No ads, no trackers, no data selling. The only analytics are anonymous install and launch counts, and they are not linked to your account.
- No bank connections. Ledgio never asks for bank logins or card details.
- Hardened by default. Escaped HTML rendering (audited), PIN hashing with Web Crypto, strict RLS on every table.
Read the full Privacy Policy and Terms. Found a security issue? Please open a private security advisory instead of a public issue.
- Offline-first sync engine with dead-letter queue
- Savings goals, loans & debts, custom categories
- Custom pay cycles and recurring salary
- Private vault with PIN, biometrics and stealth mode
- Password reset, Terms and Privacy pages
- Landing page v2 and mobile redesign
- ๐ PDF monthly and yearly statements
- ๐ฆ Play Store packaging (TWA) with native biometric prompt
Have an idea? Open a feature request.
Contributions are welcome: bug reports, ideas, docs and code.
- Fork the repo and create a branch:
git checkout -b feat/my-idea - Edit the source files (not
*.min.*), then runscripts/build.ps1 - Run
scripts/test_runtime_gate.ps1and make sure every gate passes - Use Conventional Commits (
feat(loans): โฆ,fix(sync): โฆ) - Open a pull request describing what changed and why. The runtime gates run automatically on every PR.
Please keep claims in the UI and docs truthful: if the app doesn't do it, don't say it does.
Released under the MIT License. Use it, learn from it, build on it.
If Ledgio helps you see your money more clearly, consider giving it a โญ
Built with care by Code-Breaker-Ctrl ยท Live app ยท Report a bug





