Self-hosted personal finance workspace for importing bank statements, reconciling a ledger, exploring analytics, and asking an AI assistant questions about your own data.
Ledger Sync is built for people who want useful personal finance analysis without handing over direct bank access or paying a recurring subscription. It supports Indian fiscal years and tax planning, multi-currency display, transaction organization, investments, goals, recurring commitments, and FIRE planning.
- Accepts
.xlsx,.xls, and.csvstatements. - Parses files in the browser with SheetJS.
- Sends validated JSON rows to the API instead of uploading the source file.
- Uses deterministic SHA-256 transaction IDs plus occurrence counters so repeated imports remain idempotent without collapsing legitimate duplicate rows.
- Soft-deletes rows no longer present in the latest import and refreshes analytics after reconciliation.
- Dashboard and fixed Overview for quick financial status.
- Server-paginated transaction ledger with search, filters, tags, saved views, sorting, and CSV export.
- Expense, income, cash flow, period comparison, year review, forecasting, and net worth analysis.
- Investment analytics, SIP projections, XIRR/CAGR returns, and instrument projections.
- 50/30/20 budget analysis, goals, recurring commitments, bill calendar, and anomaly review.
- Merchant intelligence derived from transaction notes, with spend concentration and recurring detection.
- Data health reporting: ledger coverage, last-import row counts, and whether analytics rollups are current.
- Indian income tax, RSU vesting, projected TDS, GST estimation, and FIRE planning.
- Fifteen read-only, user-scoped financial tools.
- App-provided Bedrock mode with a daily message limit.
- BYOK configuration for supported providers with configurable token limits.
- AES-256-GCM encrypted key storage. Current ciphertexts use HKDF-SHA256 with
LEDGER_SYNC_ENCRYPTION_KEY; legacy PBKDF2 ciphertexts remain readable during migration. - Financial data is fetched through tools when needed instead of being copied into a large prompt.
- Compact desktop workspace with grouped navigation, search, notifications, theme control, and AI access.
- Phone bottom navigation plus a complete More page.
- Light and dark themes. New users start on the operating system preference.
- Mobile card layouts for wide data tables and 44px touch targets for primary controls.
- Distinct loading, empty, and retryable error states across protected financial pages.
- Installable PWA that never caches API responses.
The router contains 3 public routes and 26 protected workspace pages.
| Area | Pages |
|---|---|
| Public | Home, demo entry, OAuth callback |
| Top level | Dashboard, Overview, Transactions |
| Analytics | Expense Analysis, Merchants, Income Analysis, Cash Flow, Comparison, Year in Review |
| Wealth | Net Worth, Trends and Forecasts, Investment Analytics, Projections, Returns Analysis |
| Commitments | Recurring, Bill Calendar |
| Planning | Budget Rule, Financial Goals, FIRE Calculator, Anomaly Review, Data Health |
| Tax | Income Tax, Indirect Tax (GST) |
| Utility bar | Upload and Sync, Settings |
| Mobile | More |
See docs/PAGES.md for the route and data-source catalog and docs/HANDBOOK.md for the user workflow guide.
- Node.js 22+
- pnpm 11
- Python 3.13+
- uv
git clone https://github.com/Sagargupta16/ledger-sync.git
cd ledger-sync
pnpm install
pnpm run setup
pnpm run devLocal services:
- Frontend:
http://localhost:5173 - Backend:
http://localhost:8000 - Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
Copy the required LEDGER_SYNC_* entries from .env.example into backend/.env.
LEDGER_SYNC_ENVIRONMENT=development
LEDGER_SYNC_DATABASE_URL=sqlite:///./ledger_sync.db
LEDGER_SYNC_FRONTEND_URL=http://localhost:5173
LEDGER_SYNC_JWT_SECRET_KEY=replace-with-at-least-32-random-characters
LEDGER_SYNC_ENCRYPTION_KEY=replace-with-a-separate-random-key
# Configure at least one provider for real sign-in.
LEDGER_SYNC_GOOGLE_CLIENT_ID=...
LEDGER_SYNC_GOOGLE_CLIENT_SECRET=...
LEDGER_SYNC_GITHUB_CLIENT_ID=...
LEDGER_SYNC_GITHUB_CLIENT_SECRET=...Local frontend development uses Vite's same-origin /api proxy. Set VITE_API_BASE_URL only when the built frontend and API are hosted on different origins.
pnpm run dev # Start backend and frontend
pnpm run check # Lint, type-check, and test both stacks
pnpm run build # Production frontend build
pnpm run format # Format both stacksBackend migrations:
cd backend
uv run alembic upgrade head| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript 6, Vite 8, Tailwind CSS 4, Recharts 3, Motion 12 |
| Backend | Python 3.13+, FastAPI, SQLAlchemy 2, Alembic, Pydantic 2 |
| Database | SQLite for development, Neon PostgreSQL 17 for production |
| State | TanStack Query 5, Zustand 5 |
| Deployment | GitHub Pages, Vercel, Neon |
| Tooling | pnpm 11, uv, Vitest, pytest, Ruff, mypy, ESLint |
The web import path is:
Excel or CSV
-> browser parser and validation
-> authenticated JSON upload
-> normalization and reconciliation
-> PostgreSQL or SQLite
-> analytics refresh
-> TanStack Query cache invalidation
See docs/architecture.md for component and data-flow details.
The hosted installation uses:
| Service | Platform |
|---|---|
| Frontend | GitHub Pages |
| Backend | Vercel serverless, ASGI |
| Database | Neon PostgreSQL 17 with PgBouncer |
Frontend and backend deployments run from main. Database migrations run through the dedicated GitHub Actions workflow when migration or model files change. See docs/DEPLOYMENT.md before changing production configuration.
- Complete Handbook
- Page and Route Catalog
- API Reference
- Architecture
- Calculations
- Database
- Development
- Testing
- Deployment
- Changelog
- Contributing
docs/AUDIT.md and docs/plans/ are dated historical records. Their status headers identify the snapshot or implementation state.
MIT. See LICENSE.