Skip to content

btwigley/ledger-finance

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

Ledger

A self-hosted personal finance tracker with automatic bank sync via Plaid, rule-based transaction categorization, recurring transaction detection, and an encrypted credential store.

Features

  • Automatic Bank Sync — Connect bank accounts via Plaid and sync transactions on a configurable schedule (default: every 6 hours)
  • Rule-Based Categorization — Classify transactions automatically using merchant name regex, MCC codes, and amount ranges
  • Recurring Detection — Identifies recurring charges (subscriptions, bills) using both Plaid's recurring streams and custom pattern matching
  • Encrypted Token Storage — Plaid access tokens are AES-encrypted at rest using a key you control
  • Dashboard — Jinja2-templated web UI with account overview, transaction search, reports, and rule management
  • Single-User Auth — bcrypt-hashed password with session-based login and brute-force lockout (Redis-backed)
  • Rate Limiting — Per-IP in-process rate limiter on sensitive endpoints
  • Health Checks/health (lightweight) and /readyz (DB + Redis verification)

Tech Stack

Component Technology
Backend Python 3.12+, FastAPI, SQLAlchemy
Database MySQL 8.0+ (MariaDB compatible)
Cache/Sessions Redis 5.0+
Bank Data Plaid API
Scheduling APScheduler
Encryption cryptography (Fernet)
Auth bcrypt + Redis session store
Frontend Jinja2 templates (server-rendered)

Quick Start

Prerequisites

  • Python 3.12+
  • MySQL 8.0+ or MariaDB 10.6+
  • Redis 5.0+
  • A Plaid account (sandbox is free)

Setup

cd server

# Create virtual environment
python -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate   # Windows

# Install dependencies
pip install -r requirements.txt

# Configure environment
cp .env.template .env
# Edit .env with your database credentials, Plaid keys, and encryption key

Database Setup

Run the migrations in order:

mysql -u ledger_api -p ledger < migrations/001_initial_schema.sql
mysql -u ledger_api -p ledger < migrations/002_starter_rules.sql
mysql -u ledger_api -p ledger < migrations/003_simplify_business_units.sql
mysql -u ledger_api -p ledger < migrations/004_fix_mcc_column_length.sql

Generate Credentials

# Generate encryption key for Plaid token storage
python -c "import os; print(os.urandom(32).hex())"

# Generate admin password hash
python -c "import bcrypt; print(bcrypt.hashpw(b'YOUR_PASSWORD', bcrypt.gensalt()).decode())"

Run

uvicorn app.main:app --host 127.0.0.1 --port 8084

Visit http://localhost:8084 to access the dashboard.

Configuration

All configuration is via environment variables (see .env.template for the full list):

Variable Default Description
APP_ENV development Environment mode
DB_HOST localhost MySQL host
DB_NAME ledger Database name
PLAID_CLIENT_ID Your Plaid client ID
PLAID_SECRET Your Plaid secret key
PLAID_ENV production sandbox or production
PLAID_ENCRYPTION_KEY Hex-encoded 32-byte key for token encryption
SYNC_INTERVAL_HOURS 6 How often to sync transactions
ADMIN_USERNAME admin Dashboard login username
ADMIN_PASSWORD_HASH bcrypt hash of admin password
REDIS_DB 5 Redis database number
DISPLAY_TIMEZONE US/Eastern Timezone for dashboard display

Architecture

server/
├── app/
│   ├── main.py           # FastAPI app, middleware, lifespan
│   ├── config.py         # Pydantic settings (env-backed)
│   ├── database.py       # SQLAlchemy engine + session factory
│   ├── encryption.py     # AES encryption for Plaid tokens
│   ├── models.py         # SQLAlchemy ORM models
│   ├── plaid_client.py   # Plaid API client singleton
│   ├── recurring.py      # Recurring transaction detection
│   ├── rules.py          # Rule engine for categorization
│   ├── scheduler.py      # APScheduler background jobs
│   ├── sync.py           # Plaid transaction sync logic
│   └── dashboard/
│       ├── api.py        # REST API endpoints
│       ├── auth.py       # Login, sessions, lockout
│       ├── routes.py     # Template-rendered pages
│       └── templates/    # Jinja2 HTML templates
├── deploy/
│   ├── ledger-api.service    # systemd unit (example)
│   └── nginx-ledger-api.conf # nginx reverse proxy (example)
└── migrations/
    ├── 001_initial_schema.sql
    ├── 002_starter_rules.sql
    ├── 003_simplify_business_units.sql
    └── 004_fix_mcc_column_length.sql

Deployment

Example systemd and nginx configs are provided in server/deploy/. These are templates — update the domain, paths, and user to match your environment.

Plaid Integration

  1. Sign up at dashboard.plaid.com
  2. Use sandbox mode for testing (free, uses test credentials)
  3. For production, apply for Plaid access (approval required for production credentials)
  4. Add your PLAID_CLIENT_ID and PLAID_SECRET to .env
  5. Generate an encryption key and add as PLAID_ENCRYPTION_KEY
  6. Use the dashboard to link accounts via Plaid Link

Security

  • Plaid access tokens are encrypted at rest (AES via Fernet)
  • Admin password stored as bcrypt hash (never plaintext)
  • Redis-backed login lockout after failed attempts
  • Per-IP rate limiting on authentication and API endpoints
  • No secrets in source code — everything via .env
  • systemd hardening: NoNewPrivileges, ProtectSystem=strict, PrivateTmp

License

MIT — see LICENSE.


Built by Wigley Studios

About

Self-hosted personal finance tracker with Plaid bank sync, rule-based categorization, and encrypted credential storage

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors