Cairn
Built a private, self-hosted financial dashboard for tracking net worth, modelling FIRE projections, running rule-based financial insights, and setting financial goals — designed specifically for UK users with full Scottish tax band support.
View on GitHub →Overview
Cairn is a personal finance dashboard I built because I wanted full visibility of my financial position without feeding my data into a third-party service. It tracks 11+ account types across pensions, ISAs, savings, property, and debt; models retirement and FIRE projections with adjustable return assumptions; runs a 14-rule financial advisor engine; includes a goals tracker; and features a Claude-powered AI copilot for natural language analysis of your overall position.
Problem
Most personal finance apps are cloud-hosted, subscription-based, and built for a generic audience. None of them modelled Scottish income tax bands correctly, none gave me the kind of rule-based insights I actually wanted (pension headroom, ISA allowance countdowns, salary sacrifice optimisation, carry-forward calculations), and all of them required me to hand over sensitive financial data to a third party. I wanted something self-hosted, private by default, and built around how UK personal finance actually works.
Constraints
- All financial data had to stay on my own infrastructure — no third-party sync or cloud storage
- It needed to handle the full range of UK account types: DC pensions, SIPPs, DB/final salary pensions, S&S ISAs, cash ISAs, property, mortgage, credit cards, loans, and custom accounts
- Scottish income tax bands had to be modelled correctly for the salary sacrifice calculator
- The AI copilot had to be safe to use — no personal identifiers sent to external APIs
- It had to be deployable simply via Docker for easy self-hosting
Approach
I built Cairn as a Flask API backend with a React frontend, packaged into a single Docker image via a multi-stage build. The database is SQLite, which keeps it simple to back up and run without a separate database service. The financial advisor module is rule-based — 14 checks covering everything from ISA deadlines to pension carry-forward opportunities — and runs on every dashboard load. The AI copilot uses the Claude API but only sends numerical summaries, not account names or personal details. A cron job handles automated monthly snapshots for net worth history.
Key Decisions
SQLite over a dedicated database
For a single-user self-hosted app, SQLite is the right call. It's a single file, trivially easy to back up, and removes the need for a separate database container. The performance ceiling is nowhere near a concern at this scale.
Rule-based financial advisor rather than AI-first
Rules are transparent, predictable, and always available. I know exactly why I'm being told to top up my ISA or look at salary sacrifice. The AI copilot is a useful addition on top, not a replacement for deterministic logic.
Send only numerical summaries to the Claude API
The whole point of self-hosting is that sensitive data stays home. The AI copilot gets total values and ratios — enough to give useful commentary — but no account names, institutions, or personal identifiers.
Multi-stage Docker build combining Node and Python
A single image that builds the frontend assets and then serves everything through Flask keeps the deployment simple. One compose file, one container, one port.
Design for local network use
Token-based auth with hashed passwords is appropriate for a home network app. Not every self-hosted tool needs to be hardened for public internet exposure, and keeping the security model honest avoids a false sense of safety.
FIRE scenarios rather than a single projection
Lean, Regular, and Fat FIRE use different safe withdrawal rates (5%, 4%, 3.5%) and give a more honest picture of the range of outcomes. A single number would feel artificially precise for something this speculative.
Tech Stack
- Python 3
- Flask
- Gunicorn
- SQLite
- React
- Vite
- Docker / Docker Compose
- Claude API (Anthropic)
- Bank of England API
- Werkzeug (PBKDF2 auth)
Result & Impact
- Self-hosted via DockerDeployment
- 11+ (pensions, ISAs, savings, property, debt, and more)Account types supported
- FIRE planner, salary sacrifice, debt payoff, mortgage scenarios, carry-forward calculator, goals trackerFinancial tools
Cairn replaced a scattered set of finance mobile apps with a proper dashboard that gives me a complete, private view of my financial position. The monthly snapshots make progress tangible, the rule-based advisor surfaces things I'd otherwise miss or defer, and the FIRE projections give a concrete sense of where things are heading. The goals tracker makes individual targets feel less abstract.
Learnings
- Rule-based systems age better than they're given credit for. Clear, auditable logic is more trustworthy for financial decisions than a black box, even an intelligent one.
- SQLite is genuinely the right database for most self-hosted personal projects. The temptation to reach for Postgres is usually premature.
- Multi-stage Docker builds that combine frontend and backend into one image dramatically simplify self-hosted deployment — one compose file is much easier to maintain than an orchestrated multi-service stack.
- Designing privacy constraints upfront (what goes to the API, what stays local) is much easier than retrofitting them later.
- Offering multiple FIRE scenarios rather than a single projection is more honest — it surfaces how sensitive the outcome is to your assumptions about spending and withdrawal rate.
What it is
Cairn is a private financial dashboard I built and run on my own infrastructure. It tracks my full financial position — pensions, ISAs, savings, property, mortgage, credit cards — takes monthly snapshots to chart net worth over time, and runs a rule-based financial advisor that surfaces actionable insights on every load.
Beyond the core dashboard, it includes:
- A FIRE planner with Lean, Regular, and Fat FIRE scenarios, a Coast FIRE calculator, and a drawdown simulator
- A goals tracker for named financial targets linked to net worth or specific account types
- A tax year summary panel covering ISA contributions, pension contributions, personal allowance status, and a £100k+ taper warning
- A salary sacrifice calculator with accurate Scottish and rUK tax bands
- A carry-forward pension calculator for three-year historical unused allowance calculations
- A debt payoff planner (avalanche vs snowball) and a mortgage scenario modeller
- A Claude-powered AI copilot for natural language analysis of your position
- A Bank of England base rate display for live context
The name comes from the idea of building something one stone at a time.
Why I built it
Most personal finance apps are cloud-hosted, subscription-based, and built for a broad audience that doesn’t include Scottish taxpayers, SIPP holders, or people who want their financial data to stay on their own hardware.
I was managing things across a variety of mobile finance apps. That’s fine for a while, but it doesn’t give you centralised financial projections, it doesn’t surface what you should be doing, and it doesn’t keep a comprehensive history you can actually look back on.
How it works
The backend is a Flask API serving a React frontend, packaged into a single Docker image via a multi-stage build. The database is SQLite — a single file that’s trivial to back up and requires no separate database service.
On the dashboard, you get a live view of all accounts, the current net worth, a snapshot history chart (stacked by asset category with a target line), and the financial advisor panel. The advisor runs 14 rule-based checks every time the page loads:
- ISA allowance usage and days remaining in the tax year
- Pension contribution headroom against the annual allowance
- Carry-forward pension opportunities from the previous three years
- Salary sacrifice opportunities based on current income
- Debt prioritisation and emergency fund status
- Mortgage rate alerts and acceleration opportunities
- Net worth velocity (how quickly things are moving)
- Personal allowance taper warning for income approaching £100k
These are deterministic rules, not model outputs. The logic is transparent and the results are predictable. Insights are filterable by category — Savings, Debt, Pension, Property, Tax.
FIRE planning
The FIRE planner projects retirement across three scenarios using different safe withdrawal rates:
- Lean FIRE — 5% SWR, minimal spending
- Regular FIRE — 4% SWR, the standard assumption
- Fat FIRE — 3.5% SWR, more comfortable spending
For each scenario it calculates the target pot size, years-to-FIRE based on current net worth and monthly savings rate, and whether Coast FIRE has already been reached. A separate drawdown simulator lets you project how long a retirement pot will last given a customisable retirement age, spending level, and State Pension start date. Defined benefit pension income is factored in separately.
Goals tracker
Named financial targets — each with an amount, target date, and emoji — can be linked either to overall net worth or to a specific account type balance. Progress is tracked as a percentage with days remaining, and achieved goals get a badge. It’s a small feature, but it makes individual targets feel concrete rather than abstract.
The AI copilot
The Claude-powered copilot sits alongside the rule-based advisor and lets you ask natural language questions about your financial position. The important detail is what it doesn’t receive: no account names, no institutions, no personal identifiers — only numerical summaries. Totals, ratios, and projections are enough for useful commentary, and keeping it that way means the self-hosted privacy model holds.
Deployment
Cairn runs via Docker Compose. The multi-stage Dockerfile builds the React frontend and then hands off to Gunicorn serving the Flask app with static assets included. One image, one container, one port. A cron job inside the container handles the automated monthly snapshots. It’s hosted on a Ubuntu virtual server alongside a number of other Docker containers managed via Portainer.
For home network use, token-based auth with PBKDF2-hashed passwords and 7-day sessions is the right level of security. If you wanted to expose it externally, you’d put it behind a reverse proxy with HTTPS and something like Authelia in front — but for a local-only tool, keeping the security model honest is better than over-engineering it.
Accounts can be reordered via drag-and-drop, filtered by type, and searched by name. A full JSON export and import is available for backups and migration. Dark and light themes are supported and persisted in localStorage.