Personal Finance AI
ActiveA personal financial assistant that ingests AEB-43 (Norma 43) bank exports into a double-entry ledger and exposes a Claude-powered agent that answers natural-language questions about my finances and renders charts on demand.
What it is
A self-hosted personal finance system. It ingests Spanish bank exports in AEB-43 (Norma 43) format, stores every movement in a strict double-entry ledger, and lets me ask questions in natural language to a Claude-powered agent that has tool access to my own data.
It is the closest thing I have built to “what an accountant who actually understands my data feels like”.
Why I built it
I wanted three things off-the-shelf tools couldn’t give me at the same time:
- Accuracy. Most personal finance apps rely on category soup. I wanted real bookkeeping — double-entry, P&L, cash flow — so the same question always returns the same number.
- Privacy. My data lives on my own server. No third party sees the movements.
- AI-native. I wanted the answers in plain language, with charts when relevant, and decisions documented as ADRs because the architecture matters.
The interesting parts
The ledger is the only source of truth
ADR-002 declares the double-entry ledger (LedgerAccount, JournalEntry, JournalLine) as the single source of truth for any financial query. Bank Transaction rows feed the ledger at import time, and the chat agent never queries Transaction directly — only the ledger.
The payoff: balances and P&L are always consistent because they are derived from the same accounting primitives. Adding a new financial operation forces me to define the journal entries that represent it, which keeps the data model honest.
Hexagonal architecture, but only where it earns its weight
ADR-001 commits to selective hexagonal architecture: full domain/ + application/ + infrastructure/ only in modules with non-trivial business logic (ledger, import, budgets, cashflow, subscriptions). Simple CRUD modules (transactions, goals, reports) stay flat.
The trade-off — inconsistency between modules — is documented and accepted. Empty layers in CRUD modules are worse than a small style mismatch.
A Claude agent with curated tools
The agent (@anthropic-ai/sdk + tool use loop) gets a small, deliberate set of tools:
query_ledger— Claude writes SQL against the ledger; onlySELECT, with a mandatoryworkspaceIdfilterquery_budgets,query_reports,get_cashflow_prediction,query_goals,simulate_payroll— structured queries over derived data, no raw SQLgenerate_chart— returns a Recharts config that the frontend renders
The pattern that pays off: the LLM does not have a generic SQL tool over the whole DB. It has tools shaped around how a financial advisor would ask questions. The validator on query_ledger rejects anything outside the contract.
Async work goes through BullMQ
Imports, recurring-expense reconciliation, scheduled reports, and Telegram bot interactions run through BullMQ + Redis. The HTTP layer never blocks on a slow operation. The Telegram bot (telegraf) lets me ask questions from my phone without opening the web UI.
Status
Active development. The repo currently includes 25+ NestJS modules covering ledger, import, budgets, cashflow, goals, payroll, recurring expenses, subscriptions, alerts, insights, exports (PDF + Excel), and a Telegram bot. ADRs document the decisions that matter. Self-hosted on a Hetzner server in production.
The repo is private — the value of the project for me is in using it, not in distributing it.