Sistemas Descentralizados/03 - Nostr4 min
Eventos e Chaves
- Que problema isso resolve?
- Quando usar / quando NÃO usar?
- Qual o principal trade-off?
- Como isso falha em produção?
Conceito
O NIP-01 — o único obrigatório — cabe em duas ideias: uma identidade é uma chave e um dado é um JSON assinado. Tudo mais no Nostr é consequência.
A identidade
Um par de chaves secp256k1 (a mesma curva do Bitcoin), gerado offline, sem avisar ninguém:
| Formato bruto | Formato bech32 (NIP-19) | |
|---|---|---|
| Privada | 32 bytes hex | nsec1... |
| Pública | 32 bytes hex | npub1... |
O npub é o seu identificador global. Não há servidor no meio dele, não há registro, não há unicidade a negociar — o espaço de chaves é grande o bastante para que colisão não seja preocupação prática.
O bech32 do NIP-19 não é enfeite: o prefixo (npub/nsec) e o checksum existem para que colar a chave errada seja um erro detectável, e não uma chave privada publicada por acidente.
O evento
{
"id": "<sha256 da serialização canônica>",
"pubkey": "<chave pública do autor, 32 bytes hex>",
"created_at": 1756400000,
"kind": 1,
"tags": [["e", "<id do evento respondido>"],
["p", "<pubkey mencionada>"]],
"content": "texto do post",
"sig": "<assinatura Schnorr sobre o id>"
}
Sete campos. É o protocolo inteiro.
O id é o conteúdo
O id não é atribuído por ninguém — é calculado:
id = sha256( json([0, pubkey, created_at, kind, tags, content]) )
Uma serialização canônica (sem espaços, escapes definidos, ordem fixa) garante que qualquer implementação chegue ao mesmo hash. Consequências, todas de graça:
- Deduplicação trivial entre relays: mesmo
id= mesmo evento - Imutabilidade: mudou uma vírgula, mudou o
id, e asignão bate mais - Idempotência: republicar não cria duplicata (ver 7. Kinds de Eventos e Mensagens)
- Verificação independente: qualquer um confere sozinho, sem perguntar a nenhum servidor
Essa é a mesma ideia do Git (commit hash) e do IPFS (CID): endereçamento por conteúdo. Quando o identificador é derivado do dado, "esse dado foi adulterado?" deixa de ser uma pergunta que precisa de autoridade para responder.
As tags
Tags são arrays de strings, e o primeiro elemento é o nome. As duas fundamentais:
| Tag | Aponta para | Uso |
|---|---|---|
e |
um evento (por id) |
resposta, citação, reação |
p |
uma pessoa (por pubkey) | menção, destinatário |
Tags de letra única minúscula são indexáveis pelos relays — dá para filtrar por elas ({"#e": [...]}). Tags maiores não são: servem como metadado carregado junto, sem custo de índice.
É um design de banco de dados exposto no protocolo: o formato do dado já diz o que é índice e o que é payload. Com isso, todo relay — em Go, Rust, TypeScript — chega ao mesmo esquema sem combinar nada.
O que o protocolo deliberadamente não define
Vale listar, porque a ausência é a parte instrutiva:
- Não define ordem.
created_até declarado pelo autor e pode ser mentira. - Não define verdade. A assinatura prova autoria, nunca veracidade.
- Não define unicidade. Nada impede dois eventos contraditórios do mesmo autor.
- Não define quem pode publicar. Isso é política de relay, não de protocolo.
Trade-offs
A assinatura entrega autenticidade e integridade — e só. É pouco em comparação com um banco de dados, e é exatamente o suficiente para o problema que o Nostr escolheu resolver. Ver 1. Nostr vs Blockchain.
O created_at auto-declarado é o trade-off mais desconfortável. Sem consenso não há tempo objetivo, então o protocolo escolheu a solução honesta: deixar o autor declarar e não fingir garantia. Um autor pode datar um post no futuro para fixá-lo no topo dos feeds — e faz-se isso na prática. A defesa é do cliente (rejeitar timestamps absurdos), não do protocolo.
Em compensação, a ausência de estado global torna a implementação absurdamente simples: um relay funcional cabe em algumas centenas de linhas, e isso é o que torna relays baratos e, por consequência, abundantes e descartáveis — que é a fonte real da resistência à censura do Nostr.
Exemplo prático
Por que a assinatura, sozinha, já resolve censura de conteúdo.
Um relay hostil quer alterar seu post. Ele pode:
- ❌ Editar o texto — o
idmuda, asignão bate, todo cliente rejeita - ❌ Forjar um post seu — precisaria da sua
nsec - ❌ Mudar a data — mesmo problema: quebra o hash e a assinatura
- ✅ Não te entregar o evento — isso ele pode
Ou seja: um relay não consegue mentir, só consegue silenciar. E silenciar não funciona quando o cliente pergunta a outros cinco relays.
Aí está a economia inteira do design: assinatura resolve adulteração (problema criptográfico, resolvido de graça), e redundância resolve omissão (problema de disponibilidade, resolvido com fan-out barato). Nenhum dos dois precisou de consenso — e é por isso que o Nostr é ordens de grandeza mais barato que uma blockchain fazendo um trabalho parecido.
O que sobra sem solução é a terceira coluna: se todos os relays que você usa te omitirem, você sumiu. Ver 5. Limites do Nostr.