trilha

Sistemas Descentralizados/03 - Nostr5 min

Kinds de Eventos e Mensagens

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

Tabelas de consulta do NIP-01. Para o conceito por trás, ver 2. Eventos e Chaves.

Conceito

As 4 faixas de kind — e por que elas existem

Este é o detalhe mais elegante do NIP-01: o número do kind não é só um rótulo, é uma instrução de armazenamento para o relay. A faixa em que ele cai determina o comportamento, sem precisar de campo extra nem de configuração.

Faixa Tipo O relay deve
1000 ≤ n < 10000, 4 ≤ n < 45, n = 1, n = 2 Regular guardar todos
10000 ≤ n < 20000, n = 0, n = 3 Replaceable guardar só o mais recente por (pubkey, kind)
20000 ≤ n < 30000 Ephemeral não guardar — só repassar a quem está ouvindo
30000 ≤ n < 40000 Addressable guardar só o mais recente por (pubkey, kind, tag d)

Repare no que isso resolve:

  • Replaceable é como o perfil (kind: 0) e a lista de seguidos (kind: 3) funcionam sem acumular lixo. Você não "edita" o perfil — publica um novo, e o relay descarta o anterior. É estado por sobrescrita, não por mutação.
  • Addressable adiciona uma chave secundária (a tag d), permitindo vários documentos editáveis do mesmo tipo. É como um artigo (kind: 30023) tem um endereço estável (naddr) e continua editável.
  • Ephemeral é fire-and-forget: usado por autenticação (kind: 22242) e por RPC entre apps (kind: 24133), onde persistir seria vazamento, não feature.

Empate de timestamp em replaceable? Regra do NIP-01: fica o de id menor em ordem lexicográfica. Determinístico, sem coordenação — todo relay chega ao mesmo resultado sozinho.

Kinds mais usados

kind O que é NIP
0 Metadados do usuário (perfil) 01
1 Nota de texto curta 10
3 Lista de seguidos 02
5 Pedido de deleção 09
6 / 16 Repost / repost genérico 18
7 Reação (curtida) 25
9 Mensagem de chat C7
11 Thread de fórum 7D
13 Seal (camada interna do gift wrap) 59
14 / 15 DM privada / arquivo em DM 17
20 / 21 / 22 Foto / vídeo / vídeo vertical 68, 71
1059 Gift Wrap (envelope anônimo) 59
1111 Comentário genérico 22
1984 Denúncia 56
1985 Rótulo (label) 32
9734 / 9735 Pedido de zap / recibo de zap 57
10002 Lista de relays (outbox model) 65
10050 Relays para receber DM 17, 51
22242 Autenticação no relay (efêmero) 42
24133 Nostr Connect / remote signing 46
27235 HTTP Auth 98
30023 Artigo longo (Markdown) 23
30078 Dados específicos de aplicação 78
31922–31925 Eventos de calendário e RSVP 52
34550 Definição de comunidade 72 ⚠️

A tabela oficial tem mais de 150 entradas e não é exaustiva — o registro legível por máquina fica em registry-of-kinds. Ou seja: o espaço de kind é aberto, e a colisão é evitada por convenção social, não por autoridade.

Mensagens Cliente → Relay

Mensagem Para quê NIP
["EVENT", <evento>] publicar 01
["REQ", <sub_id>, <filtros...>] pedir histórico e assinar novidades 01
["CLOSE", <sub_id>] cancelar a inscrição 01
["AUTH", <evento kind 22242>] responder ao desafio do relay 42
["COUNT", <sub_id>, <filtros...>] contar sem baixar 45

Mensagens Relay → Cliente

Mensagem Significa NIP
["EVENT", <sub_id>, <evento>] toma um evento dessa inscrição 01
["OK", <id>, <bool>, <motivo>] aceitei / recusei seu evento 01
["EOSE", <sub_id>] acabou o histórico — daqui pra frente é ao vivo 01
["CLOSED", <sub_id>, <motivo>] encerrei sua inscrição, e por quê 01
["NOTICE", <mensagem>] recado legível por humano 01
["AUTH", <desafio>] autentique-se 42
["COUNT", <sub_id>, {"count": n}] a contagem pedida 45

O EOSE é a peça de design mais interessante do conjunto. Uma única inscrição serve histórico e tempo real no mesmo canal, e o EOSE é apenas o marcador entre os dois. Não há "endpoint de histórico" e "websocket de updates" — há um só fluxo, com uma fronteira anunciada.

Motivos padronizados de OK e CLOSED

O terceiro campo do OK e o do CLOSED começam com um prefixo legível por máquina, seguido de texto livre:

duplicate · pow · blocked · rate-limited · invalid · restricted · mute · error

["OK", "b1a649eb...", false, "rate-limited: slow down, 10 events/min"]
["CLOSED", "sub1", "auth-required: precisa autenticar para ler DMs"]

O padrão — prefixo para a máquina, resto para o humano — é o mesmo dos status codes HTTP com corpo de erro, e resolve o mesmo problema: dar ao cliente algo para decidir programaticamente sem perder a mensagem diagnóstica.

Trade-offs

A faixa do kind codificar semântica de armazenamento é econômico e irreversível. Economia: zero campos extras, zero configuração, e um relay implementa as quatro políticas com um if na faixa. Irreversibilidade: um kind publicado na faixa errada não pode ser reclassificado depois sem quebrar quem já o armazena.

O custo maior é a numeração como espaço compartilhado global. Não há registro autoritativo — dois projetos podem escolher o mesmo kind para coisas diferentes, e o conflito só aparece quando os dados se cruzam num relay. A defesa é convenção e o registro comunitário, o que é frágil por construção. É o preço de não ter autoridade, coerente com o resto do desenho.

Exemplo prático

Uma sessão completa, do primeiro byte:

→ ["REQ","feed",{"kinds":[1],"authors":["3bf0c63f..."],"limit":20}]
← ["EVENT","feed",{...}]        # 20 eventos do histórico
← ["EOSE","feed"]               # acabou o passado
← ["EVENT","feed",{...}]        # 10 min depois: post novo, ao vivo

→ ["EVENT",{"kind":1,"content":"oi","sig":"..."}]
← ["OK","a1b2c3...",true,""]

→ ["EVENT",{"kind":1,"content":"oi","sig":"..."}]     # mandei de novo
← ["OK","a1b2c3...",true,"duplicate: already have this event"]

Repare na última linha: reenviar o mesmo evento devolve true, não erro. E é correto — o id é o hash do conteúdo, então "já tenho" e "acabei de guardar" levam ao mesmo estado final.

Ou seja: publicar no Nostr é idempotente por construção, e de graça. Não precisou de chave de idempotência como no exemplo de 1. O que é um Sistema Distribuído, porque o identificador é o conteúdo. É a mesma propriedade que faz o retry seguro funcionar — obtida por endereçamento por conteúdo em vez de por protocolo.

Relacionado


Parte de MOC Nostr · NIP-01

Buscar

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