Luca Ledger is a client-side personal finance manager built with React, Vite, and Material UI. It keeps checking, savings, and credit card accounts in sync, encrypts all sensitive data in the browser, and deploys as a static site on GitHub Pages—no backend required.
- Multi-account coverage: Track deposits, withdrawals, and statement cycles across checking, savings, and credit cards.
- Client-side encryption: AES-256-GCM with PBKDF2-derived keys keeps data local and secure in IndexedDB via Dexie.
- Session-aware auth: Password-derived keys gate every session; there is no password recovery, by design.
- Rich UI + insights: Material-UI, D3/Recharts visualizations, and Redux selectors provide responsive charts and ledgers.
- Offline-first: All logic runs locally, so the app works even without a network connection after first load.
- React 18.3 with hooks and context
- Vite 4.x for dev/prod builds
- Material UI 5.x + Emotion for theming
- Redux Toolkit for state + encrypted middleware
- Dexie (IndexedDB) for encrypted persistence
- Day.js/Date-fns and D3/Recharts for date math and charts
- Vitest + Testing Library + jsdom for tests
- Password → PBKDF2 (100k iterations, SHA-256) → Key Wrapping Key (KWK)
- KWK unwraps a Data Encryption Key (DEK)
- Each record is encrypted with AES-256-GCM + unique IV before storage in IndexedDB
- DEKs are kept in memory only for the active app session; exports remain unencrypted and must be protected manually
See ENCRYPTION.md and src/crypto/ for implementation details.
- Auth states:
loading,no-users,login,authenticated,legacy-migration - Users authenticate via username + password; legacy plaintext data migrates automatically
- Users must re-authenticate after refresh/new tab because keys are not persisted in web storage
- React Router (
/dashboard,/accounts,/ledger,/categories,/settings,/help,/) drives navigation src/components/MainLayoutrenders global navigation + version banner- Redux slices live under
src/store/*(accounts, transactions, categories, statements, encryption)
src/
├── components/ # UI modules (auth, ledger, analytics, layout, modals)
├── views/ # Route-level pages (Dashboard, Accounts, Ledger, etc.)
├── store/ # Redux Toolkit slices, encrypted middleware, selectors
├── auth/ # Auth context + provider
├── crypto/ # AES/PBKDF2 helpers and key manager
├── validation/ # Zod/AJV schemas + validation helpers
├── hooks/ # Custom React hooks (balances, statements, etc.)
└── __tests__/ # Vitest suites, fixtures, utilities
- Node.js 20+
- pnpm 9.x
git clone https://github.com/LucaFinancial/LucaLedger.git
cd LucaLedger
pnpm install # ~40 seconds; do not interrupt| Command | Purpose | Notes |
|---|---|---|
pnpm dev |
Start Vite dev server (http://localhost:5173) | Hot reload + fast refresh |
pnpm build |
Production build to dist/ |
~15 s; copies CNAME + 404 on GH Pages build |
pnpm preview |
Serve built assets locally | Useful for final QA |
pnpm lint |
ESLint with React/A11y rules | Required before pushing |
pnpm test |
Vitest suite (headless) | Use pnpm test:watch while developing |
Run these steps after significant UI changes:
- Navigate between Dashboard (
/dashboard) and Accounts (/accounts) - Create an account via "Create New Account" button and verify form validation
- Confirm Material-UI components render responsively on mobile + desktop widths
- Ensure the header shows the semantic app version (format
vX.Y.Z) and the update dialog dismisses
- Unit + integration tests live under
src/__tests__/ - Use
pnpm test:coveragefor v8 instrumentation reports - Fake IndexedDB + jsdom handle crypto + Dexie flows in tests
- ESLint + Prettier rules enforce single quotes, 2 spaces,
@/import aliases, and a11y checks
Luca Ledger follows Semantic Versioning (MAJOR.MINOR.PATCH) with RC suffixes during issue development.
- Determine bump type
- Major: breaking schema or removal of features
- Minor: new features, UX enhancements, noticeable perf gains
- Patch: bug fixes, refactors, docs, dependency bumps
- Apply RC suffix while iterating
- First commit on an issue:
npm version <major|minor|patch> --preid=rc --no-git-tag-version→ results inX.Y.Z-rc.1 - Increment RC number on subsequent commits (
-rc.2,-rc.3, ...)
- First commit on an issue:
- Final release
- Drop
-rc.*, runnpm version <type> - Ensure
package.jsonversion matches header display (src/components/VersionDisplay)
- Drop
- Production builds are deployed via GitHub Actions to GitHub Pages at https://lucaledger.app/
pnpm build:pageshandles the static-site artifacts (CNAME,404.html)pnpm deployusesgh-pagesto publish thedist/directory manually if needed
- Build hangs? Wait the full 15 seconds; do not cancel.
- Dev server fails? Ensure dependencies are installed and no other process uses port 5173.
- Lint errors? Run
pnpm lintfor actionable rule hints; most issues are autofixable. - Encryption data loss? Passwords cannot be recovered. Export data regularly and store encrypted backups externally.
- Fork and branch from
main - Follow the versioning flow above and include RC bumps in commits
- Add or update tests + docs for every change
- Link issues in PR descriptions using
Closes #<id>so GitHub auto-closes them
MIT © LucaFinancial