Zehntausend Sterne und ein Commit

Am 14. Juli hat xAI den Quellcode von Grok Build unter Apache 2.0 veröffentlicht, und bis heute Morgen hatte das Repository über zehntausend Sterne. Vorausgegangen war ein Test auf Netzwerkebene, der Version 0.2.93 des Werkzeugs dabei ertappte, wie sie ein versioniertes Repository samt vollständiger git-Historie als Bundle in einen von xAI kontrollierten Cloud-Speicher hochlud, darunter ein Repository, das der Assistent auf Anweisung nicht lesen sollte. Wir haben über diesen Befund am 14. Juli berichtet, und die Anweisung lautete damals, jedes Secret zu rotieren, das das Werkzeug je gesehen hat. Diese Anweisung gilt weiter.

Den Code zu veröffentlichen wirkt wie die stärkste denkbare Antwort auf einen solchen Befund. Es ist der Schritt, den eine Entwicklungsorganisation geht, wenn sie überzeugt ist, dass die Aktenlage sie entlastet, und genau so wird er auch gelesen. Die nützliche Frage ist deshalb nicht, ob xAI richtig daran getan hat zu veröffentlichen. Sie ist enger und praktischer: Wenn Sie jetzt in dieses Repository gingen, um sich Gewissheit darüber zu verschaffen, was das Werkzeug auf Ihren Rechnern getan hat, was könnten Sie tatsächlich feststellen?

Sehr wenig, wie sich zeigt, und die Gründe dafür sind strukturell, nicht finster. Das Repository trägt einen einzigen Commit, verfasst von einem Bot am 16. Juli um 05:46 UTC, mit der Nachricht, dass er Harness und TUI als Open Source veröffentliche. Ein Commit heißt keine Historie. Es gibt kein Vorher, kein Diff, keine Abfolge von Änderungen, nichts zum Vergleichen. Zehntausend Sterne auf einem Repository ohne Historie sind zehntausend Menschen, die eine Geste gutheißen.

Was das Repository Ihnen nicht sagen kann

Beginnen wir mit der einen Frage, die der Befund tatsächlich aufgeworfen hat: Wohin ging der Code? Die Uploads gingen in einen Bucket bei Google Cloud Storage, der im ausgelieferten Binary benannt ist. Im veröffentlichten Quellcode taucht dieser Name nicht auf. Er wird aus einer Umgebungsvariablen zur Compile-Zeit gelesen und erst beim Bauen des Binaries aufgelöst. Wer die Veröffentlichung kompiliert, bestimmt das Ziel, und der Quellcode schweigt dazu, auf welches Ziel der ausgelieferte Build gerichtet war. Der veröffentlichte Code kann die Frage nicht beantworten, in keine Richtung.

Dann zu der Funktion, die den Upload erledigt hat. Sie ist noch da, im Shell-Crate, und sie tut nichts mehr. Sie nimmt ihre Parameter entgegen, markiert sie als unbenutzt, wartet auf den Channel, wirft das Ergebnis weg und gibt einen Fehler zurück, der vermerkt, der Upload des Sitzungszustands sei nicht verfügbar. Der Rumpf wurde entfernt, die Umgebung blieb stehen: Die Aufrufstellen sind intakt, das Manifest führt die Abhängigkeiten weiter auf, die Storage-Helfer sind nach wie vor einkompiliert. Sie lesen das Skelett des Mechanismus, dem der Mechanismus entnommen wurde.

Es ist zugleich das zweite Mal, dass die Korrektur an einer Stelle vorgenommen wurde, die Sie nicht einsehen können. Die ursprünglichen Uploads wurden am 13. Juli über ein serverseitiges Flag gestoppt, nicht über ein Client-Release, und das war der schärfste Teil der ersten Geschichte: Das Binary, das Ihr Team geprüft und gepinnt hat, war nie die Instanz, die entschieden hat, was den Rechner verlässt. Jetzt ist der Code öffentlich, das Ziel ist weiterhin eine Eingabe zur Build-Zeit, und der Schalter sitzt weiterhin auf dem Server. Was sich prüfen lässt und was entscheidet, ist einander kein Stück näher gekommen.

Vier Dateien sind umgezogen, eine ist nicht angekommen

Es gibt ein weiteres Detail, und es muss sorgfältig formuliert werden, weil es sich leicht überinterpretieren lässt. Die Strings im ausgelieferten Binary 0.2.93 nennen ein Crate namens xai-data-collector mit fünf Quelldateien: den Storage-Client, die Cloud-Storage-Schicht, die Queue, einen Observer für den Circuit Breaker und einen File-Access-Tracker. Im veröffentlichten Repository gibt es kein Crate dieses Namens. Derselbe Satz Dateien liegt in einem Crate namens xai-file-utils, und vier der fünf entsprechen einander. Die fünfte, der File-Access-Tracker, fehlt im veröffentlichten Quellcode ganz.

Was das belegt und was nicht, darauf kommt es hier an. Eine Absicht belegt es nicht. Ein Crate von einem Namen, der das Sammeln von Daten beschreibt, in einen umzubenennen, der den Umgang mit Dateien beschreibt, ist genau das, was ein Team beim Aufräumen vor einem Release tut, und ein Modul zu entfernen, das an nichts mehr angebunden ist, ist ganz normale Aufräumarbeit. Es gibt hier keinen Beleg für eine Verschleierung, und wir behaupten auch keine. Erwähnenswert ist zudem die Asymmetrie der Belege selbst: Die Binary-Seite stützt sich auf die String-Extraktion eines Dritten, während die Quellcode-Seite jeder überprüfen kann, der das Repository klont.

Was es belegt, ist, dass die Veröffentlichung nicht als Nachweis taugt. Die Komponente, deren Name am unmittelbarsten das Verfolgen von Dateizugriffen eines Nutzers beschreibt, fehlt, ihre Geschwister sind unter einem anderen Namen vorhanden, und es gibt keine Commit-Historie, in der sich das eine oder das andere prüfen ließe. Harmlose Erklärungen und belastende erzeugen ein identisches Repository. Das ist das Problem daran, eine Veröffentlichung als Beweis zu behandeln: Es funktioniert nur, wenn das Artefakt zwischen beiden unterscheiden kann, und dieses kann es nicht.

Die Behauptung, die nie aufgestellt wurde

Man muss dem Forscher zugutehalten, dass die Disziplin auf dieser Seite der Geschichte der Grund dafür ist, dass sie standhält. Die Analyse auf Netzwerkebene lief über einen abfangenden Proxy an einem gepinnten Binary, auf einem Wegwerf-Repository, das mit Canary-Dateien bestückt war, und sie ist reproduzierbar: Es gibt ein öffentliches Harness, und jeder kann es laufen lassen. Die Messung war unmissverständlich. Ein zwölf Gigabyte großes Repository erzeugte 5,10 Gibibyte Upload-Verkehr gegenüber 192 Kilobyte tatsächlicher Modell-Konversation, ein Verhältnis von nahezu achtundzwanzigtausend zu eins. Die Canary-Datei, die der Assistent nicht lesen sollte, kam unversehrt aus dem mitgeschnittenen Bundle wieder heraus.

Und dann hat der Forscher aufgeschrieben, was der Test nicht bewiesen hat. Dass Hochladen kein Training ist, weil nur die Übertragung gemessen wurde. Dass ein Mitschnitt bei drei Gigabyte nicht gesichert wurde. Dass eine frühere Schlussfolgerung falsch gewesen war, weil eine prozessbezogene Netzwerkmessung Uploads übersehen hatte, die direkt an Google-Adressen gingen, und dass diese Schlussfolgerung zurückgezogen wurde. Ein Befund, der seine eigenen Grenzen und seinen eigenen Widerruf mitveröffentlicht, ist ein Befund, mit dem sich arbeiten lässt.

Dem stehen die Reaktionen gegenüber. xAI hat erklärt, Privatsphäre sei dem Unternehmen zutiefst wichtig, es respektiere die Wahl seiner Kunden, und für Teams mit Zero Data Retention würden weder Trace- noch Code-Daten jemals aufbewahrt. Elon Musk sagte, sämtliche bislang hochgeladenen Nutzerdaten würden vollständig und restlos gelöscht, und es werde rein gar nichts übrig bleiben. Das mag durchaus zutreffen. Überprüfbar ist nichts davon, und die Löschung lässt sich von außen nicht verifizieren. Das Muster zieht sich durch die gesamte Episode: Die Aussagen, die sich testen lassen, kamen von der Person mit dem Proxy, und die Aussagen, die sich nicht testen lassen, kamen vom Anbieter.

Drei Fragen, bevor Sie es als behoben abhaken

Die verallgemeinerbare Lehre hat nichts mit xAI zu tun, und sie wird noch in diesem Monat wieder gebraucht werden. Open Sourcing ist zur Standardantwort auf einen Sicherheitsbefund geworden, und es ist eine gute Antwort, wenn das Artefakt Belege trägt. Von selbst ist es das nicht. Wenn ein Anbieter also auf einen Befund über Ihre Daten antwortet, indem er Code veröffentlicht, stellen Sie ihm drei Fragen, bevor das Ticket geschlossen wird.

Kann ich die Historie einsehen? Ein squashed Initial-Commit ist eine Momentaufnahme, kein Protokoll. Wenn das Repository am Tag der Veröffentlichung beginnt, kann es nicht zeigen, was sich geändert hat, und untersucht wird ja gerade eine Änderung. Entspricht das veröffentlichte Artefakt dem ausgelieferten Binary? Wenn ein Verhalten, auf das es ankommt, zur Build-Zeit gesetzt oder auf einem Server vorgehalten wird, ist der Quellcode die Beschreibung einer Möglichkeit und nicht dessen, was auf Ihren Rechnern lief. Enthält der Code die Komponente, die der Befund benannt hat? Wenn genau das, was gefunden wurde, nicht im Repository liegt, ist sein Fehlen keine Antwort, was auch immer dahintersteckt.

Nichts davon spricht dafür, Code geschlossen zu halten, und nichts davon ist ein Vorwurf. Es spricht für eine Unterscheidung, die Ihr Einkaufsprozess vermutlich noch nicht trifft: Eine Veröffentlichung ist ein Akt der Offenlegung, ein Audit ist ein Verfahren, das zu einem Ergebnis kommt. Sie sind kein Ersatz füreinander, sie sind nicht einmal nahe Verwandte, und ein Anbieter, der das Erste getan hat, hat das Zweite nicht getan. Teams, die einen Link auf ein Repository als erledigten Fall verbuchen, begehen einen Kategorienfehler, und das ist die Art von Fehler, die erst im Rückblick sichtbar wird, nach dem nächsten Befund.