Quarto texto da série em que eu leio arquiteturas conhecidas a partir de material público. Não é a planta: é a leitura do que organiza o resto.
Esse é o caso que mais me interessa dos seis, porque ele inverte uma intuição comum: a restrição de segurança mais dura do conjunto é também o que deixa o sistema mais simples.
O servidor não pode ler nada
Com criptografia ponta a ponta, a mensagem é cifrada no aparelho de quem envia e só é decifrada no de quem recebe. O servidor transporta bytes que não consegue interpretar.
Isso apaga, de uma vez, uma lista de coisas que outros produtos fazem no servidor:
Busca de mensagem no lado do servidor. Impossível. A busca vive no aparelho, sobre o histórico local.
Prévia de conteúdo, moderação automática do texto, recomendação baseada no que foi dito, anúncio por assunto da conversa. Nada disso existe.
Recuperar histórico ao trocar de aparelho. O servidor não tem o histórico em claro, então isso vira um problema de transferência entre dispositivos ou de backup cifrado com chave que o usuário controla.
Parece uma lista de limitações. Eu leio como um enxugamento: o servidor tem uma responsabilidade só, que é entregar bytes para o destinatário certo.
Store and forward, e depois esqueça
O modelo de entrega é o mais simples possível.
O servidor guarda a mensagem cifrada enquanto o destinatário está offline, e descarta assim que a entrega é confirmada.
Isso tem uma consequência de custo que eu acho impressionante: o armazenamento não cresce com o uso. Um sistema que guarda todo o histórico de todos os usuários para sempre tem um custo que sobe monotonamente. Um que guarda só o que ainda não foi entregue tem um custo proporcional a quantas pessoas estão offline agora, que é um número quase constante.
A base de dados permanente é o aparelho de cada um. O servidor é infraestrutura de trânsito.
O problema real: conexões, não requisições
Mensageria não é tráfego HTTP comum. Cada aparelho mantém uma conexão aberta, esperando, por horas.
Isso muda o gargalo. Não é CPU por requisição: é quantas conexões ociosas uma máquina aguenta, cada uma consumindo um pouco de memória e um descritor de arquivo.
É por isso que a escolha de Erlang aparece em toda conversa sobre esse sistema, e ela não é folclore. A plataforma foi construída para telecomunicações: processos leves aos milhões, isolamento entre eles, supervisão que reinicia o que quebrou sem derrubar o resto.
O modelo mental encaixa quase perfeitamente. Cada conexão é um processo minúsculo e independente. Um erro em uma conversa não contamina as outras. E o número de usuários por servidor fica em uma ordem de grandeza incomum, o que explica a relação famosa entre o tamanho do time e o tamanho da base.
Eu tiro uma lição disso que não é sobre Erlang: a natureza do gargalo deveria escolher a tecnologia. Um sistema dominado por conexões ociosas tem restrições diferentes de um dominado por processamento. Escolher a linguagem por gosto e depois lutar contra o gargalo é o caminho comum, e é o mais caro.
Grupo, e o problema de N dispositivos
Aqui a criptografia cobra um preço.
Sem cifra, mandar mensagem pra um grupo de cem pessoas é uma escrita e um fan-out no servidor. Com cifra ponta a ponta, cada destinatário tem uma chave diferente, então o conteúdo precisaria ser cifrado uma vez para cada.
Pior: cada pessoa tem vários aparelhos, e cada aparelho é um destinatário criptográfico independente. Cem pessoas com três aparelhos são trezentas cifragens, feitas no celular de quem enviou.
A saída conhecida é uma chave de remetente para o grupo: quem envia cifra o conteúdo uma vez com essa chave, e distribui a chave individualmente para cada participante. A distribuição da chave é O(N), a mensagem é O(1). Enquanto ninguém entrar nem sair, as mensagens seguintes saem baratas.
Isso explica por que entrar e sair de grupo é caro e por que grupo tem limite de tamanho. O limite não é de banco de dados: é do custo de redistribuir chave.
Escrevi sobre a diferença entre garantir conteúdo e garantir origem em assinatura digital e hash — é a mesma família de decisões, aqui aplicada no caminho quente do produto.
O que a restrição obriga a fazer bem
Um sistema que não pode reler a mensagem depois precisa acertar a entrega na primeira vez.
Isso força identificador de mensagem estável gerado no cliente, para deduplicar reenvio. Força recibo em camadas, com estados distintos para enviado, entregue e lido. Força ordenação que não dependa do relógio do servidor, porque o servidor não é a fonte da verdade do conteúdo.
Tudo isso é idempotência e ordenação de eventos, os mesmos problemas de qualquer fila, só que com uma restrição a mais: quem coordena não pode inspecionar o que está coordenando.
O que eu levo pra sistema pequeno
Restrição forte simplifica. Decidir que o servidor não lê o conteúdo elimina dezenas de funcionalidades possíveis e, com elas, dezenas de subsistemas. Vale perguntar, em qualquer projeto, o que a gente ganharia se decidisse não guardar algo.
Estado permanente no cliente é uma opção legítima. Nem todo dado precisa viver no servidor. Quando o cliente é a fonte, o custo de armazenamento para de crescer com o tempo.
Deixe o gargalo escolher a tecnologia. Conexões ociosas, processamento pesado e escrita intensa são problemas diferentes, e as ferramentas boas em um costumam ser medianas nos outros.
Não guarde o que já foi entregue. É a política de retenção mais simples que existe, e quase ninguém considera, porque guardar parece sempre a opção segura. Guardar é responsabilidade, é custo, e é a primeira coisa que vaza.