Dez mil estrelas e um commit

A 14 de julho a xAI publicou o código-fonte do Grok Build sob a licença Apache 2.0 e, esta manhã, o repositório já tinha mais de dez mil estrelas. A publicação surgiu depois de um teste ao nível do tráfego ter detetado a versão 0.2.93 da ferramenta a carregar um repositório sob controlo de versões, e todo o seu histórico git sob a forma de bundle, para armazenamento na nuvem controlado pela xAI, incluindo um repositório que o assistente tinha sido instruído a não ler. Cobrimos essa constatação a 14 de julho, e a instrução na altura era rodar todos os segredos que a ferramenta alguma vez tivesse visto. Essa instrução mantém-se.

Publicar o código parece a resposta mais forte possível a uma constatação destas. É o gesto que uma organização de engenharia faz quando acredita que o registo a iliba, e está a ser lido assim. Portanto a pergunta útil não é se a xAI fez bem em publicar. É mais estreita e mais prática: se agora fosse a esse repositório para se certificar do que a ferramenta fez nas suas máquinas, o que conseguiria efetivamente estabelecer?

Muito pouco, afinal, e as razões são estruturais e não sinistras. O repositório tem um commit, da autoria de um bot, a 16 de julho às 05:46 UTC, com a mensagem de que publica o harness e a TUI em código aberto. Um commit significa nenhum histórico. Não há antes, não há diff, não há sequência de alterações, não há nada com que comparar. Dez mil estrelas num repositório sem histórico são dez mil pessoas a subscrever um gesto.

O que o repositório não consegue dizer

Comece-se pela única pergunta que a constatação levantou de facto: para onde foi o código? Os carregamentos foram para um bucket do Google Cloud Storage nomeado no binário distribuído. No código-fonte publicado esse nome não aparece. É lido de uma variável de ambiente de tempo de compilação, resolvida quando o binário é construído. Quem compila a versão fornece o destino, e o código-fonte nada diz sobre para onde apontava a build distribuída. O código publicado não consegue responder à pergunta, em nenhum dos sentidos.

Depois olhe-se para a função que fazia o upload. Continua lá, na crate shell, e já não faz nada. Recebe os parâmetros, marca-os como não utilizados, espera no canal, deita o resultado fora e devolve um erro a assinalar que o upload do estado da sessão não está disponível. O corpo foi removido enquanto o que o rodeia ficou: os pontos de chamada estão intactos, o manifesto continua a listar as dependências, os auxiliares de armazenamento continuam compilados. O que se pode ler é o esqueleto do mecanismo com o mecanismo retirado.

Esta é também a segunda vez que a correção é feita num sítio que não se pode inspecionar. Os carregamentos originais foram travados a 13 de julho por uma flag do lado do servidor, e não por uma versão do cliente, o que era a parte mais aguda da primeira história: o binário que a sua equipa reviu e fixou nunca foi a coisa que decidia o que saía da máquina. Agora o código é público e o destino continua a ser um parâmetro de compilação e o interruptor continua no servidor. A superfície que é auditável e a superfície que decide não ficaram nada mais próximas uma da outra.

Quatro ficheiros mudaram, um não chegou

Há mais um pormenor, e precisa de ser dito com cuidado porque é fácil ler nele mais do que lá está. As strings dentro do binário 0.2.93 distribuído nomeiam uma crate chamada xai-data-collector, com cinco ficheiros de código: o cliente de armazenamento, a camada de armazenamento na nuvem, a fila, um observador de circuit breaker e um rastreador de acessos a ficheiros. O repositório publicado não contém nenhuma crate com esse nome. O mesmo conjunto de ficheiros vive numa crate chamada xai-file-utils, e quatro dos cinco correspondem. O quinto, o rastreador de acessos a ficheiros, não está de todo no código-fonte publicado.

O que isso estabelece e não estabelece é o ponto central. Não estabelece intenção. Mudar o nome de uma crate de algo que descreve recolher dados para algo que descreve tratar ficheiros é exatamente o que uma equipa faz durante uma arrumação de versão, e retirar um módulo que já não está ligado a nada é manutenção corrente. Não há aqui provas de ocultação e não estamos a alegar nenhuma. Vale também a pena notar a assimetria na própria evidência: o lado do binário assenta na extração de strings feita por um terceiro, ao passo que o lado do código-fonte pode ser verificado por qualquer pessoa que clone o repositório.

O que estabelece é que a publicação não pode funcionar como registo. O componente cujo nome descreve mais diretamente o rastreio dos acessos de um utilizador a ficheiros está ausente, os seus irmãos estão presentes com outro nome, e não há histórico de commits onde qualquer um dos dois factos possa ser examinado. Explicações inocentes e explicações culpadas produzem um repositório idêntico. É esse o problema de tratar uma publicação como prova: só funciona se o artefacto conseguir distinguir entre as duas, e este não consegue.

A afirmação que nunca foi feita

Dê-se crédito ao investigador, porque a disciplina desse lado desta história é a razão por que ela se aguenta. A análise ao nível do tráfego foi feita com um proxy de interceção contra um binário fixado, num repositório descartável semeado com ficheiros canário, e é reproduzível: existe um harness público e qualquer pessoa o pode correr. A medição foi crua. Um repositório de doze gigabytes produziu 5,10 gibibytes de tráfego de upload contra 192 kilobytes de conversa efetiva com o modelo, uma proporção próxima de vinte e oito mil para um. O ficheiro canário que o assistente tinha sido instruído a não ler saiu intacto do bundle capturado.

E depois o investigador escreveu o que o teste não provou. Que carregar não é treinar, porque só foi medida a transmissão. Que uma captura de três gigabytes não foi preservada. Que uma conclusão anterior estava errada, porque uma leitura de rede limitada ao processo não apanhou carregamentos que iam diretamente para endereços da Google, e essa conclusão foi retirada. Uma constatação que publica os seus próprios limites e a sua própria retratação é uma constatação que se pode usar.

Confronte-se isso com as respostas. A xAI disse que se preocupa profundamente com a privacidade, que respeita a escolha do cliente e que, para equipas que usam retenção zero de dados, nenhum vestígio ou dado de código é alguma vez retido. Elon Musk disse que todos os dados de utilizador carregados até agora serão completa e absolutamente apagados, e que não restará absolutamente nada. Pode bem ser verdade. Nada disso é verificável, e o apagamento não é confirmável a partir do exterior. O padrão ao longo de todo este episódio é consistente: as afirmações que podem ser testadas vieram de quem tinha o proxy, e as que não podem vieram do fornecedor.

Três perguntas antes de dar o caso por resolvido

A lição generalizável nada tem a ver com a xAI, e voltará a ser precisa dentro de um mês. Abrir o código tornou-se uma resposta padrão a uma constatação de segurança, e é uma boa resposta quando o artefacto transporta prova. Não o é automaticamente. Por isso, quando um fornecedor responde a uma constatação sobre os seus dados publicando código, coloque-lhe três perguntas antes de fechar o ticket.

Consigo ver o histórico? Um commit inicial comprimido é uma fotografia, não um registo. Se o repositório começa no dia da publicação, não pode mostrar o que mudou, e o que está sob investigação é precisamente uma mudança. O artefacto publicado corresponde ao binário distribuído? Se algum comportamento que importa é definido em tempo de compilação ou mantido num servidor, o código-fonte é a descrição de uma possibilidade e não do que correu nas suas máquinas. O código contém o componente que a constatação identificou? Se a coisa concreta que foi encontrada não está no repositório, a sua falta não é uma resposta, seja qual for a causa.

Nada disto defende manter o código fechado, e nada disto é uma acusação. Defende uma distinção que o seu processo de compras provavelmente ainda não faz: a publicação é um ato de divulgação, e uma auditoria é um processo que produz uma conclusão. Não são substitutos, não são sequer parentes próximos, e um fornecedor que fez a primeira não fez a segunda. As equipas que tratam um link para um repositório como caso encerrado estão a cometer um erro de categoria, e é o tipo de erro que só se vê em retrospetiva, depois da constatação seguinte.