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.
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.
Purrse is developed with the assistance of 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.
| 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 |
- 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
- 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
- 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
- OFX (Open Financial Exchange)
- CSV (Comma-Separated Values)
- QIF (Quicken Interchange Format)
- Import preview with duplicate detection and batch confirmation
- Extensible architecture for file parsers and bank sync providers
- Automatic plugin discovery and loading at startup
Purrse handles sensitive financial data and takes security seriously:
- 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
userIdscoping via JWT claims; users can only access their own data
- 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
- 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
- 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=Preferwith certificate trust
- 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
- Docker and Docker Compose
-
Clone the repository:
git clone https://github.com/catrenelle/Purrse.git cd Purrse -
Copy the environment example and configure:
cp .env.example .env
Edit
.envand set your secrets:DB_PASSWORD=your_secure_database_password JWT_KEY=YourSuperSecretKeyHere_AtLeast32Characters! BANKSYNC_ENCRYPTION_KEY=your_32_char_aes256_key_here!!!!
Generate a secure encryption key:
openssl rand -base64 32
-
Start the application:
docker compose up -d
-
Access Purrse:
- Frontend: http://localhost:9080
- API: http://localhost:5000
| Service | Port |
|---|---|
| Frontend | 9080 |
| API | 5000 |
| PostgreSQL | 5433 |
| 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 |
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
This project is licensed under the MIT License.