- TypeScript 46.5%
- JavaScript 42.4%
- HTML 7%
- CSS 2.4%
- Java 0.9%
- Other 0.8%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- l'SSO vince: se esiste già un account con la stessa email (anche creato con la password) si entra in quello, che viene collegato all'SSO - nuovo utente anche se il provider dichiara l'email non verificata (Authentik di default manda email_verified=false); restano invito o REGISTRATION=sso/open per creare account nuovi - README ed .env.example aggiornati Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
| .forgejo/workflows | ||
| backend | ||
| docs/screenshots | ||
| fdroid | ||
| frontend | ||
| scripts | ||
| site | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.registry.yml | ||
| docker-compose.yml | ||
| README.md | ||
FinaFlow
Le tue finanze personali, sul tuo server.
Conti, movimenti, pagamenti ricorrenti, budget mensile sicuro e proiezione del saldo: un'app web self-hosted che ti dice quanto puoi spendere senza mai restare scoperto.
Perché FinaFlow
La maggior parte delle app di budget guarda al passato. FinaFlow guarda avanti: conosce le tue entrate e uscite ricorrenti (stipendio, affitto, bollette, rate) e calcola quanto puoi spendere liberamente ogni mese perché il conto principale non vada mai sotto zero nei prossimi 24 mesi, anche se lo stipendio arriva in ritardo.
I dati restano tuoi: gira con Docker su un tuo server, un NAS o un VPS, senza servizi esterni.
Funzionalità
Soldi e conti
- Conti di ogni tipo (corrente, deposito, contanti, carta, investimenti), con nota libera e saldo calcolato in tempo reale.
- Totale utilizzabile e totale di tutti i conti, più totali per tipo.
- Risparmi, obiettivi con cifra da raggiungere (i versamenti si fermano a obiettivo raggiunto) e depositi esclusi dal totale, con importo nascondibile.
- Conti remunerati: tasso annuo lordo, ritenuta e saldo massimo. Gli interessi maturano ogni giorno e vengono accreditati in automatico (mensile, trimestrale o annuale) al netto; la proiezione ne tiene conto.
- Trasferimenti tra conti.
Movimenti
- Entrate e uscite con categorie e sottocategorie.
- Filtri per mese, intervallo di date, tipo, conto, categoria e sottocategoria.
- Ricerca libera su nome, categoria, sottocategoria e note.
- Movimento veloce dalla Panoramica o dal pulsante «+» su telefono.
- Export CSV per mese o intervallo di date.
Ricorrenti e finanziamenti
- Pagamenti ricorrenti giornalieri, settimanali, mensili o annuali, con intervallo («ogni 3 mesi») e data di fine.
- Numero di rate → la data di fine si calcola da sola. Le ricorrenze con una fine diventano finanziamenti, con debito residuo e avanzamento.
- Pagato / È arrivato registra il movimento e passa alla scadenza successiva.
- Ritardo massimo dello stipendio (es. «fino a +3 giorni»), di cui budget e proiezioni tengono conto.
Budget e proiezioni
- Budget libero mensile sicuro, ricalcolato mese per mese su 24 mesi.
- Spendi il X%: metti da parte ogni mese una riserva per gli imprevisti.
- Budget per categoria con barra di avanzamento.
- Proiezione di cassa giorno per giorno dei conti inclusi nel totale, come grafico o tabella mese per mese (saldo a fine mese e minimo), con o senza il budget libero speso.
- Dove vanno i miei soldi: diagramma di flusso dalle entrate alle spese per categoria e a quanto risparmi, per mese o per anno.
- Categorie per anno: totali di ogni categoria e sottocategoria, anno per anno.
Prestiti
- Soldi prestati a qualcuno, con restituzioni parziali e quanto deve ancora rientrare.
Utenti e sicurezza
- Il primo utente diventa amministratore. Gli altri entrano su invito (con password o SSO) oppure si registrano da soli: con
REGISTRATION=ssosolo tramite SSO, conREGISTRATION=openanche con email e password (confermando l'email). SSO con qualsiasi provider OIDC (Authentik, Google...). - Email e password con verifica via email, recupero password (massimo 3 richieste al giorno).
- Verifica in due passaggi: con l'app di autenticazione (TOTP, con codici di recupero) oppure, per chi non la usa, con un codice via email a ogni accesso con password (serve l'SMTP configurato); passkey; «esci dagli altri dispositivi».
- Password salvate con scrypt (parametri OWASP), segreti 2FA e password SMTP cifrati nel database, limiti ai tentativi per IP e per account, intestazioni di sicurezza (CSP, anti-iframe).
- Ogni utente vede solo i propri dati: l'amministratore gestisce utenti e inviti, mai i conti degli altri.
- Backup completo in JSON (export e import) ed eliminazione dell'account con tutti i dati.
Assistente AI (MCP)
- Server MCP integrato: collega Claude (o un altro assistente compatibile) ai tuoi dati e chiedi «quanto ho speso al bar a settembre?» o «segna 12 € di spesa di oggi sul conto corrente».
- Token personali da Profilo e sicurezza, in sola lettura oppure lettura + movimenti: l'assistente può aggiungere movimenti e modificare o eliminare solo quelli creati da lui; conti, categorie, budget e ricorrenze non si toccano. Vedi Server MCP per l'assistente AI.
Interfaccia
- PWA: da Chrome, Edge o Safari «Installa app» / «Aggiungi a schermata Home» e FinaFlow si apre a schermo intero come un'app, con icona e scorciatoie (movimento veloce, movimenti, budget). Si aggiorna da sola: quando esce una nuova versione compare «Aggiorna».
- Tema chiaro e scuro che segue il sistema.
- Su telefono: barra in basso stile app e tabelle a schede.
Screenshot
![]() |
![]() |
| Movimenti con filtri, ricerca ed export | Budget libero e per categoria |
![]() |
![]() |
| Proiezione del saldo per conto | Ricorrenti con rate e prossime scadenze |
![]() |
![]() |
| Conti con totali per tipo | Tema scuro |
I dati negli screenshot sono inventati.
Installazione
Servono Docker e Docker Compose. FinaFlow usa tre container: MongoDB, backend Node.js e frontend (nginx).
Con le immagini già pronte
mkdir finaflow && cd finaflow
# scarica docker-compose.registry.yml e .env.example dal repository, poi:
cp .env.example .env
nano .env # FINAFLOW_IMAGE, APP_URL, SESSION_SECRET (openssl rand -hex 32)...
docker compose -f docker-compose.registry.yml pull
docker compose -f docker-compose.registry.yml up -d
FINAFLOW_IMAGE è il prefisso delle immagini (diventano <prefisso>-backend e <prefisso>-frontend).
FINAFLOW_TAG sceglie la versione: stable (ultimo rilascio, il default), latest (ultima build di sviluppo) o un numero di versione.
Compilando dai sorgenti
git clone <url-del-repository> finaflow && cd finaflow
cp .env.example .env
nano .env
docker compose up -d --build
Apri http://localhost:8080 (o la porta scelta con APP_PORT): il primo utente che si registra
diventa amministratore.
Su host dietro VPN o exit-node
npmpuò bloccarsi durante la build: per questo ildocker-compose.ymlusanetwork: hostnella build. Altrove puoi toglierlo.
Aggiornare
docker compose -f docker-compose.registry.yml pull && docker compose -f docker-compose.registry.yml up -d
# oppure, dai sorgenti:
git pull && docker compose up -d --build
All'avvio il backend allinea da solo gli indici del database.
Configurazione
Tutto si imposta nel file .env (vedi .env.example).
| Variabile | Default | Descrizione |
|---|---|---|
APP_URL |
— | URL pubblico dell'app (redirect SSO, link nelle email, passkey) |
APP_PORT |
8080 |
Porta pubblicata dal frontend |
SESSION_SECRET |
— | Secret per sessioni e dati cifrati: obbligatorio (senza, o con un valore di esempio, il backend non parte), genera con openssl rand -hex 32 |
TRUST_PROXY |
1 |
Proxy davanti al backend: 1 solo l'nginx del frontend, 2 se c'è anche un reverse proxy (Traefik, NPM, Caddy). Serve per i limiti ai tentativi di accesso per IP |
FINAFLOW_IMAGE / FINAFLOW_TAG |
— / stable |
Immagini da usare con docker-compose.registry.yml |
REGISTRATION |
invite |
Chi può creare un account da solo: invite nessuno (solo su invito) · sso solo con l'SSO · open anche con email e password (accesso dopo la conferma dell'email) |
SSO_DISABLED |
false |
true = solo accesso con email e password |
ISSUER_BASE_URL |
— | Issuer OIDC (es. Authentik, termina con /; Google: https://accounts.google.com) |
SSO_NAME |
dal provider | Nome sul pulsante «Accedi con …» (default Google, Authentik o SSO) |
CLIENT_ID / CLIENT_SECRET |
— | Credenziali del provider OIDC |
SCOPE |
openid profile email |
Scope OIDC |
AUTH_REQUIRED |
true |
Lasciare true in produzione |
SMTP_HOST / SMTP_PORT / SMTP_SECURE / SMTP_USER / SMTP_PASS / MAIL_FROM |
— | Email di verifica, inviti e recupero password. Senza SMTP i link finiscono nei log del backend |
APP_ORIGINS |
https://localhost,capacitor://localhost |
Origini dell'app Android ammesse a chiamare le API (CORS): lascia il default |
DATA_DIR |
/data |
Cartella dei file generati dal backend (assetlinks.json per passkey e link dell'app): montaci un volume |
ANDROID_CERT_SHA256 |
— | Impronte SHA-256 di altri certificati di firma dell'APK (separate da virgola), oltre a quella ufficiale |
TEST_ENDPOINTS |
false |
Solo per gli screenshot automatici dei test: mai in produzione |
DEMO_MODE |
false |
true = istanza dimostrativa, vedi Demo |
TZ |
Europe/Rome |
Fuso orario per «oggi», fine mese, scadenze ed export |
CURRENCY |
EUR |
Valuta per i nuovi utenti |
LOW_BALANCE_THRESHOLD |
0 |
Soglia di avviso del saldo |
FORECAST_MONTHS |
12 |
Mesi di proiezione di default |
Email (SMTP)
Si configura nel .env oppure dal pannello Amministrazione → Email (SMTP). Le impostazioni
salvate dall'amministratore (password cifrata nel database) hanno sempre la precedenza sul .env.
Se le email (conferma, inviti, recupero password) non arrivano, prova l'invio dal server: mostra
la configurazione in uso e l'errore esatto. Ogni invio riuscito o fallito è anche nei log del backend
(righe [mail] e [recupero]).
docker exec -it finaflow-backend node src/cli.js test-mail tuo@indirizzo.it
docker logs finaflow-backend 2>&1 | grep -E '\[mail\]|\[recupero\]'
Il recupero password vale per gli account con email e password: chi accede solo con SSO riceve un'email che lo invita ad accedere con l'SSO.
SSO con Authentik, Google o un altro provider OIDC
- In Authentik: Applications → Providers → Create, tipo OAuth2/OpenID Connect, confidential.
- Redirect URIs:
<APP_URL>/callbacke<APP_URL>/login. Post-logout redirect:<APP_URL>. - Crea un'Application collegata al provider.
- Nel
.env:ISSUER_BASE_URL=https://authentik.tuodominio.it/application/o/<slug>/,CLIENT_ID,CLIENT_SECRET,SSO_DISABLED=false.
Dietro un reverse proxy HTTPS imposta APP_URL=https://...: i cookie sicuri si attivano da soli.
Google: in Google Cloud Console → API e servizi → Credenziali crea un ID client OAuth di tipo
Applicazione web con URI di reindirizzamento <APP_URL>/callback, poi nel .env
ISSUER_BASE_URL=https://accounts.google.com, CLIENT_ID, CLIENT_SECRET. Il pulsante diventa
«Accedi con Google»; all'uscita si esce da FinaFlow ma non dall'account Google.
Con l'SSO vince l'email: se esiste già un account con la stessa email (anche creato con la password)
si entra in quello, che da allora è collegato all'SSO. Altrimenti viene creato un nuovo utente, se ha
un invito per quell'email oppure REGISTRATION=sso/open. Ci si fida dell'email del provider: usa
solo un provider in cui gli utenti non possono scegliersi un'email altrui. Con Google lascia
REGISTRATION=invite se non vuoi che entri chiunque abbia un account Google. Per avere più provider
insieme (es. Google e account locali Authentik) collega Google come source in Authentik.
Amministratore senza password
Reimposta la password dal server, disattiva la verifica in due passaggi e chiude le sessioni aperte:
docker exec -it finaflow-backend node src/cli.js list-admins
docker exec -it finaflow-backend node src/cli.js reset-password admin@tuodominio.it # genera una password
docker exec -it finaflow-backend node src/cli.js reset-password admin@tuodominio.it NuovaPass123 # oppure la scegli tu
Backup
Ogni utente può scaricare e reimportare tutti i propri dati da Profilo e sicurezza → I tuoi dati
(file JSON). Per un backup dell'intera istanza salva il volume mongo-data, per esempio:
docker exec finaflow-mongo mongodump --archive --db finaflow > finaflow-$(date +%F).archive
Server MCP per l'assistente AI
FinaFlow espone un server MCP su /api/mcp (Streamable HTTP, senza stato).
Ogni utente crea i propri token da Profilo e sicurezza → Assistente AI (MCP) (serve la password o il codice 2FA);
il token si vede una sola volta e nel database ne resta solo l'hash.
| Permessi | Strumenti |
|---|---|
| Sola lettura | list_accounts, list_categories, search_transactions, totals_by_category, get_budget, get_forecast, list_recurring |
| Lettura + movimenti | in più add_transaction (uscite, entrate, trasferimenti), update_transaction e delete_transaction solo sui movimenti creati dall'assistente |
ChatGPT, claude.ai e le altre app con OAuth non hanno bisogno di token: aggiungi un connettore
personalizzato con l'indirizzo https://finaflow.example.com/api/mcp, accedi a FinaFlow (con qualsiasi metodo)
e scegli i permessi nella pagina di consenso. Il server implementa OAuth 2.1 come chiede MCP: metadati
(/.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server), registrazione dinamica
dei client, PKCE S256 obbligatorio, token di accesso di un'ora con refresh token che cambia a ogni rinnovo.
Il collegamento compare tra i token in Profilo e sicurezza e si revoca da lì. Il server deve essere raggiungibile
in HTTPS da internet (ChatGPT e claude.ai si collegano dai loro server).
Per i client con intestazioni personalizzate, dopo la creazione del token l'app mostra la configurazione già compilata (da copiare o scaricare) per:
- Claude Code:
claude mcp add --scope user --transport http finaflow https://finaflow.example.com/api/mcp --header "Authorization: Bearer ffk_..." - Claude Desktop:
claude_desktop_config.jsonconnpx mcp-remote(serve Node.js):{ "mcpServers": { "finaflow": { "command": "npx", "args": ["-y", "mcp-remote", "https://finaflow.example.com/api/mcp", "--header", "Authorization:${FINAFLOW_AUTH}"], "env": { "FINAFLOW_AUTH": "Bearer ffk_..." } } } } - OpenCode:
~/.config/opencode/opencode.json(oopencode.jsonnel progetto):{ "$schema": "https://opencode.ai/config.json", "mcp": { "finaflow": { "type": "remote", "url": "https://finaflow.example.com/api/mcp", "enabled": true, "headers": { "Authorization": "Bearer ffk_..." } } } }
L'endpoint accetta solo token (mai i cookie del browser). Token e collegamenti OAuth smettono di valere quando si cambia la password, quando si usa «Esci dagli altri dispositivi» o quando l'amministratore disattiva l'utente; si revocano uno per uno dalla stessa pagina.
Demo
Con DEMO_MODE=true l'istanza diventa una demo pubblica:
- all'avvio e ogni notte a mezzanotte cancella tutti i dati e ricrea un utente con conti, movimenti, ricorrenti, obiettivi e prestiti fittizi, con date sempre aggiornate;
- si entra con
demo@demo.it/demo1234(la pagina di accesso lo mostra, con il pulsante «Entra nella demo»); - registrazione, recupero password, cambio password, 2FA, passkey, eliminazione dell'account, amministrazione e import del backup sono disattivati; l'SSO è spento.
Usala solo su un'istanza dedicata, con un suo database: non attivarla dove ci sono dati veri.
App Android e repository F-Droid
L'app Android (Capacitor, cartella frontend/android) contiene l'interfaccia e si collega al tuo
server: al primo avvio chiede l'indirizzo dell'istanza. Funzionano accesso con email e password,
verifica in due passaggi, SSO, passkey e tutte le pagine.
Export CSV, backup e codici di recupero si salvano tramite la condivisione di Android.
- SSO: il login si apre nel browser di sistema e torna all'app da solo (link
it.devmin.finaflow://ssocon un codice monouso, protetto come PKCE). In Authentik non va cambiato nulla. - Passkey (Android 14+): native, con Credential Manager; quelle create dal browser valgono anche
nell'app e viceversa. Il dominio deve pubblicare
/.well-known/assetlinks.jsoncon l'impronta dell'APK: lo genera il backend a ogni avvio inDATA_DIR(volumebackend-datanei compose), già con l'impronta dell'APK ufficiale. Se firmi un tuo APK aggiungi la sua impronta inANDROID_CERT_SHA256(o direttamente nel file nel volume: le aggiunte restano). Il server deve essere raggiungibile in HTTPS da internet, perché Google verifica il file.
Il workflow .forgejo/workflows/android.yml parte con un tag
v1.2.3 (o a mano): compila l'APK firmato e lo allega alla release v1.2.3 di Forgejo, da cui si
scarica e si installa.
Facoltativo: impostando la variabile FDROID_REPO_URL crea anche un repository F-Droid firmato
e lo pubblica come immagine finaflow-fdroid (nginx, da mettere dietro il tuo reverse proxy, es.
https://fdroid.tuodominio.it): la sua pagina spiega come aggiungerlo a F-Droid (pulsante,
indirizzo con impronta e QR code) e da lì gli aggiornamenti arrivano in automatico.
Preparazione (una volta sola) — crea il keystore e conservalo con cura: con un'altra chiave gli aggiornamenti non si installano più.
keytool -genkeypair -keystore finaflow.jks -alias finaflow -keyalg RSA -keysize 4096 -validity 10000 -dname "CN=FinaFlow"
base64 -w0 finaflow.jks # -> secret ANDROID_KEYSTORE_BASE64
| Tipo | Nome | Valore |
|---|---|---|
| Variabile | FDROID_REPO_URL |
facoltativa: indirizzo pubblico del repository F-Droid, es. https://fdroid.tuodominio.it/repo |
| Secret | ANDROID_KEYSTORE_BASE64 / ANDROID_KEYSTORE_PASSWORD / ANDROID_KEY_ALIAS |
keystore in base64, password, alias |
| Secret | ANDROID_KEY_PASSWORD |
password della chiave, se diversa (facoltativo) |
| Secret | RELEASE_TOKEN |
token Forgejo per le release (facoltativo: di default usa il token automatico del workflow) |
| Secret | DEPLOY_WEBHOOK_FDROID |
webhook per aggiornare il container del repository (facoltativo) |
Compilare l'APK in locale (serve Docker):
docker run --rm -v "$PWD":/src -w /src node:24-trixie bash scripts/build-apk.sh 1.2.3 # -> dist-apk/
Build automatica con Forgejo Actions
Il workflow .forgejo/workflows/build.yml compila le
immagini finaflow-backend, finaflow-frontend e finaflow-site (il sito vetrina, pubblicato
solo nel registry), le pubblica e chiama un webhook per aggiornare il server (Portainer, Komodo,
Watchtower o uno script tuo).
| Evento | Tag delle immagini | Webhook |
|---|---|---|
push su dev |
latest |
DEPLOY_WEBHOOK_DEV |
tag v1.2.3 (es. creato su main) |
1.2.3, stable, latest |
DEPLOY_WEBHOOK_STABLE |
push su main |
nessuna build: si rilascia creando il tag | — |
Variabili (facoltative): REGISTRY (default: l'host di Forgejo), IMAGE_OWNER, IMAGE_NAME (default finaflow), PLATFORMS (default linux/amd64).
Secret: REGISTRY_USERNAME, REGISTRY_TOKEN (token con permesso write:package) e, facoltativi, DEPLOY_WEBHOOK_STABLE (rilasci), DEPLOY_WEBHOOK_DEV, DEPLOY_WEBHOOK_TOKEN (inviato come Authorization: Bearer).
Il runner deve avere l'etichetta docker e accesso a Docker (container.docker_host: "automount" nel config.yml di forgejo-runner).
Sviluppo
docker run -d -p 27017:27017 --name mongo-dev mongo:9
cd backend && npm install && npm run dev # http://localhost:3000
cd frontend && npm install && npm start # http://localhost:4200 (proxy verso :3000)
Test
cd backend
npm test # test unitari
MONGO_URI_TEST=mongodb://localhost:27017/finaflow_test npm test # + test delle API (il database viene svuotato)
I test unitari coprono ricorrenze e rate, budget libero (spendendolo tutto il conto non va mai sotto zero), interessi, password, 2FA e cifratura. Quelli di integrazione provano le API su un database vero: accesso e inviti, conti e trasferimenti, ricerca e filtri, finanziamenti, prestiti, interessi, isolamento tra utenti, cambio password, backup, eliminazione dell'account e demo.
Sito vetrina
La cartella site/ è una pagina statica con il suo Dockerfile (nginx): la pipeline la pubblica come
immagine finaflow-site, non fa parte dei docker compose dell'app.
docker run -d -p 8081:80 <registry>/<utente>/finaflow-site:latest
Stack: Angular 22 (componenti standalone, signals, build con esbuild) e Tailwind CSS · Node.js 24 con Express e Mongoose · MongoDB 9 · Chart.js.
finaflow/
├── docker-compose.yml # build dai sorgenti
├── docker-compose.registry.yml # immagini dal registry
├── .forgejo/workflows/build.yml # CI: build, push, webhook
├── backend/
│ ├── tests/ # unit/ e integration/ (node --test)
│ └── src/
│ ├── app.js # app Express: sessioni, SSO, API
│ ├── server.js # avvio: database, demo, interessi
│ ├── cli.js # reset password admin
│ ├── models/ # utenti, conti, movimenti, ricorrenti, budget...
│ ├── routes/ # API dati, auth, sicurezza, admin
│ └── services/ # saldi, ricorrenze, budget e proiezioni, interessi, backup, email, 2FA
├── frontend/src/app/
│ ├── pages/ # panoramica, movimenti, ricorrenti, budget, proiezioni, prestiti...
│ └── core/ # servizi, guardie, componenti condivisi
├── docs/screenshots/
└── site/ # sito vetrina statico (immagine finaflow-site)






