Nell'ultimo post avevo chiuso con una promessa: il prossimo sarebbe andato sotto il cofano — database locale, timestamp, e come tutto questo reggerà quando i dispositivi diventeranno più di uno. Eccoci.
Niente di tutto questo si vede usando l'app. È proprio il punto: deve sparire. Ma è la parte che mi tiene sveglio, quindi vale la pena raccontarla.
Un database che vive sul telefono
I grain non stanno da nessuna parte, tranne che sul tuo telefono. Sotto c'è SQLite — lo stesso database che gira dentro miliardi di app — e sopra WatermelonDB, che lo rende reattivo.
"Reattivo" qui ha un significato preciso: l'interfaccia non chiede mai "ci sono dati nuovi?". Si iscrive a una query e viene aggiornata da sola quando qualcosa cambia. Scrivi un grain, e la lista lo mostra non perché qualcuno l'ha detto alla schermata, ma perché la schermata stava già guardando. È un dettaglio tecnico che diventa una sensazione: l'app risponde senza scatti, senza ricariche.
Local-first non è uno slogan, è l'architettura. Nessun server riceve i tuoi contenuti, quindi non c'è una richiesta di rete tra te e il tuo pensiero. Offline e online sono la stessa cosa, perché per grain "online" non è mai stato un requisito.
Il tempo è il dato
La maggior parte delle app per note tratta la data come un'etichetta: utile, ma secondaria. In grain è il contrario. Ogni grain nasce con il suo timestamp automatico, e da lì si dirama tutto.
C'è l'ora esatta, quella che vedi accanto a ogni riga. Ma c'è anche un livello sotto: ogni grain viene assegnato a un giorno e a una fascia — mattina, pomeriggio, sera. Non sono decorazioni: sono il modo in cui la timeline raggruppa la giornata senza chiederti di archiviare niente. Tu scrivi e basta; il tempo fa l'ordine.
Questo ribalta una piccola cosa nel modo di prendere appunti. Non decidi dove va un pensiero. Va dove l'hai pensato, nell'istante in cui l'hai pensato. La struttura emerge dopo, da sola.
I tag non sono una tabella
Quando scrivi #lavoro, non succede niente di speciale al salvataggio. Il grain è solo il suo testo. I tag vengono estratti al volo con una semplice espressione regolare ogni volta che servono — per la ricerca, per la timeline, per i colori.
Niente tabella dei tag, niente relazioni da mantenere, niente da tenere sincronizzato con il testo. Se correggi un tag scrivendo, il tag è corretto: non esiste una seconda copia che può andare fuori sincrono. Lo stesso tag riceve sempre lo stesso colore, calcolato dal nome, così #lavoro è terracotta oggi come lo sarà tra un mese.
È la versione minuscola della stessa filosofia: meno stato da gestire, meno cose che possono rompersi.
Due telefoni, una verità
Qui arriva la parte difficile, quella per cui ho costruito le fondamenta prima ancora di costruirci sopra.
Immagina due dispositivi, entrambi offline, che modificano lo stesso grain. Quando tornano online, chi ha ragione? L'istinto dice "l'ultimo che ha scritto". Ma quando è l'ultimo? Gli orologi dei telefoni non sono mai perfettamente d'accordo — bastano pochi secondi di scarto e l'ordine si inverte.
La risposta non è l'orologio da parete, è l'ordine causale. grain usa un Hybrid Logical Clock (HLC): un contatore che combina il tempo reale con un piccolo numero logico, così due eventi sono sempre confrontabili anche se gli orologi mentono. A ogni dispositivo è legato un node_id che fa da spareggio quando tutto il resto è pari. La regola di fusione è last-write-wins, ma deterministica: dati gli stessi eventi, ogni dispositivo arriva alla stessa conclusione, sempre.
La cosa interessante è che questo è già nel codice, anche se la sincronizzazione è ancora spenta. Ogni grain che scrivi oggi nasce già con il suo HLC e il suo node_id, pronto per un futuro in cui esisterà un secondo telefono. E quando quel futuro arriverà, i contenuti verranno cifrati sul dispositivo prima di partire — end-to-end, by design — così "sincronizzato" non vorrà mai dire "leggibile da noi".
Le fondamenta, non ancora il tetto
Voglio essere onesto: grain+, la sincronizzazione, non è attiva. Quello che ho descritto sono le basi, posate apposta in anticipo.
L'ho fatto in quest'ordine per una ragione precisa. Una sync costruita male è peggio di nessuna sync — può perdere i tuoi dati con la faccia di chi ti sta aiutando. Volevo che il giorno, il flusso, la timeline fossero solidi, e che il modello del tempo fosse a prova di orologi bugiardi, prima di accendere la rete. Adesso lo sono.
Il prossimo telefono può aspettare. Il tuo primo, intanto, ha già tutto quello che gli serve.
ogni momento, al suo posto — e ogni momento con il suo posto nel tempo, anche quando i telefoni saranno due.