# Purrse A self-hosted personal finance manager built with ASP.NET Core and Vue 3. > **Work in Progress** — Purrse is under active development and subject to change. Features may be added, removed, or reworked without notice. > **Source Code Note** — GitHub is not the primary repository for this project. The GitHub mirror is updated periodically and may not always reflect the latest state of development. ## About Purrse is a self-hosted personal finance application for tracking accounts, transactions, budgets, loans, investments, and more. It supports automatic bank syncing via SimpleFIN and Plaid, AI-powered transaction categorization and chat through a local Ollama LLM, and multi-format file imports. ### AI-Assisted Development Purrse is developed with the assistance of [Claude Code](https://docs.anthropic.com/en/docs/claude-code), Anthropic's CLI tool for AI-assisted software development. Claude Code contributes to feature implementation, code review, refactoring, and security hardening throughout the project. ## Tech Stack | Layer | Technology | |-------|-----------| | Backend | ASP.NET Core 9 (.NET 9) | | Frontend | Vue 3, TypeScript, Vuetify 3 | | Database | PostgreSQL 17 | | ORM | Entity Framework Core 9 | | Real-time | SignalR | | Charts | ApexCharts | | AI | Ollama (local LLM) | | Bank Sync | SimpleFIN, Plaid | | Containerization | Docker Compose | ## Features ### Financial Management - **Accounts** — Checking, savings, credit card, loan, mortgage, brokerage, retirement, and more - **Transactions** — Full CRUD with categorization, payee tracking, transfers, and status management (pending, cleared, reconciled) - **Budgets** — Monthly budgets with per-category targets and actual vs. planned tracking - **Scheduled Transactions** — Recurring transactions with automatic posting via background service - **Loans** — Amortization schedules, payoff scenarios, and interest tracking - **Investments** — Holdings management with security prices, cost basis, gain/loss, and performance tracking - **Reconciliation** — Bank statement reconciliation workflow - **Reports** — Spending by category, income vs. expense, net worth over time, cash flow analysis - **Dashboard** — Net worth, account summaries, recent transactions, spending breakdown, upcoming bills ### Bank Sync - **SimpleFIN** — Open banking standard for automatic transaction syncing - **Plaid** — Account aggregation with sandbox, development, and production environments - **Background Sync** — Configurable sync intervals with automatic background processing - **Duplicate Detection** — FitId matching and fuzzy fingerprint-based deduplication - **Sync History** — Detailed logs with import/skip counts and error tracking ### AI Features (Ollama) - **Transaction Categorization** — Automatic classification with configurable confidence thresholds and customizable prompts - **Chat Assistant** — Conversational finance assistant with tool-calling support for querying transactions, spending reports, balances, budgets, and more - **Local Processing** — All AI processing runs locally via Ollama; no data is sent to external services ### Data Import - **OFX** (Open Financial Exchange) - **CSV** (Comma-Separated Values) - **QIF** (Quicken Interchange Format) - Import preview with duplicate detection and batch confirmation ### Plugin System - Extensible architecture for file parsers and bank sync providers - Automatic plugin discovery and loading at startup ## Security & Encryption Purrse handles sensitive financial data and takes security seriously: ### Authentication & Authorization - **JWT Bearer Authentication** with configurable expiration and zero clock skew - **BCrypt Password Hashing** with a work factor of 12 - **Refresh Token Security** — tokens are SHA-256 hashed before database storage; raw tokens are never persisted - **Password Strength Validation** — minimum 8-character requirement enforced server-side - **User Isolation** — all API endpoints enforce `userId` scoping via JWT claims; users can only access their own data ### Encryption - **AES-256-GCM Authenticated Encryption** for bank sync credentials (access tokens, API keys) - **HKDF Key Derivation** (SHA-256) to derive encryption keys from the configured master key - **Versioned Encryption Format** — ciphertext includes a version byte prefix for forward-compatible algorithm upgrades ### API Security - **Rate Limiting** — sliding window rate limiter on all endpoints (10 req/min for auth, 120 req/min for general API) - **SSRF Protection** — Ollama URL validation blocks private IPs, IPv6-mapped addresses, link-local ranges, and cloud metadata endpoints - **IDOR Prevention** — all resource access verified against authenticated user; no cross-user data access - **File Upload Limits** — 10 MB maximum with filename sanitization - **Pagination Caps** — server-enforced maximum page size (200) to prevent data exfiltration - **Input Validation** — parameterized queries throughout; no raw SQL interpolation ### Transport & Headers - **Security Headers** — `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Strict-Transport-Security`, `Referrer-Policy`, `X-Permitted-Cross-Domain-Policies`, `Cache-Control: no-store` - **CORS** — configured origin allowlist with credential support - **PostgreSQL SSL** — connections use `Ssl Mode=Prefer` with certificate trust ### Production Safeguards - **Startup Validation** — application refuses to start in Production if JWT key or encryption key are still set to defaults - **Account Number Masking** — only the last 4 digits are returned by the API ## Quick Start ### Prerequisites - [Docker](https://www.docker.com/) and Docker Compose ### Run with Docker Compose 1. Clone the repository: ```bash git clone https://github.com/yourusername/Purrse.git cd Purrse ``` 2. Copy the environment example and configure: ```bash cp .env.example .env ``` Edit `.env` and set your secrets: ```env DB_PASSWORD=your_secure_database_password JWT_KEY=YourSuperSecretKeyHere_AtLeast32Characters! BANKSYNC_ENCRYPTION_KEY=your_32_char_aes256_key_here!!!! ``` Generate a secure encryption key: ```bash openssl rand -base64 32 ``` 3. Start the application: ```bash docker compose up -d ``` 4. Access Purrse: - **Frontend**: http://localhost:9080 - **API**: http://localhost:5000 ### Default Ports | Service | Port | |---------|------| | Frontend | 9080 | | API | 5000 | | PostgreSQL | 5433 | ### Environment Variables | Variable | Description | Default | |----------|-------------|---------| | `DB_PASSWORD` | PostgreSQL password | `purrse_dev` | | `JWT_KEY` | JWT signing key (min 32 chars) | Dev default (blocked in production) | | `BANKSYNC_ENCRYPTION_KEY` | AES-256 encryption key for bank credentials | Dev default (blocked in production) | | `ASPNETCORE_ENVIRONMENT` | Runtime environment | `Development` | ## Project Structure ``` Purrse/ ├── src/ │ ├── Purrse.Api/ # ASP.NET Core API │ │ ├── Controllers/ # REST endpoints │ │ ├── Services/ # Business logic │ │ ├── Hubs/ # SignalR real-time │ │ └── Middleware/ # Request logging, error handling │ ├── Purrse.Core/ # Domain models, DTOs, interfaces │ ├── Purrse.Data/ # EF Core DbContext & migrations │ └── Purrse.Plugins.*/ # File parsers & bank sync providers ├── frontend/ # Vue 3 + Vuetify SPA │ └── src/ │ ├── views/ # Page components │ ├── components/ # Reusable components │ ├── services/ # API clients │ ├── stores/ # Pinia state management │ └── types/ # TypeScript definitions └── docker-compose.yml ```