A local-first personal finance manager for the desktop. Import your bank statements, see where your money goes, and keep your data on your machine.
🇫🇷 French first · 🇬🇧 English
Important
Your data never leaves your computer. There are no bank connections, no cloud account and
no telemetry. The backend listens on 127.0.0.1 only, and the optional AI runs on a local
model.
| 📥 Imports | OFX/QFX statements, routed to the right account by its bank id, with deduplication and an import history that flags balance mismatches. |
| 📊 Dashboard | Monthly income and expenses, month-over-month trend, savings rate, and a breakdown by category. |
| 🏷️ Categorisation | Deterministic rules first, then an optional local model for the rest. Uncertain guesses go to a review queue, and a correction can become a rule. |
| 🔁 Subscriptions | Recurring charges detected automatically, including price changes and missed payments. |
| 🎯 Goals | Virtual savings envelopes that track progress without moving any money. |
| 🏠 Patrimoine | Mortgages with derived amortisation schedules and the debt ratio, a new-loan simulator against the HCSF limits, and a net-worth view across accounts, properties and loans. |
| 💾 Backup | Export everything to one .bastide file and restore it anywhere. |
Tip
Just want to use Bastide? Download it from the Releases page and follow the install guide (en français). The steps below are for working on the code.
Note
You need uv and the Flutter SDK with desktop support enabled for your OS.
# 1 · backend — the local API sidecar
cd backend
uv sync
uv run alembic upgrade head
uv run uvicorn app.main:app --host 127.0.0.1 --port 8765 --reload
# 2 · frontend — in a second terminal
cd frontend
flutter pub get
flutter run -d windows # or macos / linuxuv run python -m app (or bastide-backend) starts the backend the way the desktop app will:
it backs up and migrates the database itself, then prints READY <port> <version>. It takes
--port 0 for a free port, --data-dir for another data folder, and --exit-on-stdin-close.
Logs go to stderr and to logs/backend.log in the data folder. Use the uvicorn command above
when you want --reload.
Tip
AI categorisation is optional. Run Ollama or llama-server with a
small model (Gemma 4 E4B by default), then switch it on under Paramètres → Données → IA
locale; an Ollama on its default port is detected and offered there. Without a model, the rules still categorise everything they match, and the rest goes
to the review queue.
With the backend running, interactive API docs are served at http://127.0.0.1:8765/docs.
flowchart LR
UI["Flutter desktop app<br/>Riverpod · fr/en"] -- "REST · loopback only" --> API["FastAPI sidecar<br/>Python 3.14"]
API --> DB[("SQLite · WAL<br/>SQLAlchemy + Alembic")]
API -. optional .-> LLM["Local LLM runtime<br/>Ollama / llama-server"]
- Money is integer minor units, and rates are integer basis points. Floats are never used.
- Derived figures are not stored. Amortisation schedules, simulations and net worth are computed from what you declared, so they can't go out of sync with it.
- Feature-first on both sides: each feature owns its routes, models and services in the backend, and its screens and state in the frontend.
| Document | What's inside |
|---|---|
docs/database.md |
ER diagram and constraints, generated from the migrated database |
docs/api.md |
Every endpoint, generated from the OpenAPI schema |
docs/install.md |
Installing, updating and uninstalling the app, where the data lives (fr) |
docs/development.md |
Setup, running, CI checks, and working with coding agents |
docs/design/ |
The binding design system and one spec per panel |
.claude/skills/ |
Area-by-area rules for coding agents (database, testing, i18n, …) |
Note
The schema and API references are checked by the test suite and fail CI when they go stale.
After changing a migration or a route, regenerate them from backend/ with
uv run python -m scripts.schema_doc and uv run python -m scripts.api_doc.
Bastide/
├── backend/ FastAPI sidecar · app/features/<feature>/ · Alembic migrations · pytest
├── frontend/ Flutter desktop app · lib/features/<feature>/ · ARB l10n (fr, en)
├── docs/ generated references, design specs, JSON schemas, dev guide
├── .claude/skills/ conventions for coding agents
└── CLAUDE.md global agent conventions (backend/ and frontend/ extend it)
- Commits follow Conventional Commits:
type(scope): subject, one logical change each. - i18n from day one. User-facing text is never hard-coded, and French and English stay in parity.
- Tests ship with every change, with external dependencies (model, network, clock,
filesystem) mocked. CI runs
ruff,tyandpyteston the backend, andanalyze,format,gen-l10nandteston the frontend, plus dependency-vulnerability and secret scanners. - Fixed chrome. The sidebar and top bar are identical on every panel.
Bastide is licensed under the GNU Affero General Public License v3.0. You may use, modify and share it, but if you distribute a modified version or offer it as a network service, you must release its source under the same license.
The bundled Geist and Space Grotesk fonts keep their own SIL Open Font License. The Bastide name and logo are not covered by the AGPL.