Finance tracking
Find a file
megaproxy 9897d03d91 Add public demo mode with auto-seeding, hourly reset, and Portainer deploy guide
- DEMO_MODE=true env flag: disables password changes and backup endpoints (403),
  exposes GET /demo/status for frontend detection
- Auto-seed on first startup: creates demo user (demo@mymidas.app / demo123)
  with 6 months of transactions, investments, budgets, subscriptions, and tax
  payslips; takes a pg_dump snapshot immediately after for hourly restore
- Hourly reset: resetter Alpine container with cron restores DB from snapshot
  and purges uploaded attachments every hour on the hour
- Frontend: amber demo banner on all pages, login page shows credentials,
  password change disabled with notice, backups section replaced with notice
- demo/ directory: self-contained docker-compose.yml (ports 4001/8091),
  .env.example, reset.sh, and step-by-step Portainer DEPLOY.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 22:08:24 +00:00
backend Add public demo mode with auto-seeding, hourly reset, and Portainer deploy guide 2026-04-23 22:08:24 +00:00
demo Add public demo mode with auto-seeding, hourly reset, and Portainer deploy guide 2026-04-23 22:08:24 +00:00
frontend Add public demo mode with auto-seeding, hourly reset, and Portainer deploy guide 2026-04-23 22:08:24 +00:00
postgres/init Initial commit: MyMidas personal finance tracker 2026-04-21 11:56:10 +00:00
redis Initial commit: MyMidas personal finance tracker 2026-04-21 11:56:10 +00:00
scripts Initial commit: MyMidas personal finance tracker 2026-04-21 11:56:10 +00:00
.env.example Initial commit: MyMidas personal finance tracker 2026-04-21 11:56:10 +00:00
.gitignore Initial commit: MyMidas personal finance tracker 2026-04-21 11:56:10 +00:00
CLAUDE.md Investment portfolio charts, search fix, and holding creation fixes 2026-04-22 23:06:41 +00:00
docker-compose.yml Initial commit: MyMidas personal finance tracker 2026-04-21 11:56:10 +00:00
README.md Add recurring transaction detection, subscriptions page, and UK tax reporting 2026-04-23 21:40:02 +00:00
RECURRING_TRANSACTIONS_PLAN.md Add recurring transaction detection, subscriptions page, and UK tax reporting 2026-04-23 21:40:02 +00:00
TAX_FEATURE_PLAN.md Add recurring transaction detection, subscriptions page, and UK tax reporting 2026-04-23 21:40:02 +00:00

MyMidas

A self-hosted, single-user personal finance tracker built for security and completeness. Combines budgeting, investment tracking, and ML-powered predictions in one application — things that no single open-source tool does together.

Runs entirely on your own hardware via Docker Compose. Designed for LAN access with VPN for remote, behind an existing reverse proxy (nginx proxy manager, Caddy, etc.).


Features

Accounts & Transactions

  • Multiple account types: Checking, Savings, Cash ISA, Stocks & Shares ISA, Credit Card, Investment, Pension, Crypto Wallet, Loan, Mortgage, and more
  • Full transaction history with categories, tags, merchant tracking, and notes
  • Transfer detection between accounts
  • Recurring transaction detection — CSV imports are automatically scanned for recurring payments (direct debits, subscriptions, standing orders) using frequency analysis; manually override any transaction
  • Receipt and document attachments on transactions (JPEG, PNG, WebP, PDF — up to 10 MB each)
  • AI receipt scanning — photograph a receipt to auto-extract merchant, amount, date, and description into a new transaction; receipt is automatically attached
  • CSV import with auto-detection for 10 UK bank formats: Monzo, Starling, Revolut, Barclays, Lloyds, NatWest, HSBC, Santander, Nationwide, and generic fallback
  • SHA-256 deduplication prevents re-importing the same transactions

Budgets

  • Monthly, weekly, quarterly, and yearly budget periods
  • Per-category budget tracking with rollover support
  • Visual radial gauges showing spend vs. budget
  • Configurable alert threshold (default 80%)
  • Edit existing budgets (amount, period, alert threshold) without losing history

Investments

  • Portfolio tracking with holdings and buy/sell/dividend/split transactions
  • Asset search by name or ticker (stocks, ETFs, crypto, funds) via yfinance full-text search
  • Live price feeds: stocks and ETFs via yfinance, crypto via CoinGecko (refreshed every 15 minutes)
  • Hourly FX rates for multi-currency conversion to GBP base
  • TWRR and MWRR performance metrics
  • Capital gains reporting (short/long-term by tax year)
  • OHLCV candlestick charts per asset with buy/sell transaction history overlay
  • Portfolio visualisations: allocation donut by holding, allocation donut by asset type, cost basis vs current value bar chart, return % per holding bar chart
  • Investment account balances include holding market values in net worth, balance sheet, and accounts list — supports both simple (holdings only) and full cash-flow tracking workflows

Subscriptions

  • Automatic grouping of recurring transactions into a subscriptions dashboard
  • Monthly cost equivalent for each subscription (normalised across weekly/fortnightly/monthly/quarterly/yearly frequencies)
  • Next payment date badges with overdue/upcoming highlighting
  • Re-scan button to re-run detection after new imports
  • Mark any transaction as recurring (or not) from the transaction detail view

Categories

  • 45 built-in system categories across income, expense, and transfer types
  • Create custom categories with a name, type, and colour
  • Rename and recolour existing custom categories
  • Managed in Settings → Categories

UK Tax Reporting

  • Dedicated Tax page with per tax-year reports (tax_year=2026 = 2025/26)
  • Enter monthly payslips or a single P60 annual figure
  • Supports all HMRC tax codes: standard (1257L), BR, D0, D1, NT, K-codes, 0T, W1/M1
  • Calculates income tax liability with personal allowance taper above £100,000
  • NI Class 1 employee contributions
  • Capital gains tax: auto-detected from investment disposals + manual entry for property/other assets
  • Dividend tax: auto-detected from investment dividend transactions
  • Shows liability vs. withheld (PAYE) and net owed/overpaid
  • Configurable rate tables — edit tax bands in-app so Budget changes don't require a rebuild; pre-seeded for 2024/25 and 2025/26 (18%/24% CGT post Oct 2024 Budget)
  • All figures are estimates for informational purposes only

Reports

Seven report views:

  1. Net Worth over time (area chart with time slider)
  2. Income vs. Expense (bar chart)
  3. Cash Flow (waterfall)
  4. Category Breakdown (pie with drilldown)
  5. Budget vs. Actual
  6. Investment Performance
  7. Spending Trends

Export to CSV and PDF.

ML Predictions

Four prediction engines, all running locally:

Engine Algorithm Output
Spending Forecast Meta Prophet per category; SARIMA fallback 90-day forecast with 80%/95% confidence bands
Net Worth Projection Holt-Winters ExponentialSmoothing 1/3/5-year fan chart, 3 scenarios (conservative/base/optimistic)
Monte Carlo Portfolio Geometric Brownian Motion + Cholesky covariance 1,0005,000 simulations, P10P90 percentile paths
Budget Forecast Velocity-based daily projection + normal CDF Probability of overspend per category this month

Plus a 30-day cash flow forecast with negative-balance risk flagging.

Themes

Seven selectable UI themes — pick from the top bar:

Theme Style
Obsidian Dark slate, blue-grey accents
Arctic Clean light with ice-blue tones
Midnight Deep navy with cyan highlights
Vault Forest green, financial gravitas
Terminal Phosphor green on black, scanline overlay, monospace
Synthwave Neon pink/purple, grid lines, Orbitron headings
Ledger Warm paper, serif typography, ruled lines

Security

Ten independent security layers:

  1. Network isolation — Postgres and Redis are on an internal Docker bridge, never exposed to the host
  2. TLS — handled upstream by your reverse proxy; internal traffic is HTTP (LAN-only)
  3. Security headers — CSP, HSTS, X-Frame-Options DENY, X-Content-Type-Options, form-action, Permissions-Policy
  4. CSRF — double-submit cookie on all mutating endpoints
  5. Authentication — Argon2id password hashing; RS256 JWT (15-min access + 7-day HttpOnly refresh cookie); TOTP (RFC 6238) with backup codes; brute-force lockout (exponential backoff via Redis)
  6. Rate limiting — Redis sliding window: 20/min on login (per IP), 10/min on TOTP, 20/min on predictions, 300/min general API
  7. Field-level encryption — AES-256-GCM on all PII fields (account names, transaction descriptions, merchant, notes, TOTP secret); IV ‖ ciphertext ‖ tag stored in bytea
  8. Database encryption — pgcrypto as a second layer on backup dumps
  9. Row-level security — PostgreSQL RLS policies enforce user isolation at the DB layer; buggy queries cannot leak another user's data
  10. Audit log — every auth event and data mutation is written to an append-only audit_logs table; the app role has no UPDATE/DELETE on it

Single-user hardening: registration is permanently disabled after the first account is created. Re-enable with ALLOW_REGISTRATION=true if needed.


Tech Stack

Layer Technology
Backend Python 3.12, FastAPI, SQLAlchemy 2.0 async
Frontend React 18, TypeScript, Vite, TanStack Query v5
Styling Tailwind CSS v4, shadcn/ui primitives
Charts Recharts (standard), Plotly (Monte Carlo, candlestick)
Database PostgreSQL 16 with pgcrypto and RLS
Cache / Sessions Redis 7
ML Prophet, statsmodels, NumPy, SciPy
OCR Tesseract 5, pdfplumber, pdf2image
Background jobs APScheduler (in-process)
Containerisation Docker Compose

Getting Started

Prerequisites

  • Docker and Docker Compose
  • An existing reverse proxy (nginx proxy manager, Caddy, Traefik, etc.) pointing at port 8090

1. Clone and configure

git clone https://git.rdx4.com/megaproxy/MyMidas.git
cd MyMidas
cp .env.example .env

Edit .env and fill in every value:

# 32-byte hex key — generate with:
# python3 -c "import secrets; print(secrets.token_hex(32))"
ENCRYPTION_KEY=

# Strong random passwords
DB_PASSWORD=
REDIS_PASSWORD=
BACKUP_PASSPHRASE=

BASE_CURRENCY=GBP
ENVIRONMENT=production
ALLOW_REGISTRATION=false   # set true only for initial account creation

2. Generate JWT keys

mkdir -p secrets
openssl genrsa -out secrets/jwt_private.pem 4096
openssl rsa -in secrets/jwt_private.pem -pubout -out secrets/jwt_public.pem

3. Start

docker compose up -d --build

All four containers start: backend, frontend, postgres, redis.

4. Create your account

Temporarily set ALLOW_REGISTRATION=true in .env, restart the backend, register, then set it back to false:

# In .env: ALLOW_REGISTRATION=true
docker compose up -d backend

# Register via the web UI, then:
# In .env: ALLOW_REGISTRATION=false
docker compose up -d backend

5. Point your reverse proxy

Forward your domain to http://<host>:4000. The frontend nginx serves the React app and proxies all /api/ requests to the backend internally.


AI Receipt Scanning

Receipt scanning uses OCR (Tesseract) to extract text from the image first, then optionally passes that text to an AI model to parse it into structured fields. This means any text-capable LLM works — you're not limited to vision models.

If no AI is configured, or if the AI call fails, a rule-based parser runs on the OCR text as a fallback (finds totals, dates, and merchant names via regex).

Setup

Go to Settings → AI and fill in:

Field Description
Provider Anthropic or OpenAI-compatible
API Key Your key (stored AES-256-GCM encrypted on your server)
Custom API URL Optional — for Open WebUI, LM Studio, Ollama, etc.
Model Optional — defaults to claude-haiku-4-5-20251001 or gpt-4o-mini
Debug mode Shows OCR text and raw AI response in the scan form when enabled

For Open WebUI: set the provider to OpenAI-compatible, enter http://your-server:port as the URL (MyMidas appends /v1/chat/completions), and enter the model name exactly as shown in Open WebUI's interface.

Use the Test connection button to verify your settings before scanning.

Usage

Click Scan Receipt in the transactions toolbar, select a photo or PDF. The form opens pre-filled with extracted fields — review and save. The receipt image is automatically attached to the created transaction.


Backups

Encrypted backups run automatically every night at 3 AM (GPG AES-256 symmetric encryption). Backups are stored in ./data/backups/ and retained for 30 days.

Via the web UI: Settings → Backups — trigger a manual backup, download a copy, or restore from any listed backup file.

Manual backup from the command line:

docker compose exec backend bash /app/scripts/backup.sh

Manual restore (stop the backend first to avoid lock contention):

docker compose stop backend

docker compose run --rm backend bash -c '
  export GNUPGHOME=/tmp/.gnupg; mkdir -p $GNUPGHOME && chmod 700 $GNUPGHOME
  PG_URL=${DATABASE_URL/postgresql+asyncpg/postgresql}
  gpg --batch --yes --no-symkey-cache --pinentry-mode loopback \
      --passphrase "$BACKUP_PASSPHRASE" \
      --decrypt $(ls /app/backups/*.sql.gz.gpg | tail -1) \
  | gunzip | psql "$PG_URL"
'

docker compose start backend

Key Rotation

To rotate the AES-256-GCM encryption key without data loss:

# Generate a new key
NEW_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
echo "New key: $NEW_KEY"

# Dry-run first — validates decryption works, no DB changes
docker compose exec backend python /app/scripts/rotate_keys.py \
  --old-key "$ENCRYPTION_KEY" --new-key "$NEW_KEY" --dry-run

# If dry-run passes, rotate for real (atomic — rolls back on any failure)
docker compose exec backend python /app/scripts/rotate_keys.py \
  --old-key "$ENCRYPTION_KEY" --new-key "$NEW_KEY"

# Update ENCRYPTION_KEY in .env, then restart
docker compose up -d backend

Do not lose your current ENCRYPTION_KEY — without it, all encrypted fields are permanently unreadable.


Environment Variables

Variable Description Default
ENCRYPTION_KEY 32-byte hex key for AES-256-GCM field encryption required
DB_PASSWORD PostgreSQL password required
REDIS_PASSWORD Redis password required
BACKUP_PASSPHRASE GPG symmetric passphrase for backup encryption required
BASE_CURRENCY ISO 4217 base currency GBP
ENVIRONMENT production or development production
ALLOW_REGISTRATION Enable new account creation false

Project Structure

MyMidas/
├── backend/
│   ├── app/
│   │   ├── api/v1/          # REST endpoints
│   │   ├── core/            # Security, encryption, middleware, rate limiting
│   │   ├── db/              # Models, migrations, repositories
│   │   ├── ml/              # Prediction engines
│   │   ├── services/        # Business logic
│   │   └── workers/         # Scheduled jobs (price sync, FX, snapshots, backups)
│   └── alembic/             # Database migrations
├── frontend/
│   └── src/
│       ├── api/             # Typed API client
│       ├── components/      # Shared UI components
│       ├── pages/           # Route-level page components
│       └── store/           # Zustand state (auth, UI/theme)
├── postgres/init/           # PostgreSQL init SQL (extensions, RLS policies)
├── backend/scripts/         # backup.sh, rotate_keys.py, entrypoint.sh
├── secrets/                 # JWT keys (git-ignored, generate locally)
└── docker-compose.yml

Licence

Personal use. Not intended for public deployment or multi-user hosting.