A modern, privacy-first web app to track accounts, debts, income, and spending, helping you plan and visualize your path to financial freedom.
All calculations happen locally in your browser — no accounts required. An optional self-hosted Node.js + PostgreSQL backend is available for multi-device sync (see DEPLOYMENT.md).
Security Status: ✅ Production-Ready | Risk Level: LOW | Audit Date: August 31, 2026 | Last Scan: Static security scan passed (0 HIGH, 0 MEDIUM)
# Development (Python)
python -m http.server 32900
# Docker (recommended for consistent environments)
docker compose up -d # Build and run at http://localhost:32900
docker compose down # Stop
# Open browser
http://localhost:32900
# Run tests
pytest tests/ -v # Run all tests
pytest tests/security/ -v # Security tests only
pytest tests/features/ -v # Feature tests only
pytest tests/ui/ -v # UI/UX tests only
pytest tests/integration/ -v # End-to-end tests only
pytest -m "not slow" -v # Skip slow testsSee tests/README.md for comprehensive test documentation.
See CHANGELOG.md for version history and release notes.
For production deployment, see DEPLOYMENT.md.
MyFinances prioritizes your financial data security:
- ✅ Zero Data Transmission — All data stays on your device
- ✅ XSS Prevention — All user input sanitized and HTML-encoded via
escapeHtml() - ✅ Strong CSP —
script-src 'self' https://cdn.jsdelivr.net,style-src 'self'— no inline scripts or styles (plusgoogletagmanager.com/google-analytics.com, allow-listed for the opt-in analytics feature below but never contacted unless you configure it) - ✅ Security Headers — X-Content-Type-Options, X-Frame-Options, frame-ancestors protection
- ✅ No External Dependencies — Vanilla JavaScript (no npm supply-chain risk)
- ✅ Secure File Imports — JSON validation + 2 MB size limit + full re-sanitization on import
- ✅ Input Validation — Numeric bounds, date validation, text sanitization at every entry point
- ✅ Client-Side Only (default) — No server, no authentication surface
- ✅ Optional Postgres Backend — argon2id password hashing, server-side sessions (opaque token), httpOnly/Secure/SameSite=Strict cookies, CSRF double-submit tokens on every mutating request, NIST-style new-password rules with an optional Have I Been Pwned breach check, and a Settings → Security change-password form
The CI pipeline runs five automated scans on every push and pull request to main:
- CodeQL — GitHub SAST engine; detects XSS, injection, and logic bugs in
src/JavaScript - Trivy — Docker image — Gates on fixable CRITICAL/HIGH CVEs in the published container image
- Trivy — Filesystem — Scans repo lockfiles and source for known-vulnerable packages and leaked secrets; gates on CRITICAL/HIGH
- Trivy — IaC — Scans
Dockerfileanddocker-compose.ymlfor CIS-benchmark misconfigurations (informational) - Dependency Review — Blocks PRs that introduce new dependencies with HIGH or CRITICAL severity CVEs
All findings feed into the GitHub Security tab as SARIF reports.
- ✅ All calculations run entirely in your browser
- ✅ No data is sent to any server
- ✅ No accounts, no tracking, no analytics by default — self-hosters can opt their own instance into Google Analytics via an env var (see DEPLOYMENT.md); nothing is ever sent to Google unless you configure it
- ✅ Data stored only in your browser's
localStorage(same-origin isolated)
- SECURITY.md — Detailed security practices, CSP policy, vulnerability reporting
- docs/audit/security/ — Full security audit reports
- DEPLOYMENT.md — Production deployment with security headers
- Grouped main navigation — Three labeled groups: Overview (Health, Accounts, Income), Manage (Liabilities, Recurring, Savings, Plan), Analyze (Reports, Ledger, Reconcile, Retirement); active-group highlighting,
aria-current, hamburger menu on ≤768px - Command palette — Ctrl/Cmd+K or the 🔍 toolbar button opens a fuzzy-search jump list across all 11 pages and common actions; Arrow-key navigation, Enter to activate, Escape restores focus; full
role="dialog"/role="listbox"ARIA semantics - Theme selector (Light / Dark / High Contrast) — 3-way selector in the Settings modal (gear icon); preference persisted to localStorage. High Contrast targets WCAG AA/AAA with pure black surfaces, bright accent colors, and a bold focus outline on every interactive element; command palette includes a "Cycle theme" action
- Print / Save as PDF — 🖨️ Print button on every page;
@media printhides forms/controls so only read-only data content prints; browser print-to-PDF doubles as a PDF export - Keyboard navigation — all interactive elements reachable by keyboard; modals trap focus and Escape-to-close; calendar day-cells keyboard-activatable
- Accessible control names — every button/tab has an accessible name; per-item Edit/Delete/Save/Cancel buttons name the item (e.g.
Delete Visa card), toggle chips exposearia-pressed, and sortable column headers exposearia-sortplus a direction-aware label (#331); enforced bytests/ui/test_aria_labels.py - Reduced motion —
prefers-reduced-motion: reducecollapses all CSS transitions/animations;Chart.defaults.animationdisabled app-wide when the OS preference is set - Screen-reader chart tables — every Chart.js canvas has a paired visually-hidden
.sr-only<table>with the same data, rebuilt on every chart re-render viarenderChartDataTable()inutils.js - localStorage quota monitoring — soft-warning banner when usage crosses ~80% of the estimated 5 MB limit; hard-failure banner if
setItemactually throws - In-app guide — the ❓ Help button opens
guide.htmlin a new tab; theme (including High Contrast) follows the main app preference - $0 ledger amount policy — Settings → Advanced lets you Allow, Warn about, or Block $0 amounts in ledger overrides and recurring templates (transfers always need more than $0)
- Language support (English / Español / Polski) — switch languages from the Settings modal (gear icon). Every page, pop-up and chart is translated, including Ledger-generated transaction names, and number/date formatting follows the selected language. Changing the language re-renders the page you're on.
The app's default landing page — six at-a-glance metric cards:
| Card | What it shows |
|---|---|
| Debt-to-Income Ratio | Monthly debt payments ÷ income; Healthy / Moderate / High Risk badge |
| Savings Rate | Emergency + sinking fund contributions as % of income; Strong / Moderate / Low badge |
| Emergency Fund Runway | Months of expenses covered per fund; Critical → Excellent badge |
| Debt Payoff Timeline | Years to debt-free at minimum payments (avalanche); % progress bar |
| Monthly Cash Flow | Income minus all outflows; Surplus / Break Even / Deficit badge with breakdown |
| Credit Utilization | Card balances ÷ credit limits, per person and per card; an expandable "What affects your credit?" explainer covers the five common credit factors, with a status line where the app has data and a link to AnnualCreditReport.com — education only, never a score |
| Budget Allocation | Bill + expense categories as % of income with colour-coded bars; debt payments row included |
Internal links on each card navigate directly to the relevant page (Savings, Liabilities, Plan).
- Account types — Checking, Savings, Cash, Investment, Credit Card, Loan, or Other; name, starting balance, and optional interest rate (% APY)
- Interest income — optional per-account interest rate (% APY); auto-generates a monthly Interest deposit in the Ledger (last day of month, APY ÷ 12 on projected end-of-month balance, compounding), overridable with the true posted amount
- Account linking — income sources, one-time entries (bonuses/deposits), debts, recurring templates, and expense budgets can all be linked to an account
- Account notes — optional free-text notes (up to 500 characters, multi-line) on each account, e.g. an institution phone number or why a starting balance is unusual. Cards show the first line; expand to read the whole note.
- Projected monthly balance — each account card shows a projected end-of-month balance: starting balance ± all linked income, debt payments, recurring transactions, and expenses for the current month, including any ledger amount overrides
- Net worth widget — current net worth and change from the prior snapshot shown on the Accounts page
- Export / Import — accounts are included in the JSON backup (format version
"4.0.0")
- Unlimited debts — credit cards (revolving balance + APR) or fixed-amount recurring payments (subscriptions, rent, instalments)
- Single-page add & manage — add form and debt list side by side; no page switching
- Inline editing — Edit button populates the form for editing; list updates immediately on save
- Update Balance — record a new balance for any credit card while preserving the original for progress tracking
- Category labels — tag debts and filter the list by category
- Priority levels — assign 1–100 priority for custom strategy ordering
- Payoff Date display — every debt card shows a 📅 Payoff Date sourced from the plan calculation; shows "Run a plan to see" hint until a plan exists
- Interest Paid to Date — estimate shown on each debt card, based on original balance and opened date
- Payoff Analysis badge — each credit-card debt card shows a break-even comparison: your plan payment vs. minimum-only, with months saved and interest saved; no-plan state shows minimum-only estimate with a clear banner; toggle between Fixed and % of Balance minimum-payment mode
- Accelerate modal — "Accelerate this debt →" button opens a live-preview modal: type an extra monthly payment and instantly see updated payoff date and interest saved; "Apply to Plan" wires the total into the Strategy page
- Income sources — name, amount per paycheck, first pay date, and frequency (bi-weekly or monthly); automatic pay-schedule projection for the current month
- One-time entries — bonus, tax refund, cash deposit, check deposit, or other; linkable to an account
- Monthly summary — expected income this month, regular pay, one-time entries, estimated annual total
- Debt-to-income ratio — Plan page shows monthly debt payment as a % of income with a warning above 40%
- Expense budgets — variable monthly spending targets with date tracking (for calendar and ledger); inline editing in the list
- Bills — fixed monthly costs with due day and category (data model; no standalone add UI — use Recurring Templates for new fixed costs)
- Cash Flow Summary — live panel: Income − Debt minimums − Bills − Budgeted expenses = Net remaining
- Persisted to storage — expenses and bills are saved to localStorage and included in JSON export/import
- Three types — Subscription (outflow), Reimbursement (inflow), Transfer (debit one account, credit another; both Ledger legs are marked ⇄, like one-off transfers)
- Flexible frequency — Weekly, bi-weekly, monthly, quarterly, yearly
- Start and end dates — constrain to a specific date range
- Pause & skip controls — temporarily pause a template or skip individual months without deleting
- Mark as Paid — confirm each of the month's payments and its actual amount; this marks the matching Ledger transaction Cleared and a different amount becomes its Ledger amount. Checking Cleared in the Ledger marks it paid here too (issue #434). Distinct from Skip; resets each calendar month
- Integrated — recurring templates generate projected transactions in the ledger and appear in all Reports views
- Emergency Fund — target amount, current savings, monthly contribution, auto-contribution toggle; progress bar + % badge
- Sinking Funds — named savings goals with three allocation methods: Fixed Monthly Amount, Annual Cost ÷ 12, or Target Date Planning (app calculates required monthly amount)
- Auto-contribute — auto-contributions appear as dedicated outflows in the Ledger, Reports, and Account projections
- Integrated with Reports — contributions appear in Income vs. Expenses, Money Flow chart, Variance Dashboard, and account projections
- Reconcile page — per-account statement balance entry with a live colour-coded difference vs. tracked balance; explicit statement start/end dates (defaulting to the day after the last reconciliation → today, validated so the end can't precede the start) drive an "Expected transactions" listing that helps identify discrepancies; history rows show the statement period
- Two modes — Adjust Balance (reconciliation updates
startingBalancegoing forward) or Visible Only (recorded and shown on Ledger but does not mutate the tracked balance); changeable any time via the Settings modal (⚙ gear icon or command palette) - Ledger integration — every reconciliation appears as a marker row on the unified Ledger with previous → statement balance and difference; an inline ℹ icon shows whether the current mode is active (blue) or informational (muted)
- History log — each reconciliation records previous balance, statement balance, difference, and an optional note; per-entry delete does not revert the balance
- Quick-reconcile modal — "🔄 Reconcile this account" button on the Ledger page (when a single account is selected)
- Export/Import —
app.reconciliationsandapp.settingsround-trip through JSON backup
- Unified transaction list — income, one-time entries, debts, bills, expenses, recurring templates, savings contributions, and reconciliation markers in one scrollable view
- Account + date range filters — filter by account and by range (All, Past & Today, Next 30/60/90 Days, Through Next Month)
- Amount overrides — modal-based Override / Edit override / Reset per transaction; overrides are keyed by type+id+account+date so they survive ledger re-sorts; changing an override immediately recalculates running balances, Reports, and account projections
- Cleared tracking — a Cleared checkbox per transaction records whether it has actually posted to the account, plus the timestamp it was marked cleared (shown as the checkbox's tooltip); keyed the same way as amount overrides, so it's independent of ledger re-sorts. Purely informational — it does not affect running balances — useful for spotting scheduled transactions (bills, recurring items) that haven't posted on the day they were expected to
- Running balance — continuous balance column; snaps to the statement balance at each reconciliation marker when "Adjust Balance" mode is active
- CSV export — column-picker modal (date, account, name, amount, category, running balance, type, cleared, cleared at); exports respect the current filter and sort; selected columns persist between sessions
- Month-by-month calendar — income paydays, recurring occurrences, expenses, debt payments, and bonuses pinned to their dates; compact dot-count cells open a full event modal on click/Enter
- Income vs. Expenses — chart with income, recurring costs, and debt minimums for the current month
- Money Flow — cumulative income/outflow/net tracked day-by-day through the month; per-account balance table
- Variance Dashboard (What Changed) — current vs. previous period deltas for income, expenses, recurring costs, debt minimums, and net available; a Range selector (Single Month/Quarter/Half Year/Year) aggregates across multiple months instead of just one, compared against the equivalent-length prior period
- Net Worth — historical trend chart (3/6/12 month), Net Worth vs. Liabilities, Asset Growth vs. Debt Reduction, snapshot history audit table, manual snapshot capture; milestone celebrations at +$5K increments
- Spending Analysis — doughnut pie of all outflows for the selected period; 6-month stacked bar trend; ranked category list with change % badges (vs. prior period or vs. same period last year); drill-down modal per category; the same Range selector as the Variance Dashboard aggregates multiple months (e.g. a quarter) into one view
- Cash Flow Forecast — project balances 1/2/3/6/12 months ahead for total cash position or a single account; notable-month driver flags; negative-balance and intra-month dip warnings
- Summary Report — Monthly/Yearly toggle with cash flow, per-account balance changes, and net worth for the selected period
- Calendar Feed — toolbar 📅 button exports bills, debts, recurring items, expenses, and income paydays (
Payday - Name) as all-day iCal events over a ±12-month window; download a.icsfile (any backend) or subscribe via a livewebcal://link (PostgreSQL backend). Archived debts follow Settings → Show archived debts and are titledDebt (Archived) - Namewhen shown
- Daily compounding interest —
monthlyInterest = balance × ((1 + APR/365)^daysInMonth − 1) - Four strategies — Avalanche (highest APR first), Snowball (lowest balance first), Priority-Low, Priority-High
- Per-month stimulus — add a one-time extra payment for any month; plan recalculates instantly
- Negative-amortisation warning — badge flags any debt where the minimum payment is below the monthly interest
- Strategy comparison panel — runs all four strategies and shows interest/time saved vs. your current choice
- What-If simulator — drag a slider to add extra monthly payment and instantly see months saved / interest saved
- Break-Even Analysis — per-debt module (
src/breakEven.js) runs two single-debt simulations (plan vs. minimum-only) to compute months saved, interest saved, and balance-over-time series for mini charts; supports both fixed-minimum and percent-of-balance modes - Target Payoff Date — enter a desired payoff date; binary-search back-calculator (up to 60 iterations) finds the required monthly payment
- Payoff Timeline — one line per debt declining to zero
- Cumulative Progress — Total Paid, Principal Paid, Interest Paid running totals
- Principal vs. Interest doughnut
- Debt Balance Distribution pie
- Monthly Debt-to-Income pie (requires income sources)
- Persistent storage — all data auto-saved to
localStorage; keydebtTrackerData, format"4.0.0". Optional: switch to a self-hosted PostgreSQL backend for multi-device sync - JSON export — one-click full backup including accounts, debts, income, bills, expenses, recurring templates, snapshots, overrides, reconciliations, and settings
- JSON import — Replace (full restore) or Merge (append debts; restore income, bills, expenses, overrides); legacy v1.0 files accepted; 2 MB max; full re-sanitization after import; a collapsible warning list reports any coerced fields or rejected records, and a malformed file shows the specific parse error inline
- CSV export — debt payment schedule + per-debt summary; Ledger CSV with custom column picker
- Clear All Data — wipe everything and start fresh
- Add to Home Screen / Install —
manifest.jsonplus a generated icon set (icons/) make the app installable as a standalone app on desktop and mobile - Offline app shell — a service worker (
sw.js) precaches the app shell on first visit, so the app loads with zero network connectivity afterward (your saved data already lives inlocalStorage, so nothing about it depends on the network either way) - Current releases — while online, stable app-shell URLs revalidate before use so Chrome receives newly deployed HTML, JavaScript, and styles instead of retaining an older cached release
- Update prompt — when a new version is deployed, a dismissible banner offers to reload rather than silently swapping assets mid-session
Click ❓ Help in the toolbar to open the full usage guide (guide.html) in a new tab.
Navigate to Liabilities. The page shows the add form and the debt list side by side.
| Field | Notes |
|---|---|
| Debt Name | Any label (e.g. "Visa", "Car Loan") |
| Debt Type | Credit Card (revolving) or Fixed Amount (recurring) |
| Balance / Amount | Current balance or fixed monthly cost |
| APR | Annual interest rate, e.g. 18.9 |
| Minimum Payment | The minimum you must pay each month |
| Due Date | Day of month the payment is due (used in Calendar view) |
| Priority | 1–100, used by Priority strategies |
| Opened Date | Optional — enables the Interest Paid to Date estimate |
| Category | Optional label for filtering |
Navigate to Accounts. Define the bank accounts, wallets, or credit cards your money flows through. Each account card shows a projected end-of-month balance factoring in all linked income, debts, and expenses.
Navigate to Plan.
- Enter your Total Monthly Payment.
- Choose a Payment Strategy.
- Optionally use Back-calculate from target payoff date.
- Click Calculate Payment Plan.
Results appear in three tabs:
| Tab | Contents |
|---|---|
| 📊 Overview | Strategy comparison panel; What-If extra-payment simulator |
| 📋 Debt Summary | Sortable per-debt table with payoff dates and progress bars; amortization modal |
| 📅 Schedule | Tabular (monthly schedule with editable stimulus), Calendar, Chart |
Reloading the app later automatically shows your last-calculated plan's results again — no need to re-enter your payment and re-click Calculate. Every submission is also logged to the collapsible 📜 Plan History panel (date, payment, strategy, total interest, payoff date), capped at the 20 most recent runs, so you can compare past plans at a glance.
Navigate to Income. Add one row per income source. Use the one-time entry section for bonuses, tax refunds, and irregular deposits.
Navigate to Liabilities (Budget sub-tab). Add bills (fixed monthly costs) and expense budgets (variable spending targets with category and date). The Cash Flow Summary panel shows net remaining after all outflows.
The Backup & Restore button (⇅ icon) is in the top-right toolbar on every page, opening a two-tab modal.
- Export (JSON) — complete backup of all data as a single
.jsonfile - Import (JSON) — choose Replace (full restore) or Merge (append debts)
- Export CSV — payment schedule or Ledger with custom columns
- Export Charts as PNG — downloads every currently-visible chart as a PNG image
- Print / Save as PDF — 🖨️ Print button on every page
| Strategy | Order | Best for |
|---|---|---|
| Avalanche | Highest APR first | Minimising total interest paid |
| Snowball | Lowest balance first | Psychological momentum |
| Priority – Low first | Lowest priority number first | Custom ordering (deprioritise debts manually) |
| Priority – High first | Highest priority number first | Tackling the most important debt first |
The Strategy Comparison panel on the Plan page always shows all four strategies simultaneously.
index.html — Main page shell + responsive nav + CSP meta tag
guide.html — In-app usage guide (opened by Help button)
guide.css — Styles for guide.html (externalized for CSP compliance)
styles.css — Responsive styles + dark mode + utilities + print stylesheet
styles-csp-classes.css — CSP-compliant utility classes + dynamic CSS variable rules
manifest.json — Web app manifest (installability metadata + icon references)
sw.js — Service worker: app-shell precache + Chart.js CDN runtime cache
src/
├─ app.js — Main app controller & state (DebtTrackerApp)
├─ ui.js — Event listeners, page navigation, mobile menu
├─ commandPalette.js — Ctrl+K command palette
├─ keyboardShortcuts.js — Keyboard Shortcuts help panel (KEYBOARD_SHORTCUTS single source of truth)
├─ settings.js — App settings (reconciliation mode, storage backend, etc.)
├─ setupWizard.js — First-run setup wizard + storage backend switcher
├─ loginGate.js — Full-page login gate overlay (PostgreSQL backend only)
├─ storageAdapters.js — Storage backend adapters (LocalStorage / Session / Postgres)
├─ serviceWorker.js — Service worker registration
├─ i18n.js — Internationalization (t(), locale switching, applyStaticTranslations)
├─ locales/ — i18n locale dictionaries (en.js, es.js, pl.js)
├─ strategy.js — Payment-plan orchestrator & income widget
├─ strategyPlanCalculation.js — Payment-plan calculation entry points
├─ strategyCalendar.js — Payment calendar view
├─ strategyComparison.js — Interest comparison & what-if simulator
├─ strategySummaryTable.js — Debt summary table & amortization modal
├─ strategyScheduleTable.js — Payment schedule table
├─ debts.js — Debt management (CRUD & inline editing)
├─ debtBreakEven.js — Break-even badge, mini chart & accelerate-debt modal
├─ accounts.js — Account management & balance projections
├─ bonusAdvisor.js — Bonus/one-time income advisor
├─ income.js — Income sources & one-time entries
├─ bills.js — Bills data model (no standalone add UI — use Recurring)
├─ recurring.js — Recurring transaction templates
├─ savings.js — Emergency fund & sinking fund tracking
├─ ledger.js — Transaction ledger page rendering & CSV export
├─ ledgerTransactions.js — Ledger transaction aggregation (pure computation)
├─ ledgerOverrides.js — Per-transaction manual amount overrides
├─ ledgerCleared.js — Per-transaction cleared/posted tracking with timestamp
├─ reconciliation.js — Account statement reconciliation
├─ retirement.js — Retirement page rendering (account cards, snapshot log, charts, projection, Coast FIRE date)
├─ health.js — Financial health dashboard (DTI, savings rate, runway)
├─ spending.js — Spending analysis (category breakdowns, trends)
├─ reports.js — Reports page orchestrator (month nav)
├─ reportsNetWorth.js — Net worth snapshots & widget
├─ reportsCalendar.js — Reports calendar grid & day-detail modal
├─ reportsCashFlow.js — Income/expense & money flow reports
├─ reportsMoneyFlowSankey.js — Money Flow Sankey chart
├─ reportsVariance.js — Budget variance report
├─ reportsSummary.js — Reports summary metrics
├─ forecast.js — Cash Flow Forecast (Reports tab)
├─ charts.js — Chart rendering & lifecycle
├─ storage.js — Persistence (save/load/quota/backend switching)
├─ sanitizers.js — Sanitizers for persisted/imported record shapes
├─ dataExport.js — JSON/CSV export & import
├─ dataTransferModal.js — Postgres-to-local data transfer modal
├─ postgresSync.js — Per-resource Postgres mutation helpers (pgPost/pgPatch/pgDelete)
├─ postgresImport.js — loadFromPostgres() fan-out to all 15 resource endpoints
├─ pgMigrationModal.js — One-time local→Postgres migration modal
├─ breakEven.js — Per-debt break-even calculation engine (plan vs. min-only)
├─ debtCalculator.js — Pure calculation engine (no side effects, no DOM access)
├─ retirementCalculator.js — Pure retirement projection engine (future-value projection, growth/contribution split, Coast FIRE)
├─ guideTheme.js — Applies saved dark-mode theme to guide.html
├─ guideNav.js — In-app guide navigation helpers
└─ utils.js — Formatting, date utilities, sanitization, chart tables
server/ — Optional self-hosted Node.js + PostgreSQL backend
├─ src/index.js — Express app (auth, CRUD routes, CSRF, rate-limiting)
├─ migrations/ — node-pg-migrate SQL migration files
├─ scripts/create-user.js — Bootstrap admin user (argon2id hashed)
├─ docker-entrypoint.sh — Reads Docker secret → sets DATABASE_URL before Node starts
├─ Dockerfile — node:24-alpine image (build context = repo root for sanitizer reuse)
└─ README.md — Server setup, dev, and deployment guide
tests/ (778 tests across 81 files)
├─ conftest.py — Shared fixtures & utilities
├─ README.md — Comprehensive test documentation
├─ security/ (65 tests) — XSS, CSP, input validation, static scan
│ ├─ test_xss.py, test_csp.py
│ └─ test_input_validation.py, test_static_scan.py
├─ features/ (389 tests) — Per-feature CRUD, calculations, business logic
│ ├─ test_accounts.py, test_debts.py, test_income.py, test_bills.py
│ ├─ test_expenses.py, test_recurring.py, test_recurring_occurrences.py
│ ├─ test_ledger.py, test_reports.py, test_savings.py, test_networth.py
│ ├─ test_health.py, test_forecast.py, test_reconciliation.py
│ ├─ test_spending_analysis.py, test_storage_import.py, test_storage_quota.py
│ ├─ test_debt_calculator.py, test_strategy.py, test_settings.py
│ ├─ test_main_nav_groups.py, test_reports_nav_groups.py, test_versioning.py
│ ├─ test_break_even.py, test_interest_income.py, test_i18n.py, test_i18n_coverage.py
│ ├─ test_storage_backend.py, test_pwa.py, test_pwa_icons.py, test_analytics.py
│ ├─ test_cash_flow_trend.py, test_money_flow_sankey.py, test_validation_modals.py
│ └─ test_issue_92_export.py, test_issue_93_expense_save.py
├─ ui/ (249 tests) — UI/UX, responsiveness, accessibility
│ ├─ test_mobile.py, test_modals.py, test_dark_mode.py, test_high_contrast_theme.py
│ ├─ test_css_load.py, test_accessibility.py, test_main_nav.py
│ ├─ test_charts.py, test_chart_accessibility.py, test_guide_theme.py
│ ├─ test_guide_nav.py, test_reduced_motion.py, test_command_palette.py
│ ├─ test_setup_wizard.py, test_overview_print.py, test_remaining_pages_print.py
│ ├─ test_table_mobile_scroll.py, test_data_transfer_modal.py, test_delete_confirm_modal.py
│ ├─ test_settings_theme_location.py, test_pwa_update_banner.py
│ ├─ test_debt_actions.py, test_recurring_actions.py, test_reports_actions.py
│ └─ test_reports_nav.py, test_reconciliation_actions.py, test_spending_ui.py, test_strategy_calendar.py, test_whatif_simulator.py
├─ a11y/ (10 tests) — Site-wide WCAG 2.1 AA accessibility audit
│ └─ test_a11y_audit.py, run_a11y_audit.py
├─ integration/ (18 tests) — End-to-end workflows, import/export, data persistence
│ ├─ test_smoke.py, test_workflows.py, test_interest_income_workflow.py
│ └─ test_pwa_offline.py
└─ postgres/ (47 tests — requires Docker stack)
├─ test_postgres_bootstrap.py — Auth, login gate, session handling
├─ test_postgres_import.py — loadFromPostgres fan-out + import round-trip
├─ test_postgres_mutations.py — Per-resource CRUD via pgPost/pgPatch/pgDelete
├─ test_postgres_migration.py — local→Postgres one-time migration modal flow
├─ test_postgres_notifications.py — Mailpit-backed end-to-end coverage for the test-email flow
└─ test_postgres_setup_wizard.py — First-run setup wizard against the Postgres backend
tools/
├─ debug/ — Ad-hoc manual debugging scripts (not part of pytest suite)
└─ generate-icons.js — One-time PWA icon generator (Node zlib only, not run in CI)
icons/ — Generated PWA icon set (tools/generate-icons.js output)
Known gap: The standalone Bills UI (
#billForm/#billList) was removed in favor of Recurring Templates, butbills.jsstill defines the full bills data model and calculation logic —app.billsis read byaccounts.js,health.js,ledger.js, andstrategy.js, and round-trips through import/export. There is currently no reachable UI to add or edit a bill.
- README.md — You are here
- CHANGELOG.md — Version history and release notes
- ROADMAP.md — Planned features, known gaps, technical debt tracking
- SECURITY.md — Security practices, CSP policy, vulnerability reporting
- DEPLOYMENT.md — Production deployment guides (Nginx, Apache, Docker)
- tests/README.md — Comprehensive test documentation
- docs/audit/security/ — Security audit reports
- docs/audit/a11y/ — Accessibility audit reports
- docs/audit/test/ — Test suite audit and coverage reports
- guide.html — In-app user guide
- Frontend: Vanilla ES6+ JavaScript — no frameworks, no build step
- Storage: Browser localStorage (same-origin isolated); optional self-hosted PostgreSQL backend
- Charts: Chart.js 4.4.3 via CDN
- Testing: pytest + Playwright (browser automation)
monthlyInterest = balance × ((1 + APR / 100 / 365)^daysInMonth − 1)
totalAccrued = originalBalance × ((1 + APR/100/365)^daysSinceOpened − 1)
interestPaid = max(0, totalAccrued − principalPaidDown)
lo = sum of all minimum payments
hi = 2 × totalBalance + 10,000
for 60 iterations:
mid = (lo + hi) / 2
if calculatePaymentPlan(mid) pays off by target date → hi = mid
else → lo = mid
normalizeText()— strips dangerous characters from text fieldssanitizeFiniteNumber()— validates numbers with optional min/max boundssanitizeInteger()— integer validationsanitizeDateISO()— strict YYYY-MM-DD format validationescapeHtml()— HTML entity encoding for all output viainnerHTML
- Maximum file size: 2 MB
- JSON validation with try-catch — a malformed file surfaces the native parse error (position/line/column when the browser provides it) inline in the Import tab's error banner, never a popup alert
- Complete re-sanitization on every import (all
sanitize*functions run again) - Per-field coercion/rejection warnings — a collapsible "Import succeeded — N issues found" list names every field that was coerced to a default and every record dropped for missing/invalid required fields, across all importable collections
- Duplicate detection and filtering
- Type checking on all fields
default-src 'self';
script-src 'self' https://cdn.jsdelivr.net;
style-src 'self';
img-src 'self' data:;
font-src 'self';
connect-src 'self' https://cdn.jsdelivr.net;
object-src 'none';
base-uri 'self';
form-action 'self'
frame-ancestors 'none'is enforced viaX-Frame-Options: DENYHTTP header (browsers ignoreframe-ancestorsin<meta>CSP tags per spec). Server deployments in DEPLOYMENT.md add it via the HTTP CSP header.
- Total Tests: 731 comprehensive tests (plus 47 Postgres/CI-only), all passing
- Test Files: 74 organized across 6 categories (81 including
postgres/) - Framework: pytest with Playwright browser automation
- Coverage: All major features + security + UI + accessibility + integration paths
- XSS Prevention — Input sanitization across accounts, income, debts, recurring, savings, reconciliation, spending, health, ledger
- CSP Compliance — Strict Content Security Policy enforcement; meta tag / nginx header sync check
- Input Validation — Bounds checking, unicode, special characters, negative-amount guards on all forms
- Static Analysis — Code patterns, hardcoded secrets, dependencies
- Accounts — CRUD, projections, graceful orphaning of linked items on deletion; interest-rate (% APY) badge display — threshold/formatting boundaries, multi-account scoping, edit-to-clear, reload persistence, import clamping
- Debts — Liability management, interest, amortization, fixed-amount validation
- Interest Income — monthly compounding deposit engine, last-day posting, override-aware compounding, negative/zero/sub-cent skips, Reports/Forecast integration
- Debt Calculator — Strategies (incl. multi-debt priority ordering), daily compounding, back-calculator, stimulus edge cases
- Strategy / Plan — Avalanche/Snowball/Priority switching, comparison panel, stimulus validation, plan history logging/cap, last-plan restore on load
- Income — Sources, frequencies, totals, negative-amount rejection (add + inline edit paths)
- Expenses — Add/edit/delete, amount/date validation
- Bills — Data model, sanitization, calculation integration
- Recurring — All frequencies, pause/resume, skip-month, mark-as-paid, account linkage, validation
- Ledger — Filtering, amount-override modal, cleared-checkbox tracking, key collision checks, CSV export column picker
- Reports — Income vs expenses, money flow, net worth analytics, tab grouping, date-boundary handling
- Savings — Emergency funds, sinking funds, persistence
- Net Worth — Snapshots, milestones, historical calculations
- Financial Health — All six cards, empty states, nav links
- Cash Flow Forecast — Horizon/account selection, notable months, negative-balance warnings, intra-month dip detection
- Account Reconciliation — Statement balance adjustments, expected-transaction matching, sanitization, import/export
- Spending Analysis — Category breakdowns, month-over-month trends
- Settings — Reconciliation mode persistence and import/export round-trip
- Storage Import — Sanitizer primitives, adversarial inputs for all record types, legacy v1.0 format
- Storage Quota — Soft warning at ~80%, dismissibility, re-arming, hard-failure on write error
- Main Nav Groups — Grouped navigation structure (Overview/Manage/Analyze)
- Break-Even Analysis — badge no-plan and plan-active states, min-type toggle, accelerate modal (open/preview/apply), plan table columns, fixed-amount exclusion, edge cases (0% APR, balance=minimum, invalid percent, $0/$negative extra)
- Analytics — optional self-hosted Google Analytics stays off with no
window.__ENV__.GA_MEASUREMENT_ID; injectsgtag.js/dataLayerwhen it's present;index.html/guide.htmlwiring
- Mobile Responsiveness — Hamburger menu, viewport handling, touch sizing, table horizontal scroll
- Modals — Visibility toggling, close buttons, amortization, calendar day-detail, ledger export
- Dark Mode — Theme switching, contrast, persistence, corrupted-localStorage fallback
- High Contrast Theme — 3-way selector wiring,
dark-mode+high-contrast-modeclass pairing, persistence, corrupted-value fallback, focus-ring,.nav-group-labelcontrast regression guard - CSS Loading — External stylesheet, utility classes, responsive breakpoints
- Accessibility — Keyboard navigation, ARIA labels, semantic HTML, Results tab-bar semantics
- Guide Theme —
guide.htmldark/light mode sync, nav back-link - Charts — Chart.js instance destroy-before-recreate on repeated re-render
- Chart Accessibility —
.sr-onlydata-table fallback present for health, spending, forecast, net-worth, strategy, and budget cash-flow charts - Reduced Motion — CSS transition-duration collapse and
Chart.defaults.animationdisabled underprefers-reduced-motion - Command Palette — Ctrl+K / toolbar open, filtering, empty-state, arrow-key navigation, Enter-to-navigate, Escape/backdrop close
- Keyboard Shortcuts Panel — palette command + account-menu entry points, rows match
KEYBOARD_SHORTCUTS, focus trap, Escape/backdrop close with focus restore, CSP-safe rendering - Setup Wizard — First-run modal, setting persistence, skip flow
- Print Buttons — Present on Overview pages (Health/Accounts/Income) and all remaining pages;
@media printstylesheet hides forms - Table Mobile Scroll — Ledger, reconciliation expected, and report tables scroll within
.table-wrapperon narrow viewports - Main Nav — Grouped active-state highlighting and keyboard reachability
- Reports Nav / Actions — Tab-bar grouping, sticky positioning, dark mode, tab-switching visibility
- Debt / Recurring / Reconciliation Actions — Inline edit, mark-paid, reconcile-modal flows
- Spending UI — Pie/bar charts, ranked list, drill-down modal
Site-wide sweep across all 10 pages × 2 themes + guide.html: dangling ARIA refs, duplicate IDs, orphaned form inputs, unnamed interactive elements, missing alt text, computed WCAG 1.4.3 colour contrast, modal Escape-to-close, mobile nav aria-expanded.
- End-to-End Workflows — Complete user journeys (account → debt → net worth → reconciliation)
- Data Persistence — Cross-navigation data integrity
- Import/Export — JSON roundtrip, CSV schedule export (incl. comma/quote escaping), full clear-all-data → reimport → render-every-page consistency check
- Ledger CSV Export — Column-picker modal (incl. cleared/cleared-at columns), filtered export, column persistence
pytest tests/ -v # All 778 tests (requires Docker for postgres/)
pytest tests/security/ -v # 65 security tests
pytest tests/features/ -v # 389 feature tests
pytest tests/ui/ -v # 249 UI/UX tests
pytest tests/a11y/ -v # 10 accessibility audit tests
pytest tests/integration/ -v # 18 integration tests
pytest -m "security" -v # All security tests by marker
pytest -m "not slow" -v # Skip slow testsA separate, dev-only Node toolchain mutation-tests the pure calculation/formatting/sanitization functions in src/debtCalculator.js, src/utils.js, and src/sanitizers.js — not the Python suite above. It runs as the mutation-testing job in CI (.github/workflows/ci.yml).
npm install
npm run test:unit # Jest unit tests
npm run test:mutation # Stryker mutation run (HTML report at reports/mutation/mutation.html)| Severity | Count | Status |
|---|---|---|
| HIGH | 0 | ✅ |
| MEDIUM | 0 | ✅ |
| LOW | 12 | ✅ All properly handled |
| Browser | Version | Support |
|---|---|---|
| Chrome | 60+ | ✅ Full |
| Firefox | 55+ | ✅ Full |
| Safari | 12+ | ✅ Full |
| Edge | 79+ | ✅ Full |
| IE 11 | — | ❌ Not supported |
Required: ES6+ JavaScript, localStorage enabled, CSS Grid & Flexbox, CSS custom properties.
# Development
python -m http.server 32900See DEPLOYMENT.md for Nginx, Apache, and Docker configurations including security headers, HTTPS, and caching.
My data disappeared.
Browser storage may have been cleared. Use Backup & Restore → Export regularly as a portable JSON backup you can re-import any time.
Import says "duplicate skipped" but I wanted to update.
Delete or rename the existing item first, then re-import. Or use inline editing to update manually.
The payoff date is very far away.
Increase your monthly payment, switch to Avalanche strategy, or use the What-If slider to see required payment increase.
Payment is less than minimum required.
The app will show an error. Your total monthly budget must cover every debt's minimum payment.
A debt shows a
Your minimum payment is too low to cover monthly interest. Increase the payment or reduce the APR (e.g. balance transfer).
Mobile menu isn't working.
Check that JavaScript is enabled and no CSP violations appear in the browser console.
Data isn't persisting between refreshes.
Verify localStorage is enabled. Safari may require explicit permission in Storage settings.
The storage quota warning appeared.
Export a JSON backup immediately. Consider clearing old net-worth snapshots or unused data to free space.
If you discover a security vulnerability:
- Do not create a public GitHub issue
- Contact the repository owner privately
- Include steps to reproduce + impact assessment
- See SECURITY.md for details
Open an issue with steps to reproduce, browser version, and OS. Run the test suite and include output.
- All changes should pass the security and feature test suites
- Add tests for new features (security, feature, and UI coverage)
- Follow the existing modular pattern: feature logic in a module function
featureFn(app, ...), thin wrapper onDebtTrackerApp - All user-supplied strings rendered via
innerHTMLmust useescapeHtml() - New persisted fields must have a
sanitize*entry instorage.js - See SECURITY.md for security guidelines and ROADMAP.md for planned work
Start your journey to being debt-free today! 💰
MyFinances v4.45.0 — Updated September 4, 2026