Skip to main content
Questa pagina è per chi modifica o deploya il tool: team Mattone, chi fa manutenzione, qualunque agente. Uno studente non ha bisogno di leggerla.

Le tre fonti di verità

Le credenziali (database, JWT, segreto HMAC con la platform) stanno nel .env del server e nel Vault. Mai nel repo, mai in questa documentazione.

L’architettura non si sposta

Il tool è diviso in tre pezzi che vivono in posti diversi: backend e frontend sul droplet DigitalOcean, database su Supabase, più un progetto Vercel che builda il frontend ma non serve il sito pubblico. Questa divisione fa venire la tentazione di «rifare il backend da un’altra parte» (su Vercel, su Supabase Edge, su un nuovo server) o di attaccare un secondo backend allo stesso database. Non si fa. Gli studenti e Cosmica usano il tool ogni mese: un secondo backend, o un database parallelo, li rompe in silenzio (dati scritti in due posti, tenant che divergono, riconciliazioni che spariscono). Finché una decisione esplicita di Mattone non dice altro:
  • il backend è uno, gira sul droplet, si aggiorna solo da main di questo repo;
  • il database è uno, quello canonico su Supabase, e nessun altro servizio ci scrive sopra;
  • il frontend pubblico è quello servito dal droplet: il build Vercel serve a verificare che compili, non a servire gli studenti;
  • chi vuole cambiare questa architettura apre una discussione prima, con piano di migrazione e rollback, non un branch.
Se trovi un backend, un cron o uno script che scrive nel database del tool da un posto diverso dal droplet, è un errore da segnalare subito, non una feature.

Perché esistono queste regole

Fino ad agosto 2026 la produzione non corrispondeva a main: c’era codice che viveva solo sul server, e un git reset --hard avrebbe cancellato roba viva. Sullo stesso server girava anche un servizio che col tool non c’entrava niente. Il tentativo di aprile di spostare il database su Supabase era fallito perché le tabelle avevano la protezione per riga accesa e l’utente dell’app non le vedeva: il tool sembrava vuoto ed è stato riportato indietro. Ogni regola qui sotto chiude uno di questi buchi. Il resoconto degli incidenti sta nel repo interno.

I guardrail nel codice

  • db-guard.ts: all’avvio confronta l’host del database con quello canonico. Se è diverso, o è un host DigitalOcean, il processo esce. Non ci sono eccezioni self-service: se un caso lo richiedesse davvero, si concorda prima con il team e la procedura è nel repo interno.
  • scripts/preflight-deploy.sh: prima del deploy verifica albero git pulito, HEAD presente su origin, nessun certificato DigitalOcean, tsc --noEmit a zero errori. Se fallisce, non si deploya.
  • /api/health: risponde con db.canonical, un’impronta dell’host e schemaGuard. Se nella risposta manca la chiave db, in produzione gira codice vecchio.
  • assertTenantId: ogni query dei repository lancia un errore se il tenant manca. Motivo: TypeORM ignora tenantId: undefined e restituirebbe i dati di tutti i tenant.
  • Script da riga di comando: richiedono --tenant=<id> o TENANT_ID, nessun default. Un audit esiste: node backend/scripts/audit-tenant-scope.js.
  • Migration all’avvio spente: le migration non partono all’avvio; si applicano a mano via SQL dopo averle lette.
Non usare i log come prova che lo SchemaGuard è andato bene. La riga SchemaGuard: all tables and columns OK esce solo quando non ha corretto nulla: se ha applicato anche una sola modifica scrive SchemaGuard applied N fix(es), e chi cerca la prima riga conclude che qualcosa è rotto quando invece ha funzionato. Entrambi i casi sono normali dopo un deploy — al redeploy del 5 settembre 2026 la riga è uscita, allo switch del database no. La verifica che vale è schemaGuard: "ok" in /api/health.

Come si fa una modifica

1

Branch e PR sul repo

Lavori in locale su un branch, apri una pull request verso main. Niente commit diretti su main, niente modifiche sul server.
2

Typecheck e test in locale

npm run typecheck nel backend deve dare zero errori. Il preflight lo ricontrolla.
3

Merge

La PR si mergia su main. Il frontend viene buildato anche da Vercel (utile per vedere che compila), ma il sito pubblico è servito dal server.
4

Deploy sul server

Sul droplet, nella cartella del progetto: git fetch, git checkout main, git pull --ff-only, poi ./scripts/preflight-deploy.sh. Solo se è verde: docker-compose -f docker-compose.prod.yml build backend && docker-compose -f docker-compose.prod.yml up -d backend. Per il frontend lo stesso con frontend.
5

Verifica

La prova buona è curl -s https://reconciliation.mattone.co/api/health: deve rispondere db.canonical: true e schemaGuard: "ok". Infine login dalla platform e un conteggio noto (es. i movimenti di un mese del tenant Cosmica).

Da non fare mai

  1. Mai modificare file sul server e lasciarli lì. Se una cosa funziona solo sul droplet, non esiste.
  2. Mai git reset --hard sul server: prima si controlla git status, se c’è roba non committata si porta su GitHub.
  3. Mai far partire il backend su un database diverso da quello canonico. Se un caso lo richiede, si concorda prima con il team e si scrive nella PR.
  4. Mai DB_MIGRATIONS_RUN=true. Le migration si leggono e si applicano a mano.
  5. Mai una query o uno script senza tenant.
  6. Mai un database di sviluppo sul server di produzione, e mai una porta database esposta su internet.
  7. Il deploy si fa solo a mano, con la procedura qui sopra. La vecchia GitHub Action di deploy è ferma: prima di anche solo prendere in considerazione di riaccenderla va riscritta e rivista.
  8. Mai fidarsi della documentazione sullo stato del server: la verità è curl /api/health e docker exec ... printenv DB_HOST.

Cose note da sistemare

  • L’elenco puntuale dei residui da ripulire nel codice sta nel repo interno, insieme a chi li ha in carico.
  • Alcuni documenti in docs/ del repo descrivono endpoint o regole di abbinamento diversi dal codice. Fa fede il codice; questa pagina è allineata al 6 settembre 2026.
  • Nella configurazione di avvio le migration automatiche sono spente, ma alcune note nel repo dicono ancora il contrario.
Stato al 6 settembre 2026 — passaggio completato e consolidato. Il server gira main sul database canonico su Supabase. Verificato allo switch: preflight verde e conteggi delle tabelle principali uguali a quelli del database precedente. Il giorno dopo sono state riattivate le regole di accesso per riga su tutte le tabelle — con verifica che l’applicazione continuasse a funzionare — e corrette le date invertite rimaste dall’importazione, confrontate una per una con il backup.Il database precedente non si legge e non si scrive: è fuori dal giro, e la sua dismissione è una procedura programmata del team. Sia il piano di rientro sia quello di dismissione stanno nel repo interno con le loro verifiche — non sono operazioni self-service.Da qui in poi vale la regola in cima: main è la produzione, e la verità sullo stato del server è curl /api/health, non questa pagina.