Ledger Sync is a self-hosted personal finance dashboard built as a full-stack application with clear separation between backend and frontend. The system imports Excel bank statements, reconciles transactions via SHA-256 hashing, and delivers 24 pages of financial analytics -- from spending breakdowns to investment tracking and tax projections -- with multi-currency display support.
- Responsibility: HTTP endpoints, request/response handling
- Components:
main.py- FastAPI application setup, middleware, CORS, security headersauth.py- Token refresh, logout, profile managementoauth.py- OAuth login via Google and GitHub (authorization code exchange viahttpx)analytics.py- Analytics endpoints (overview, KPIs, trends, behavior)calculations.py- Financial calculation endpointsexchange_rates.py- Exchange rate proxy with 24h cache (frankfurter.dev)deps.py- JWT authentication dependency (get_current_user)- Routes requests to business logic and return JSON responses
- Authentication: OAuth-only (no email/password). Google/GitHub OAuth providers configured via environment variables. Backend exchanges authorization codes for user info, then issues JWT access/refresh tokens.
- Responsibility: Business rules, algorithms, data processing
- Components:
reconciler.py- Transaction reconciliation logic- Insert new transactions
- Update existing ones
- Soft-delete stale records
- Uses SHA-256 hashing for deterministic IDs
sync_engine.py- Data synchronization orchestrationcalculator.py- Financial calculations (income, expenses, insights)query_helpers.py- Shared SQL aggregation helpers (income_sum_col,expense_sum_col,build_transaction_query) used by bothcalculations.pyandanalytics.pyto eliminate duplicated CASE/SUM patternstime_filter.py- Time range filtering logic
- Responsibility: Database interactions, ORM operations
- Components:
models.py- SQLAlchemy models (Transaction, Account, etc.)session.py- Database session managementbase.py- Base configuration- Direct database queries and transactions
- Responsibility: Data import and validation
- Components:
excel_loader.py- Read Excel files, parse data (used by CLI only; web uploads are parsed client-side)csv_loader.py- Read CSV files (used by CLI only)normalizer.py- Clean, transform, standardize data.normalize_from_dict()handles JSON upload rows;normalize_row()handles DataFrame rows from CLIvalidator.py- Validate data integrity and formathash_id.py- Generate deterministic transaction IDs
- Responsibility: Helper functions, logging, common utilities
- Components:
logging.py- Centralized logging configuration
For each imported transaction:
1. Calculate SHA-256 hash of (date, amount, category, account)
2. Look up hash_id in database
3. If not found: INSERT
4. If found but data changed: UPDATE
5. If found and same: SKIP
6. Mark old transactions not in import as SOFT_DELETEThe projection system is entirely client-side (no backend computation). Data flows through these layers:
Settings (SalaryStructureSection)
-> preferencesStore (Zustand, persisted to API)
-> TaxPlanningPage reads salaryStructure, rsuGrants, growthAssumptions
-> projectionCalculator.ts (pure functions)
-> projectFiscalYear(targetFY, salary, rsus, growth, fyStartMonth)
-> Returns ProjectedFYBreakdown (gross, basic, variable, RSU vestings)
-> projectMultipleYears(salary, rsus, growth, fyStartMonth)
-> Returns array of projected breakdowns for comparison table
-> getRsuVestingsByFY(grants, fyStartMonth, appreciation)
-> Returns FY-keyed vesting amounts with stock appreciation
-> taxCalculator.ts computes tax on projected gross
-> Returns slab breakdown, cess, surcharge, rebate
Key design decisions:
- Pure functions:
projectionCalculator.tshas zero side effects, making it trivially testable - FY-keyed salary: Each fiscal year has its own salary structure, allowing users to track raises
- Growth compounding: Projections compound from the latest user-entered FY (not from the current FY)
- RSU appreciation: Stock price appreciates at user-configured rate from grant date to vesting date
Income = Sum of all Income type transactions
Expenses = Sum of all Expense type transactions
Net = Income - Expenses
Savings Rate = (Income - Expenses) / Income
# 50/30/20 Budget Rule (based on income)
Needs = Essential expenses (should be ≤50% of income)
Wants = Discretionary expenses (should be ≤30% of income)
Savings = Income - Expenses (should be ≥20% of income)
# Investment NET calculation
NET Investment = Transfer-In amounts - Transfer-Out amounts- Responsibility: Screen-level components, layout, page composition
- Components (24 pages):
HomePage- Landing pageDashboardPage- Main dashboard with KPIs, sparklines, and quick insightsUploadSyncPage- Hero upload UI with sample format previewTransactionsPage- Transaction table with filteringSpendingAnalysisPage- 50/30/20 budget rule analysisIncomeAnalysisPage- Income sources and growth trackingComparisonPage- Period-over-period financial comparisonTrendsForecastsPage- Trends and forecastingIncomeExpenseFlowPage- Sankey diagram cash flow visualizationInvestmentAnalyticsPage- 4-category investment portfolioMutualFundProjectionPage- SIP/MF projectionsReturnsAnalysisPage- Investment returns trackingTaxPlanningPage- Tax planning with salary-based multi-year projectionsFIRECalculatorPage- FIRE number, Coast FIRE, retirement corpus plannerNetWorthPage- Net worth trackingBudgetPage- Budget tracking and monitoringGoalsPage- Financial goal setting with savings allocationInsightsPage- Advanced analytics (velocity, stability, milestones)AnomalyReviewPage- Flag and review unusual transactionsYearInReviewPage- Annual financial summarySubscriptionTrackerPage- Recurring expense detection and manual trackingBillCalendarPage- Monthly calendar of upcoming billsSettingsPage- Single-page settings with collapsible sections (incl. SalaryStructureSection)
- Responsibility: Reusable UI components organized by domain
- Modules:
analytics/- Analytics visualization componentsFinancialHealthScore- Comprehensive health score (8 metrics across 4 pillars)YearOverYearComparison- YoY financial comparisonPeriodComparison- Month-to-month comparison with selectorsCashFlowForecast- Future cash flow predictionsRecurringTransactions- Recurring payment detectionCategoryBreakdown- Shared category treemap/table component (parameterized for income or expense)ExpenseTreemap- Thin wrapper aroundCategoryBreakdownfor expense visualizationTopMerchants- Top merchants/vendors analysisEnhancedSubcategoryAnalysis- Advanced subcategory analysisMultiCategoryTimeAnalysis- Time-based category analysisStandardBarChart- Reusable bar chart wrapper with consistent theming and defaultsStandardAreaChart- Reusable area chart wrapper with gradient fills and consistent stylingStandardPieChart- Reusable pie/donut chart wrapper with legend defaults
layout/- Layout components (AppLayout, Sidebar)shared/- Shared components (EmptyState, AnalyticsTimeFilter, MetricCard)transactions/- Transaction table componentsui/- Base UI components (shadcn-style), includingchartDefaults.tsxwhich exports shared Recharts configuration tokens (GRID_DEFAULTS,xAxisDefaults,yAxisDefaults,LEGEND_DEFAULTS,BAR_RADIUS,shouldAnimate,areaGradient, etc.) used by all chart components for consistent stylingupload/- File upload components (DropZone)
- Responsibility: Custom React hooks for logic reuse
- Examples:
useAnalyticsTimeFilter- Shared time-filter state (view mode, date range, FY) used by all analytics pagesuseAccountTypes- Account type managementuseAnalytics- Analytics data fetchinguseChartDimensions- Responsive chart sizing based on viewportusePeriodNavigation- Time period navigationapi/- TanStack Query hooks for API calls
- Responsibility: API communication
- Components:
api/- Backend API client with typed endpoints
- Responsibility: Global state management with Zustand
- Stores:
authStore- JWT tokens with persist middlewarepreferencesStore- User display/financial preferences (hydrated from API)accountStore- Account settings and preferencesinvestmentAccountStore- Investment account classificationsbudgetStore- Budget settings
- Responsibility: Utility functions and helpers
- Modules:
cn.ts- Class name utility (clsx + tailwind-merge)queryClient.ts- TanStack Query client configurationfileParser.ts- Client-side Excel/CSV parsing (lazy-loads SheetJS, SHA-256 hashing, column mapping, row validation)projectionCalculator.ts- Pure functions for multi-year salary/RSU/tax projectionstaxCalculator.ts- India tax slab computation (old and new regime)fireCalculator.ts- FIRE number, Coast FIRE, retirement corpus calculationsformatters.ts- Currency and number formatting with multi-currency conversion
App
├── Layout
│ ├── Sidebar (Navigation)
│ └── Main Content Area
│ └── Page Component
│ ├── Page-specific state
│ ├── Data fetching (TanStack Query)
│ └── Analytics Components
│ ├── Chart Components (Recharts)
│ │ ├── LineChart
│ │ ├── BarChart
│ │ ├── PieChart
│ │ ├── AreaChart
│ │ └── Treemap
│ ├── Score Components
│ │ └── FinancialHealthScore
│ ├── Comparison Components
│ │ ├── YearOverYearComparison
│ │ └── PeriodComparison
│ └── Analysis Components
│ ├── CashFlowForecast
│ ├── RecurringTransactions
│ └── SubcategoryAnalysis
└── Upload Components
└── FileUpload
id (hash_id) - Unique identifier (SHA-256)
date - Transaction date
amount - Transaction amount
type - Income/Expense/Transfer
category - Spending category
subcategory - Sub-category (optional)
account - Account name
description - Transaction description
is_deleted - Soft delete flag
file_source - Source file
created_at - Insert timestamp
updated_at - Last update timestamp
name - Account name (Savings, Checking, etc.)
total_balance - Current balance
transactions - Related transactions
- Total Income
- Total Expenses
- Net Savings
- Savings Rate
- Average Monthly Spending
- Category Totals
- Spending Patterns
- Recurring Payments
- Unusual Transactions
- Cash Flow Forecast
- FastAPI: Modern, fast, with automatic API documentation
- SQLAlchemy 2.0: Type-safe ORM with async support
- SQLite: Lightweight, file-based, no server needed
- Alembic: Version control for database schema
- React 19: Component-based UI with latest features
- TypeScript: Type safety and better DX
- Vite: Fast build tool and dev server
- Tailwind CSS: Utility-first styling
- Recharts: Data visualization library (replaces Chart.js)
- Zustand: Lightweight state management
- TanStack Query: Server state management and caching
Request: JSON
Response: JSON
Status Codes:
200 - Success
400 - Bad Request
404 - Not Found
409 - Conflict (duplicate file)
500 - Server Error
POST /api/upload - Upload transactions (JSON body: file_name, file_hash, rows, force)
GET /api/transactions - Get all transactions
GET /api/analytics/overview - Get financial overview
GET /api/analytics/kpis - Get KPIs
GET /api/analytics/behavior - Get spending behavior
GET /api/analytics/trends - Get financial trends
GET /api/analytics/wrapped - Get yearly financial wrap
GET /api/analytics/charts/* - Chart data endpoints
GET /api/analytics/insights/generated - AI-generated insights
GET /api/calculations/totals - Get income/expense totals
GET /api/calculations/monthly-aggregation - Monthly data
GET /api/calculations/yearly-aggregation - Yearly data
GET /api/calculations/category-breakdown - Category analysis
GET /api/calculations/account-balances - Account balances
GET /api/calculations/categories/master - Master category list
GET /api/account-classifications/* - Account classification endpoints
GET /api/meta/* - Metadata endpoints
-
Input Validation
- Client-side file parsing via SheetJS (files never leave the browser as raw uploads)
- Backend validates structured JSON via Pydantic schemas (
TransactionRow,TransactionUploadRequest) - Sanitize data during normalization
- Type checking with TypeScript and Python
-
Data Protection
- Use soft deletes instead of hard deletes
- Maintain audit trail with timestamps
- CORS configuration for cross-origin requests
-
CORS
- Frontend and backend on different ports
- CORS middleware configured in FastAPI
- SQLite for development (file-based), PostgreSQL-ready for production
- PostgreSQL connection pooling pre-configured (pool_size=20, max_overflow=10)
- JWT-based multi-user authentication and authorization
- All data is user-scoped
- Implement caching (Redis) for analytics
- Async database operations
- Virtual scrolling for large transaction tables
- Composite indexes on high-traffic query patterns (
user_id+category,user_id+date+type) - PostgreSQL connection pooling (pool_size=20, max_overflow=10, pool_pre_ping=True)
- SQLite WAL mode with 64MB cache and NORMAL sync for fast writes
- O(n) pre-grouped category analysis (replaced O(c*n) nested loops)
- Batch processing for large imports
- Shared SQL aggregation helpers to reduce query duplication
- Code splitting with Vite and lazy-loaded pages (
React.lazy) useDeferredValuefor non-blocking search in CommandPalette- Memoized chart data objects in DashboardPage to prevent unnecessary re-renders
- Responsive chart dimensions via
useChartDimensionshook - TanStack Query caching with
staleTime: Infinityto avoid refetches
- Unit tests for business logic
- Integration tests for API endpoints
- Test database fixtures
- Coverage reporting
- Component testing
- Integration testing
- Type checking with TypeScript
- End-to-end testing (future)
- Vercel serverless function via Mangum adapter (
backend/api/index.py) - Wraps FastAPI ASGI app for AWS Lambda-compatible execution
- Dependencies installed via
uv(auto-detected fromuv.lock) - Neon PostgreSQL connected via Vercel's Neon integration
- Static React SPA built with Vite, deployed to GitHub Pages
VITE_API_BASE_URLGitHub Actions variable points to Vercel backend- SPA routing via
404.htmlcopy workaround
- Try-catch blocks for database operations
- Meaningful error messages
- Logging of errors and warnings
- Error boundaries for React errors
- Toast notifications for user feedback
- Graceful fallbacks for failed API calls
- Structured logging with timestamps
- Log levels: DEBUG, INFO, WARNING, ERROR
- Centralized logging configuration
- Browser console for development
- Error tracking in production (future)
- Performance monitoring (future)