In coda a ogni post compaiono l'ultima modifica e un hash del contenuto (una stringa che inizia con sha256:). Servono a uno scopo preciso: permettere a chi cita un post di accorgersi se nel frattempo il contenuto è cambiato e, se è cambiato, di averne evidenza.

Il fine non è rivendicare una paternità («l'ha scritto questo sito, per primo»), ma due cose più modeste e più verificabili: poter controllare in autonomia se un contenuto è cambiato e in quale finestra di tempo esisteva, senza dover dare fiducia a nessuno. È lo stesso spirito del resto del sito, il contenuto prima della tecnologia.

Conviene dirlo subito, perché è il punto su cui si regge tutto: questo meccanismo non abolisce la fiducia, la sposta. Si passa dal «credere a tutto ciò che dice il sito» al «doversi fidare del sito solo per ciò che è irriducibilmente parola sua»; tutto ciò che è meccanico (questo testo produce questo hash, questo testo esisteva prima di un certo blocco) lo controlla chiunque, con strumenti universali.

Un piccolo disclaimer: non si ha la pretesa che il meccanismo sia il migliore possibile, il più robusto o il più efficiente. È piuttosto un'occasione per un piccolo esperimento su un caso d'uso possibile, una sorta di proof of concept costruita con concetti e strumenti facilmente accessibili: uno stimolo a usare possibilità che sono a disposizione di tutti, senza reinventare la ruota. La si può leggere anche come un piccolo esempio di uso della time chain di Bitcoin per uno scopo non legato al trasferimento di valore.

La verifica in un minuto

Per chi vuole solo controllare, tutto si riduce a tre comandi su file che stanno in coda a ogni post (le voci «testo verificabile», «firma» e «marca temporale»):

sha256sum canonical.txt                                  # l'impronta coincide?
minisign -Vm canonical.txt -P <chiave pubblica del sito> # la firma è del sito?
ots verify -f canonical.txt <hashbreve>.ots              # esisteva prima di quel blocco?

Il quarto controllo non ha bisogno di comandi: la riga btc-anchor dentro il testo verificabile si confronta con un qualunque block explorer (è il limite «non prima»). Che cosa provino questi controlli, dove stiano la chiave e i file, e quali siano i limiti onesti del meccanismo è spiegato nel resto della pagina.

Cosa accompagna ogni post

Oltre alla data e all'hash, in coda al post ci sono alcune voci:

  • verifica — questa pagina;
  • testo verificabile — un file canonical.txt con i byte esatti che producono l'hash;
  • dettagli (json) — la scheda di verifica (verify.json), l'elenco di tutte le versioni note di quel post;
  • marca temporale — il file .ots che colloca la versione nel tempo (vedi «la marca temporale»);
  • firma — un file .minisig con la firma del sito sul testo verificabile (vedi «la firma e l'identità»).

A cosa serve, in concreto

Vanno tenuti distinti tre scopi, perché reggono promesse diverse, e ciascuno ha un suo confine onesto.

1. Accorgersi che è cambiato. Per questo basta l'hash: annotandolo per intero (o salvando il testo verificabile) al momento della citazione, un controllo successivo è immediato. In pagina ne compare la forma corta, che è un'etichetta da leggere, non quella da annotare (vedi «come è fatto l'hash»). Se l'hash è diverso, il contenuto è cambiato. Va detto con franchezza che, per sé stesso, un lettore attento potrebbe arrivarci da solo: basterebbe salvare il testo a ogni visita e riconfrontarlo. Qui il sito non aggiunge il «rilevare il cambiamento», aggiunge tre cose più piccole: definisce qual è il contenuto (questa esatta sequenza di byte, non l'HTML né l'impaginazione, così un cambio di template non si confonde con un cambio di sostanza); rende il confronto riproducibile con uno strumento universale, non con il codice del sito; e riduce la diligenza a copiare una stringa breve, abbastanza leggera da stare in una nota di una citazione.

2. Poterlo dimostrare a terzi. Una cosa è convincersene, un'altra è provarlo a qualcun altro. Per questo serve avere in mano la preimmagine, il testo esatto che produce quell'hash: è ciò che fornisce il file canonical.txt. Chi lo conserva al momento della citazione può in seguito mostrare «ecco il testo, se ne ricalcoli l'hash, e si vede che oggi il sito ne dichiara un altro: è cambiato». La prova decisiva, però, è quella che il lettore si è conservato: il sito la rende facile e standard, non la sostituisce. E un limite va detto chiaro: il sito non può provare, da solo, di aver servito proprio quel testo a quell'indirizzo in passato (vedi più sotto, «cosa non si prova»). Resta vero un argomento più modesto a favore della fiducia: un hash che non corrisponde al contenuto mostrato è smascherabile all'istante da chiunque, quindi pubblicarne uno falso non conviene: non è una prova di buona fede, è un incentivo allineato. C'è infine un gesto che il lettore può aggiungere da sé, al momento della citazione: chiedere a un archivio pubblico (per esempio la Wayback Machine) di catturare la pagina. Copre proprio ciò che questo meccanismo non può provare, che quel testo stava a quell'indirizzo in quella data, al prezzo di fidarsi dell'archivio: una terza parte che il sito, per scelta, non mette nei propri conti, ma che il lettore è libero di chiamare a testimone.

3. Collocarlo nel tempo. Questo è l'unico scopo che il lettore non può procurarsi da solo, ed è anche quello che non chiede di fidarsi del sito: la catena di Bitcoin fa da testimone del tempo. Si ottiene una finestra provata (il contenuto, in quella forma esatta, esisteva dopo un certo blocco e prima di un altro), verificabile da chiunque, contro un registro che nessuno controlla (il meccanismo è nella marca temporale, più sotto). È la parte più solida, e anche quella dalla rivendicazione più stretta: prova l'esistenza di un testo in una finestra, non chi l'ha scritto.

Come è fatto l'hash

Un hash è un'impronta. Una funzione (qui SHA-256) trasforma un testo in una stringa di lunghezza fissa: cambiare anche una sola virgola produce un'impronta del tutto diversa, e due copie identiche danno sempre la stessa impronta. Non si torna dall'impronta al testo, ma il confronto è immediato.

In pagina l'hash è in forma corta (le prime 12 cifre): comoda da leggere e da confrontare a occhio, ma è solo un'etichetta. Per annotare serve l'hash intero, che sta nel testo verificabile e nella scheda di verifica: un prefisso così corto, con abbastanza calcolo, si potrebbe far coincidere anche tra testi diversi; sull'hash completo questo è fuori portata.

Come controllare l'hash

Il file canonical.txt contiene esattamente i byte su cui è calcolato l'hash. Non serve ricostruire niente: basta scaricarlo e calcolarne lo SHA-256 con un qualunque strumento standard.

sha256sum canonical.txt

Il valore ottenuto coincide con il campo current della scheda di verifica (e, nella parte mostrata, con la forma corta in pagina). La verifica si regge su uno strumento universale, non su codice di questo sito.

Come è costruito il testo verificabile

Il file canonico è una serializzazione del Markdown grezzo del post (non dell'HTML), in UTF-8, in questa forma:

liberlume-content-v3
lang: <lingua>
title: <titolo>
summary: <sommario>
btc-anchor: <altezza>,<hash del blocco>
prev: <hash della versione precedente | genesis>
--- body ---
<corpo del post in Markdown grezzo>

Regole di normalizzazione:

  • codifica UTF-8, normalizzazione Unicode NFC;
  • fine riga ricondotti a \n;
  • title e summary sono i valori del front-matter, ripuliti dagli spazi ai bordi;
  • btc-anchor è l'ancoraggio temporale: l'altezza e l'hash di un blocco Bitcoin recente, separati da una virgola. È la parte che fissa il limite inferiore nel tempo, spiegata più sotto nella marca temporale;
  • prev è l'aggancio alla versione precedente: l'hash della versione che precede questa, dello stesso post; la prima versione porta la sentinella genesis. Lega le versioni in una catena, spiegata più sotto;
  • il corpo è il Markdown grezzo (gli spazi a fine riga restano, in Markdown sono significativi); le righe vuote in testa e in coda si rimuovono e il testo chiude con esattamente un \n;
  • le righe d'intestazione sono letterali (lang:, title:, summary:, btc-anchor:, prev: con un solo spazio dopo i due punti) e il separatore è letterale --- body ---.

Il prefisso liberlume-content-v3 non è un segreto: è un'etichetta pubblica che versiona il metodo (la -v1 era senza ancoraggio temporale, la -v2 senza la catena delle versioni) e separa il dominio (questo hash significa «contenuto canonico di questo sito», e nient'altro). Documentare la forma serve a non lasciare scatole nere: chi vuole può ricalcolare da sé, ma per il controllo ordinario basta il file e un comando.

La scheda di verifica

Accanto a ogni post, allo stesso indirizzo, c'è il file verify.json. Per il post all'indirizzo /it/esempio/ sta in /it/esempio/verify.json. Contiene, senza ripubblicare il testo, l'elenco di tutte le versioni note di quel post.

Per ogni versione il file tiene distinte due cose, ed è la distinzione che conta:

  • declared_date è la data dichiarata dall'autore (la prima pubblicazione, o la versione): è un'informazione, non una prova, e va creduta sulla parola;
  • window è la finestra provata dalla catena: certainly_after (dal blocco di ancoraggio: il contenuto è certamente dopo quell'istante) e, a marca confermata, certainly_before (dal blocco della marca: certamente prima), con le altezze dei blocchi per ricontrollare. Questa non chiede fiducia a nessuno;
  • editorial è l'impronta del solo contenuto: la canonica senza le righe btc-anchor e prev (si ricalcola dal testo verificabile togliendo quelle due righe). Serve a distinguere una modifica vera da un ri-sigillo: due versioni con la stessa editorial portano lo stesso testo, è cambiato solo il sigillo.

Una legenda nel file lo ricorda. Così, anche se la pagina mostra solo l'hash corrente, ogni versione si ritrova nell'elenco con la sua data dichiarata e la sua finestra.

Le versioni, inoltre, sono concatenate: ogni versione, nel suo testo verificabile, porta l'hash di quella precedente (la riga prev). Così l'ordine registrato non è alterabile a posteriori senza che il conto torni più. È un dettaglio di robustezza, non una pretesa: come si dice più sotto, non prova che l'elenco sia completo.

I checkpoint del corpus

Le schede di verifica le dichiara il sito, e nulla impedirebbe, in linea di principio, di mostrarne versioni diverse a lettori diversi. Per restringere anche questo margine, a ogni cambiamento dello stato del corpus, e comunque periodicamente, viene pubblicato un checkpoint: un piccolo file con l'elenco degli hash correnti di tutti i contenuti, ancorato a un blocco recente, firmato e marcato come i post, e agganciato al checkpoint precedente (la sua riga prev). Ogni checkpoint inchioda l'intera contabilità dichiarata a una data provata: una storia alternativa dovrebbe fare i conti con i checkpoint già emessi e conservati da chi li ha scaricati. Stanno tutti in /checkpoints/: l'indice, e accanto i file, le firme e le marche. Non provano la completezza, quella resta fuori portata; restringono la finestra in cui una contabilità alternativa può vivere senza essere smascherata.

La firma e l'identità

Ogni testo verificabile è firmato dal sito. Accanto al canonical.txt c'è un file canonical.txt.minisig: la firma, prodotta con minisign, di quei byte esatti. Avendo la chiave pubblica del sito, chiunque la controlla:

minisign -Vm canonical.txt -P <chiave pubblica del sito>

Cosa aggiunge, e cosa no. La firma non prova chi sia l'autore nel mondo reale, e non è ciò che importa qui: prova che una versione viene dallo stesso autore coerente (il titolare di una chiave stabile, sempre la stessa) e rende difficile l'impersonazione o la riscrittura della storia a chi quella chiave non la possiede. È, di nuovo, fiducia spostata: non «credetemi», ma «ecco la chiave, controllate».

Perché la firma valga, però, bisogna sapere che la chiave è davvero del sito. Per questo la chiave pubblica è pubblicata in modo stabile e in più punti, e legata al dominio per due vie indipendenti: un file servito via HTTPS (/.well-known/liberlume-identity, che vale per chi controlla il certificato del sito) e un record DNS del dominio (che vale per chi ne controlla la zona). Le due vie si possono confrontare, e un disaccordo tra loro è già un segnale. Non sono però del tutto indipendenti (chi controlla il DNS può procurarsi anche un certificato valido): alzano il costo di una sostituzione, non la rendono impossibile. La difesa più solida è un'altra, ed è la data: la dichiarazione di identità qui sotto colloca la chiave nel tempo, e una chiave sostituita non può esibire una storia così vecchia.

La chiave, a sua volta, è descritta in una dichiarazione di identità (sotto /identity/): un piccolo documento che la dichiara del sito, la colloca nel tempo con lo stesso meccanismo a finestra dei post (un ancoraggio a un blocco recente e una marca temporale) ed elenca i contenuti del sito al momento della sua nascita. Così anche la coppia di chiavi ha una data provata, non solo una parola.

Se un giorno la chiave dovesse cambiare, la dichiarazione nuova dovrà indicare la vecchia ed esserne controfirmata; e tra dichiarazioni in conflitto fa fede quella con la finestra provata più antica, perché una marca temporale vecchia non si può fabbricare a posteriori. È questa regola che rende rilevabile una sostituzione silenziosa della chiave.

Un caso resta scoperto, ed è giusto dirlo: il furto della chiave. Chi la rubasse potrebbe firmare una rotazione in apparenza legittima, controfirmata dalla chiave autentica: esisterebbero allora due successioni in conflitto, entrambe formalmente valide, e la crittografia da sola non direbbe quale appartiene all'autore. Il conflitto resterebbe almeno visibile, perché due dichiarazioni rivali sono un allarme e non una sostituzione silenziosa, ma non sarebbe risolvibile sul piano tecnico. Il rimedio noto è pre-impegnare per tempo l'impronta di una chiave di riserva custodita altrove: una miglioria possibile, qui per ora dichiarata e non attuata.

I gradi dell'evidenza

Conviene essere espliciti su quanto «pesa» ciascun elemento, dal più debole al più forte:

  • l'hash annotato da chi ha letto prova qualcosa a sé stesso, non a terzi;
  • il testo verificabile salvato è la preimmagine: è dimostrabile a chiunque, perché chiunque può ricalcolarne l'hash;
  • la scheda di verifica documenta le versioni e le date, ma la dichiara questo sito: presa da sola, richiede di dare fiducia al sito;
  • la firma lega ogni versione all'identità (la chiave) del sito: non prova chi sia nel mondo reale, ma autentica un autore coerente e rende difficile l'impersonazione;
  • la marca temporale (vedi sotto) lega ogni versione a una finestra di tempo non falsificabile nemmeno da questo sito. È il grado più forte di evidenza.

In sintesi: la prova più solida e indipendente è quella che il lettore si è conservato al momento della citazione; ciò che il sito pubblica serve a renderla controllabile e, con la marca temporale, non ripudiabile.

Cosa non si prova

Per non promettere più di quanto si mantiene, conviene elencare ciò che questo meccanismo non dimostra:

  • la paternità. Prova che un testo esisteva in una certa finestra, non chi l'ha scritto né chi è stato il primo: chiunque può calcolare un hash e marcare un testo, anche copiato.
  • che il sito abbia servito quel testo a quell'indirizzo in passato. L'hash pubblicato è un'affermazione al presente; la prova che una certa versione era online a una certa data sta nell'evidenza che il lettore ha conservato (o in un archivio terzo), non in ciò che il sito può ricostruire da sé.
  • la completezza dell'elenco delle versioni. La catena fa più di quanto sembri: poiché ogni versione aggancia l'hash della precedente, il sito non può riordinaretogliere in silenzio una versione già registrata e testimoniata. Ma due cose restano fuori portata. Primo: nulla obbliga il sito a registrare ogni modifica: può non congelare uno stato intermedio, e di quello non resta traccia. Secondo: dall'aggancio si ricava l'hash della versione precedente, non il suo testo, né la certezza che l'elenco dichiarato sia quello vero e intero; chi arriva per la prima volta conosce solo ciò che il sito dichiara adesso. La catena prova l'ordine e l'integrità delle versioni elencate, non che siano tutte. I checkpoint del corpus (vedi sopra) restringono questo margine nel tempo, non lo chiudono.
  • che la pagina mostri il testo canonico. L'hash e la firma coprono il testo verificabile, non l'HTML servito: che la pagina rispecchi fedelmente il canonico si può controllare con un confronto, ma nessun automatismo lo garantisce. Vale l'incentivo già detto: una divergenza sarebbe smascherabile all'istante da chiunque.
  • chi è l'autore nel mondo reale. La firma prova che una versione viene dal titolare della chiave del sito (un'identità coerente, non un nome anagrafico); e che quella chiave sia davvero del sito va creduto sulla parola, per quanto il legame col dominio (vedi «la firma e l'identità») lo renda più difficile da falsificare. Una parte di fiducia resta: il meccanismo la riduce al minimo e la rende controllabile dove possibile, non la azzera.

Avvertenze

  • l'hash dice se un contenuto è cambiato, non cosa è cambiato: per recuperare il testo esatto di una versione precedente bisogna averla salvata (la scheda di verifica, di proposito, non ripubblica le prose storiche).
  • in testa al post c'è la data di prima pubblicazione, dichiarata dall'autore; l'ultima modifica, in coda, è invece la finestra provata dalla catena (la marca temporale qui sotto), non una data dichiarata.
  • una marca temporale ancora pendente non è verificabile finché un blocco non la conferma; in quella finestra si conosce solo il limite inferiore («dopo il …») e l'evidenza indipendente resta quella che il lettore conserva.

La marca temporale

Un'impronta dice se un testo è cambiato, ma non quando esisteva. Per collocarlo nel tempo senza dover credere a questo sito servono due ancoraggi alla catena di Bitcoin, un registro indipendente e decentralizzato. Insieme stringono il contenuto dentro una finestra di tempo, da un lato e dall'altro.

Il limite inferiore, «non prima». Nel testo verificabile, la riga btc-anchor contiene l'altezza e l'hash di un blocco Bitcoin recente, scelto al momento del sigillo. L'hash di un blocco è imprevedibile finché il blocco non è stato estratto: nessuno può scriverlo in anticipo. Trovarlo dentro l'impronta dimostra quindi che quel contenuto è stato sigillato non prima di quel blocco, cioè non prima dell'istante in cui il blocco è entrato nella catena.

Il limite superiore, «non dopo». Ogni versione viene poi marcata con OpenTimestamps, che aggancia l'impronta del contenuto a un blocco successivo. La marca è il file .ots scaricabile dal blocco in coda al post (voce «marca temporale») e prova che quel contenuto esisteva già prima di quel blocco.

Messi insieme, i due ancoraggi dicono: il contenuto, in quella forma esatta, è nato dopo il blocco di ancoraggio e prima del blocco della marca. Una finestra chiusa, sostenuta non da una dichiarazione ma dalla catena.

Da dove viene l'ampiezza. Un blocco non è «fermo» nell'istante in cui è estratto: lo diventa, in pratica, quando sopra se ne sono accumulati altri, perché tornare indietro costerebbe rifare tutto quel lavoro. Quanti blocchi bastino è una soglia ragionevole, non un assoluto; e poiché i blocchi arrivano circa ogni dieci minuti, quella soglia è anche un intervallo di tempo. È da lì che viene l'ampiezza dei due lati della finestra: né l'ancoraggio né la marca si fissano sull'ultimo blocco, ma su uno già abbastanza sepolto da considerarsi immutabile. All'ampiezza concorre anche una tolleranza sugli orari: l'orario scritto in un blocco lo mette chi lo estrae, e il protocollo accetta scostamenti fino a un paio d'ore (chiede solo che superi la mediana degli undici blocchi precedenti e non corra più di due ore avanti rispetto all'orologio della rete). Gli estremi della finestra vanno quindi letti con una precisione di ore, non di minuti.

La marca OpenTimestamps nasce in due tempi:

  • pendente: subito dopo la pubblicazione l'impronta è presa in carico dai calendar server, ma non è ancora ancorata a un blocco;
  • confermata: dopo la conferma di un blocco Bitcoin (di norma qualche ora) la marca incorpora l'attestazione del blocco ed è verificabile in autonomia.

Come controllare la finestra

Il limite superiore si controlla con il canonical.txt e il file .ots dello stesso post:

ots verify -f canonical.txt <hashbreve>.ots

La verifica dell'attestazione del blocco richiede l'accesso alla blockchain di Bitcoin (un nodo proprio oppure un verificatore web).

Il limite inferiore si controlla a partire dalla riga btc-anchor del testo verificabile: l'altezza e l'hash del blocco sono dati pubblici. Cercando quell'altezza su un qualunque block explorer, o sul proprio nodo, si verifica che l'hash corrisponde e si legge l'orario in cui il blocco è stato estratto. Quell'orario è l'estremo inferiore della finestra. È un controllo che non passa da questo sito: il dato sta sulla catena di Bitcoin, uguale per tutti.

Una nota sugli strumenti: ots è il client OpenTimestamps (qui l'esempio è in Python, pip install opentimestamps-client, ma esistono implementazioni anche per altri ambienti); in alternativa il sito opentimestamps.org offre un verificatore da browser. Per il blocco va bene un qualunque block explorer pubblico oppure, per non dipendere da nessuno, il proprio nodo.

Tre limiti, dichiarati: la marca prova l'esistenza di un contenuto in una finestra di tempo, non la paternità; la finestra ha un'ampiezza (la tolleranza degli orari dei blocchi, il tempo tra il sigillo e la conferma, e il margine di blocchi che si attende prima di considerare fermo un blocco), non è un istante; e per i post sigillati dopo la loro prima pubblicazione il limite inferiore parte dal momento del sigillo, non dalla data originale del post.

Una nota sul punto di partenza

Il meccanismo descritto qui (firma, catena delle versioni, ancoraggio nella preimmagine) è entrato in vigore a un certo giorno, quando il sito era ancora giovane. In quel momento tutti i contenuti già pubblicati sono stati ri-congelati, concatenati e firmati insieme, ognuno come prima versione di una nuova catena. Per questo, per i contenuti anteriori a quel giorno, il limite inferiore provato dalla finestra è quel giorno, non la loro data originale; la data di prima pubblicazione resta quella dichiarata in testa al post, da credere sulla parola. È una conseguenza onesta del ripartire, non un difetto del singolo contenuto.

Strumenti

Tutto si verifica con strumenti liberi, indipendenti da questo sito:

  • OpenTimestamps — la marca temporale ancorata a Bitcoin: il client ots (pip install opentimestamps-client, ma esistono altre implementazioni) e un verificatore da browser stanno su opentimestamps.org.
  • minisign — per controllare le firme: jedisct1.github.io/minisign.
  • L'impronta si ricalcola con sha256sum (GNU coreutils), presente di base su quasi ogni sistema.