Diecimila stelle e un solo commit

Il 14 luglio xAI ha pubblicato il sorgente di Grok Build sotto licenza Apache 2.0, e questa mattina il repository aveva superato le diecimila stelle. La pubblicazione è arrivata dopo un test sul traffico di rete che aveva colto la versione 0.2.93 dello strumento mentre caricava un repository tracciato, insieme alla sua intera cronologia git sotto forma di bundle, su uno storage cloud controllato da xAI, incluso un repository che all'assistente era stato ordinato di non leggere. Di quella scoperta abbiamo scritto il 14 luglio, e l'indicazione di allora era di ruotare ogni segreto che lo strumento avesse mai visto. Quell'indicazione resta valida.

Pubblicare il codice sembra la risposta più forte possibile a una scoperta del genere. È la mossa che fa un'organizzazione tecnica quando è convinta che le carte la scagionino, ed è così che la si sta leggendo. La domanda utile, quindi, non è se xAI abbia fatto bene a pubblicare. È più circoscritta e più concreta: se adesso andaste su quel repository per farvi un'idea di che cosa lo strumento abbia fatto sulle vostre macchine, che cosa potreste davvero stabilire?

Molto poco, a quanto pare, e le ragioni sono strutturali più che sinistre. Il repository contiene un solo commit, il cui autore è un bot, datato 16 luglio alle 05:46 UTC, con il messaggio che pubblica in open source l'harness e la TUI. Un solo commit significa nessuna cronologia. Non c'è un prima, non c'è un diff, non c'è una sequenza di modifiche, non c'è niente da confrontare. Diecimila stelle su un repository senza cronologia sono diecimila persone che approvano un gesto.

Che cosa il repository non può dirvi

Partiamo dall'unica domanda che la scoperta ha davvero sollevato: dove è finito il codice? I caricamenti sono andati verso un bucket di Google Cloud Storage il cui nome è scritto nel binario distribuito. Nel sorgente pubblicato quel nome non compare. Viene letto da una variabile d'ambiente definita in fase di compilazione, risolta al momento della build. Chi compila la release fornisce la destinazione, e il sorgente tace su dove fosse puntata la build distribuita. Il codice pubblicato non può rispondere alla domanda, in nessuna delle due direzioni.

Guardate poi la funzione che effettuava il caricamento. È ancora lì, nel crate della shell, e non fa più nulla. Prende i suoi parametri, li marca come inutilizzati, attende sul canale, butta via il risultato e restituisce un errore in cui segnala che il caricamento dello stato di sessione non è disponibile. Il corpo è stato rimosso mentre il contorno è rimasto: i punti di chiamata sono intatti, il manifest elenca ancora le dipendenze, gli helper dello storage sono ancora compilati dentro. Quello che potete leggere è lo scheletro del meccanismo, senza il meccanismo.

È anche la seconda volta che la correzione viene fatta in un punto che non potete ispezionare. I caricamenti originari sono stati fermati il 13 luglio da un flag lato server, non da una release del client, ed era questo il punto più tagliente della prima storia: il binario che il vostro team aveva esaminato e bloccato a una versione non è mai stato la cosa che decideva che cosa uscisse dalla macchina. Ora il codice è pubblico, la destinazione resta un parametro fissato in fase di build e l'interruttore resta sul server. La superficie che si può controllare e la superficie che decide non si sono avvicinate di un millimetro.

Quattro file sono passati, uno non è arrivato

C'è un ultimo dettaglio, e va detto con cura perché è facile leggerci dentro troppo. Le stringhe contenute nel binario 0.2.93 distribuito nominano un crate chiamato xai-data-collector, con cinque file sorgente: il client di storage, il livello di cloud storage, la coda, un observer del circuit breaker e un tracciatore degli accessi ai file. Il repository pubblicato non contiene alcun crate con quel nome. Lo stesso insieme di file si trova in un crate chiamato xai-file-utils, e quattro dei cinque corrispondono. Il quinto, il tracciatore degli accessi ai file, nel sorgente pubblicato non c'è affatto.

Che cosa questo stabilisce e che cosa non stabilisce è tutto il punto. Non stabilisce un'intenzione. Rinominare un crate passando da un nome che descrive la raccolta di dati a uno che descrive la gestione dei file è esattamente ciò che fa un team durante il riordino di una release, e togliere un modulo che non è più collegato a nulla è ordinaria manutenzione. Non ci sono prove di occultamento, e non lo stiamo insinuando. Vale anche la pena notare l'asimmetria delle prove stesse: il lato binario poggia sull'estrazione di stringhe fatta da terzi, mentre il lato sorgente può essere verificato da chiunque cloni il repository.

Quello che stabilisce è che la release non può funzionare da registro. Il componente il cui nome descrive nel modo più diretto il tracciamento degli accessi ai file di un utente è assente, i suoi fratelli sono presenti sotto un altro nome, e non esiste una cronologia dei commit in cui l'uno o l'altro fatto possa essere esaminato. Le spiegazioni innocenti e quelle colpevoli producono un repository identico. È questo il problema di trattare una pubblicazione come una prova: funziona solo se l'artefatto sa distinguere tra le due, e questo non ne è capace.

L'affermazione che non è mai stata fatta

Va dato credito al ricercatore, perché il rigore su quel lato della storia è la ragione per cui la storia regge. L'analisi del traffico di rete è stata condotta con un proxy di intercettazione puntato su un binario bloccato a una versione, in un repository usa e getta popolato con file canary, ed è riproducibile: esiste un harness pubblico, e chiunque può eseguirlo. La misura era netta. Un repository da dodici gigabyte ha prodotto 5,10 gibibyte di traffico in caricamento contro 192 kilobyte di conversazione effettiva con il modello, un rapporto vicino a ventottomila a uno. Il file canary che all'assistente era stato ordinato di non leggere è riemerso intatto dal bundle catturato.

E poi il ricercatore ha messo per iscritto ciò che il test non ha dimostrato. Che caricare non è addestrare, perché è stata misurata solo la trasmissione. Che una cattura da tre gigabyte non è stata conservata. Che una conclusione precedente era sbagliata, perché una lettura del traffico circoscritta al processo non aveva visto i caricamenti diretti verso indirizzi Google, e quella conclusione è stata ritirata. Una scoperta che pubblica i propri limiti e la propria ritrattazione è una scoperta che potete usare.

Mettete tutto questo a confronto con le risposte. xAI ha dichiarato di tenere profondamente alla privacy, di rispettare la scelta del cliente e che, per i team che usano la zero data retention, nessun dato di traccia o di codice viene mai conservato. Elon Musk ha detto che tutti i dati utente caricati fino a oggi saranno cancellati completamente e del tutto, e che non ne resterà assolutamente nulla. Può darsi benissimo che siano affermazioni vere. Nessuna di esse è controllabile, e la cancellazione non è verificabile dall'esterno. Lo schema, lungo tutto l'episodio, è coerente: le affermazioni che si possono verificare sono venute dalla persona con il proxy, quelle che non si possono verificare sono venute dal fornitore.

Tre domande prima di dirlo risolto

La lezione generalizzabile non ha nulla a che fare con xAI, e servirà di nuovo entro il mese. Aprire il codice è diventata la risposta standard alla scoperta di un problema di sicurezza, ed è una buona risposta quando l'artefatto porta con sé le prove. Non è detto che le porti. Quindi, quando un fornitore risponde a una scoperta sui vostri dati pubblicando codice, ponetegli tre domande prima di chiudere il ticket.

Posso vedere la cronologia? Un commit iniziale schiacciato è una fotografia, non un registro. Se il repository comincia il giorno della pubblicazione, non può mostrare che cosa sia cambiato, e la cosa sotto esame è proprio un cambiamento. L'artefatto pubblicato corrisponde al binario distribuito? Se un qualsiasi comportamento che conta viene fissato in fase di build o tenuto su un server, il sorgente è la descrizione di una possibilità, non di ciò che ha girato sulle vostre macchine. Il codice contiene il componente indicato dalla scoperta? Se la cosa specifica che è stata trovata non è nel repository, la sua assenza non è una risposta, qualunque ne sia la causa.

Niente di tutto questo è un argomento per tenere il codice chiuso, e niente di tutto questo è un'accusa. È un argomento a favore di una distinzione che il vostro processo di procurement probabilmente non traccia ancora: la pubblicazione è un atto di divulgazione, mentre un audit è un processo che produce una conclusione. Non sono sostituti, non sono nemmeno parenti stretti, e un fornitore che ha fatto la prima cosa non ha fatto la seconda. I team che trattano il link a un repository come un caso chiuso stanno commettendo un errore di categoria, ed è il tipo di errore che si vede solo a posteriori, dopo la scoperta successiva.