Personal finance that stays on your own devices.
Budget, bank sync, net worth and investments — on iPhone, Mac and your own server.
Download · Features · Get started · Connect a bank · Contributing
- 🔒 Your data never leaves your devices. No Florin account, no Florin server, no analytics, no tracking SDK. Merchant logos on iPhone come straight from each merchant's own website, never from a logo service — and can be turned off.
- 🏦 Real bank sync, under your own credentials. PSD2 access to 2 000+ European banks through Enable Banking — your own free registration, not a shared key.
- 🎯 Figures that match the bank. Loans follow a real amortisation schedule; savings rates count complete months only; when a number cannot be computed honestly, Florin says so instead of showing zero.
- 🗂️ A budgeting workflow, not a chart gallery. A monthly plan in category groups and a review queue that learns from how you file your own history.
- 📈 Everything in one place. Current accounts, savings, loans and a stock portfolio, in one net worth.
- 🧾 Open source, AGPL-3.0. Read it, run it, fork it.
| Platform | Get it | Notes |
|---|---|---|
| iPhone | App Store — in review | Meanwhile: TestFlight or the sideloadable .ipa |
| Mac | Florin-*-arm64.dmg |
Signed, notarised, updates itself |
| Self-hosted | Florin-*-server.tar.gz |
docker compose up -d — see below |
Nothing to sign up for, anywhere. Want to look around first? The iPhone app has a demo on its first screen, with invented accounts you can erase from Settings.
| iPhone | Mac | Web | |
|---|---|---|---|
| Budgeting | |||
| What's left to spend this month, and the daily pace it allows | ✅ | ✅ | ✅ |
| Month-end projection that narrows as the month fills | ✅ | ✅ | ✅ |
| Monthly plan in category groups, carried over from last month | ✅ | ✅ | ✅ |
| Review queue that learns from your own filing | ✅ | ✅ | ✅ |
| Create, rename and remove categories | ✅ | ✅ | ✅ |
| Rename a merchant once — the bank's label, shown your way | ✅ | — | — |
| Money in | |||
| PSD2 bank sync through your own Enable Banking app | ✅ | ✅ | ✅ |
| CSV / OFX import that reads real French, German and English exports | ✅ | ✅ | ✅ |
| Manual entry and transfers between your own accounts | ✅ | ✅ | ✅ |
| Apple Pay payments listed as upcoming the moment you pay, then replaced by the bank's row | ✅ | — | — |
| YNAB-style spreadsheet import | — | — | ✅ |
| Wealth | |||
| Loans on a real amortisation schedule | ✅ | ✅ | ✅ |
| Holdings with cost basis, gain, and opt-in live quotes | ✅ | ✅ | ✅ |
| Net worth over time, allocation, rolling savings rate | ✅ | ✅ | ✅ |
| Balance corrections booked as visible adjustments | ✅ | ✅ | ✅ |
| Living with it | |||
| Hide every amount in one gesture | ✅ | ✅ | ✅ |
| Face ID / PIN lock | ✅ | ✅ | — |
| Home-screen widget / menu-bar widget | ✅ | ✅ | — |
| One morning summary; a payment banner only if you set up the Apple Pay automation | ✅ | — | — |
| Languages | 8 | 3 | 3 |
iPhone: English, French, German, Dutch, Italian, Spanish, Portuguese, Catalan. Mac and web: English, French, Dutch.
Open the app and pick how to start: connect a bank, enter accounts by hand, import a statement, restore a backup — or Try the demo. Everything lives in a SQLite ledger on the phone, included in its iCloud backup. It can also act as a client of your own Florin server (Settings → Use my Florin server).
Sideload, TestFlight, or build it yourself
- Sideload. Each release carries
Florin-<version>-unsigned.ipa, re-signed on the way in with your own Apple ID by AltStore, SideStore or Sideloadly. A free Apple ID re-signs for seven days at a time.- Bank sync needs an Associated Domains entitlement, which a free Apple ID cannot declare: host your own
apps/site/.well-known/apple-app-site-associationand pointBankingFlow.redirectHostandproject.ymlat it. - Notifications and background refresh are gated the same way.
- Bank sync needs an Associated Domains entitlement, which a free Apple ID cannot declare: host your own
- TestFlight. Properly signed, no seven-day expiry, bank sync works. The steps and the review note are in
apps/ios/TESTFLIGHT.md. - Build it with Xcode 26+ and XcodeGen:
Pick your team in Signing & Capabilities. Deployment floor: iOS 17.4.
cd apps/ios && xcodegen generate && open Florin.xcodeproj
Download the .dmg, drag Florin to Applications, launch — onboarding covers language, categories and your first account. Updates arrive on their own (checked at launch and every 6 hours). Data lives in ~/Library/Application Support/@florin/desktop/florin.db.
Build the Mac app from source
Requires Node 22 — better-sqlite3 has no prebuilt binary for newer majors.
pnpm install
pnpm --filter @florin/desktop run pack # → apps/desktop/dist/Florin-<version>-<arch>.dmgLocal builds are unsigned: right-click → Open, or xattr -dr com.apple.quarantine /Applications/Florin.app. Releases are cut by pushing a Florin-v* tag; CI builds both architectures, signs and notarises. Forking? Point publish.owner/publish.repo in apps/desktop/electron-builder.yml at your repo, or your users will auto-update onto upstream builds.
A single-admin Next.js + Postgres stack, installable as a PWA. Needs Docker, plus Node 22 and pnpm for the setup scripts.
Install steps
git clone https://github.com/adrbn/florin.git && cd florin
cp .env.example .env
openssl rand -base64 32 # → DB_PASSWORD
openssl rand -base64 32 # → NEXTAUTH_SECRET
cd apps/web && pnpm install
pnpm tsx scripts/hash-password.ts "your-strong-password" # → ADMIN_PASSWORD_HASHPaste the hash into .env, escaping every $ as \$, then:
cd .. && docker compose up -d
cd apps/web && pnpm drizzle-kit migrate && pnpm tsx src/db/seed.tsOpen http://localhost:3000. Put a reverse proxy with TLS in front (Caddy, Traefik, Tailscale Serve) before exposing it anywhere.
Florin has no shared bank connection. You register your own free Enable Banking application once; the private key never leaves your device.
| iPhone | Mac | Web | |
|---|---|---|---|
| 1. Key | Settings → Bank connection → Create a key | Settings → Bank Sync → Generate a key | openssl genrsa -out enablebanking-private.pem 2048 |
| 2. App | Create a Production app, paste the certificate | Create an app, paste the public key | Create an app, upload the public key |
| Redirect URI | shown in the app | https://127.0.0.1:3847/api/banking/callback |
https://<your-domain>/api/banking/callback |
| 3. App ID | Paste it back in Settings | Paste it back in Settings | ENABLE_BANKING_* in .env |
No bank? Manual entry and CSV/OFX import cover everything else.
France/EUR-first defaults, every one a setting — environment variables on the web, Settings → App on the Mac.
| Setting | Default | What it does |
|---|---|---|
APP_CURRENCY |
EUR |
Display currency and number formatting |
APP_GOAL_TARGET |
100000 |
Long-term wealth target on the goal card |
APP_GOAL_RETURN_PCT |
7 |
Assumed net annual return for the projection |
APP_PEA_CEILING |
150000 |
Contribution cap for a tax wrapper (France's PEA); 0 hides it |
APP_DCA_MONTHLY |
(blank) | Planned monthly investment; blank = inferred from history |
PRICE_PROVIDER |
none |
yahoo opts into live quotes; off by default, no outbound calls |
| iPhone | Mac | Web | |
|---|---|---|---|
| Import | Settings → Import a statement | Drop a CSV/OFX on an account | Drop a CSV/OFX on an account |
| Backup | iCloud backup, plus Export a copy (plain SQLite) | Copy florin.db, or JSON export |
pg_dump (below) |
| Move to a new device | Restore a copy, also offered at first launch | Copy the file across | Restore the dump |
Commands
# Web — a timestamped Postgres dump
docker exec florin-db pg_dump -U florin -d florin --no-owner --no-privileges \
| gzip -9 > "backups/florin-$(date -u +%Y%m%dT%H%M%SZ).sql.gz"
# Web — import a YNAB-style spreadsheet (idempotent)
cd apps/web && node --env-file=.env --import tsx scripts/import-legacy-xlsx.ts /path/to/finances.xlsxA restored iPhone copy does not carry the bank connection: a PSD2 session restored onto another phone is a dead session, and the signing key is marked ThisDeviceOnly. Reconnect the bank on the new phone.
apps/
ios/ SwiftUI app with its own SQLite ledger (XcodeGen)
desktop/ Electron + Next.js + SQLite, menu-bar widget
web/ Next.js 15 + Postgres, Docker
site/ Static pages and the Associated Domain file for bank redirects
packages/
core/ Shared UI, i18n, bank client, categorisation
db-pg/ Postgres schema and queries ┐ deliberate twins —
db-sqlite/ SQLite schema and queries ┘ change them together
scripts/ Mirror one ledger onto another (SQLite ⇄ Postgres)
Issues and pull requests are welcome. CONTRIBUTING.md covers the setup for each platform and the two rules that matter most:
- the Postgres and SQLite packages change together;
- a computed figure is tested against something real.
Security issues go through SECURITY.md, privately. If Florin is useful to you, you can buy me a coffee.
Florin is open source under the AGPL-3.0: self-host it, fork it, modify it, redistribute it. Any hosted or distributed derivative must publish its source — a Florin someone runs for you should be a Florin you can read.
The name and logo are not part of that grant. A modified version ships under its own name and icon; see TRADEMARK.md and the section 7 terms in NOTICE.md. Florin collects no data at all — see PRIVACY.md.




