trilha

Sistemas Descentralizados/03 - Nostr4 min

Relays e Fan-out

Perguntas-guia
  • Que problema isso resolve?
  • Quando usar / quando NÃO usar?
  • Qual o principal trade-off?
  • Como isso falha em produção?

Conceito

Um relay é um servidor WebSocket que faz três coisas: aceita evento, guarda evento, entrega evento por filtro. É deliberadamente burro.

O que ele explicitamente não faz:

  • não conversa com outros relays
  • não tem consenso, nem visão global
  • não decide o que é verdade
  • não conhece seus seguidores, seu feed, nem sua timeline

Quem monta a timeline é o cliente. Essa inversão é a decisão arquitetural central do Nostr.

O cliente é quem faz o fan-out

O cliente mantém conexões com N relays simultaneamente e:

  • publica o mesmo evento assinado em todos
  • consulta todos com o mesmo filtro
  • deduplica localmente por id — trivial, porque o id é hash do conteúdo
                  ┌── relay.damus.io ──┐
   cliente ───────┼── nos.lol ─────────┼──── dedup por id ──> timeline
                  ├── relay.snort.social┤
                  └── meu-relay.com ───┘

O relay virou cache replicado e descartável. Se um te bane, some ou vira lixo, você continua existindo nos outros — mesma chave, mesmos seguidores, mesmo histórico. Não há migração porque nunca houve nada preso ao servidor.

O problema difícil: descoberta

Se o fan-out fosse a história toda, o Nostr teria um problema fatal de escala. Se cada cliente precisasse perguntar a todos os relays do mundo por todos os autores que segue, o custo cresceria sem limite.

Pior: o problema aparece antes da escala. Se você e outra pessoa não têm nenhum relay em comum, vocês são invisíveis um para o outro apesar de ambos estarem "no Nostr".

A resposta: outbox model (NIP-65)

A solução, que é conceitualmente bonita: cada usuário publica um evento kind: 10002 dizendo em quais relays ele escreve e em quais ele lê.

Marcador Significa
write "eu publico aqui — venha me buscar aqui"
read "eu leio aqui — me mande menções aqui"

Com isso o cliente para de perguntar a esmo:

  1. Busca o kind: 10002 de cada pessoa que você segue
  2. Descobre os relays write de cada uma
  3. Consulta cada autor no relay dele, não todos em todos

O nome vem de "caixa de saída": em vez de todo mundo despejar tudo numa caixa comum, cada autor tem a sua, e o leitor vai buscar onde a pessoa realmente escreve.

Repare no que isso é, tecnicamente: o índice de roteamento virou dado do próprio protocolo, publicado pelo usuário e assinado por ele. Não é DNS, não é registro central, não é tabela de nenhum servidor — é só mais um evento. Descoberta resolvida com o mecanismo que já existia.

Complementando, o NIP-66 permite que monitores publiquem quais relays existem e estão vivos — mesma ideia, aplicada aos relays em vez de aos autores.

Tipos de relay que apareceram na prática

Como relay é barato de rodar, o ecossistema se diferenciou sozinho:

Tipo Modelo Por que existe
Público grátis aberto a todos porta de entrada; sofre com spam
Pago assinatura mensal o preço é o anti-spam
Pessoal você hospeda o seu garante que seus dados existem
Web of trust só quem é seguido por quem o dono segue filtro social
Especializado só um kind (git, podcast, mídia) otimizado para um caso
Arquivo guarda histórico longo contrapõe o descarte dos outros

Trade-offs

O trade-off é limpo e vale como padrão de arquitetura geral:

Servidor burro + cliente esperto = servidor descartável.

O que se ganha:

  • Relay trivial de implementar (centenas de linhas) → relays abundantes → censura precisa de coordenação global
  • Nenhum relay precisa ser confiável, porque nenhum é necessário
  • Trocar de relay não é migração, é mudar uma lista

O que se paga, e o cliente paga tudo:

  • Banda e bateria: N conexões WebSocket abertas, N cópias de cada evento chegando
  • Complexidade: dedup, reconciliação, timeouts por relay, relay lento travando a UI
  • Consultas ruins: "todas as respostas a este post" exige perguntar a todo mundo e torcer
  • Sem completude: nenhuma resposta é "a resposta certa" — só "o que esses relays tinham". Daí existir o NIP-67 (EOSE Completeness Hint), para o relay ao menos avisar se terminou de verdade ou desistiu.

E há a assimetria estrutural: escrever é barato e confiável, ler é caro e incompleto. Isso é o oposto de um banco de dados normal, e reorganiza o design de qualquer app Nostr.

Exemplo prático

"Quantos seguidores eu tenho?" — pergunta simples, sem resposta.

Num sistema centralizado é um COUNT com índice. No Nostr:

  1. Seguidores estão em eventos kind: 3 de outras pessoas, espalhados
  2. Você teria que varrer todos os relays existentes procurando kind: 3 que citem sua pubkey
  3. Não existe "todos os relays existentes" — não há registro completo
  4. Relays limitam consultas amplas assim, e com razão

O melhor que dá é uma estimativa por amostragem: "3.400 seguidores segundo os 8 relays que consultei". Números diferentes em clientes diferentes, todos corretos dentro do seu escopo.

Isso incomoda no começo e é a lição mais transferível da nota: num sistema sem estado global, agregações deixam de ser fatos e viram observações. Todo produto construído nesse tipo de sistema precisa desenhar a UI para amostras — e não prometer contadores exatos que não pode entregar.

Relacionado


Parte de MOC Nostr · NIP-65

Buscar

Busca por título, seção e texto das notas