NIP-95: Protocolo Híbrido Relay-P2P via WebRTC

npub1jxl2tnvnv9gycsy64aze295c3a529lx5sfmzlktf5lxuw805g5wqew0z0n
hex
108f443daba652bfc133accaba4cd183b830f98a29f27b0a595f6daad738f5denevent
nevent1qqsppr6y8k46v54lcye6ej46fngc8wpslx9znunmpfv47md26uu0thsprpmhxue69uhhyetvv9ujuem4d36kwatvw5hx6mm9qgsfr049ekfkz5zvgzd273v4z6vg769zln2gya30m9560nw8rh6y28qmjeapunaddr
naddr1qqxxummnw3ez6mnfwqknjdgprpmhxue69uhhyetvv9ujuem4d36kwatvw5hx6mm9qgsfr049ekfkz5zvgzd273v4z6vg769zln2gya30m9560nw8rh6y28qrqsqqqa28ct4vp5Kind-30023 (Article)
1. Resumo Executivo
A NIP-95 estende o protocolo Nostr (NIP-01) para permitir uma arquitetura de distribuição de eventos híbrida. O Relay deixa de ser a única fonte de verdade e entrega, passando a atuar também como um Seed Node e Signaling Server. Clientes estáveis tornam-se Super Peers, servindo eventos recentes via WebRTC Data Channels para outros peers, reduzindo drasticamente o consumo de banda e a carga de processamento do relay central.
2. Taxonomia de Nós (Papéis)
| Papel | Descrição | Requisitos Técnicos | | :--- | :--- | :--- | | Relay Seed | Nó de autoridade e coordenação. Armazena histórico completo. | Suporte a WebSocket + Lógica de Signaling + Indexação de Cache de Peers. | | Super Peer | Cliente estável que atua como servidor P2P temporário. | Conexão >30min, Uptime alto, Cache Local (IndexedDB/LevelDB). | | Casual Peer | Cliente padrão (mobile ou instável). Consumidor prioritário. | Suporte a WebRTC e fallback para WebSocket. |
3. Mensagens do Protocolo (Client <-> Relay)
As mensagens seguem o formato JSON padrão do Nostr: ["COMANDO", <payload>].
3.1 Registro e Gerenciamento de Peer
PEER_REGISTER (Client -> Relay)
Anuncia a intenção do cliente de participar da rede P2P.
[
"PEER_REGISTER",
{
"bandwidth": 10, // Mbps (opcional)
"storage": 500, // MB disponíveis para cache (opcional)
"publicKey": "hex", // Identidade para reputação/pagamentos
"lightningAddress": "[email protected]" // Incentivos futuros
}
]
PEER_REGISTERED (Relay -> Client)
Confirmação de registro e atribuição de ID de sessão.
[
"PEER_REGISTERED",
{
"peer_id": "uuid-v4",
"heartbeat_interval": 30000,
"status": "registered"
}
]
3.2 Sincronização de Cache
PEER_CACHE_HAVE (Client -> Relay)
Informa ao relay quais eventos o cliente possui em seu cache local para que o relay possa rotear requisições.
[
"PEER_CACHE_HAVE",
{
"events": [
{ "id": "hex", "pubkey": "hex", "kind": 1, "created_at": 162... },
...
]
}
]
3.3 Descoberta e Signaling (Smart Routing)
PEER_OFFER (Relay -> Client)
Enviado pelo relay quando um cliente faz um REQ e o relay identifica que Super Peers possuem os dados.
[
"PEER_OFFER",
{
"subscription": "sub_id_123",
"offers": {
"event_id_abc": ["peer_id_1", "peer_id_2"],
"event_id_def": ["peer_id_3"]
},
"source": "smart_routing"
}
]
PEER_SIGNAL (Client -> Relay -> Client)
Encapsula o handshake WebRTC (SDP/ICE Candidates). O Relay apenas repassa.
[
"PEER_SIGNAL",
{
"target_peer": "uuid-do-outro-peer",
"signal_data": { "type": "offer/answer", "sdp": "..." } // ou ice candidate
}
]
4. Comunicação P2P (Peer <-> Peer via WebRTC)
Uma vez aberto o WebRTC Data Channel, os peers trocam mensagens binárias ou JSON diretamente.
4.1 Requisição de Evento (P2P_REQUEST)
{
"type": "P2P_REQUEST",
"event_id": "hex-id-do-evento"
}
4.2 Entrega de Evento (P2P_EVENTS)
{
"type": "P2P_EVENTS",
"event": {
"id": "...",
"pubkey": "...",
"sig": "...",
"content": "...",
...
}
}
5. Algoritmos e Lógica de Implementação (LLM Guide)
5.1 O Algoritmo de "Smart Routing" (No Relay)
Ao receber uma mensagem ["REQ", sub_id, filter]:
- Processamento Padrão: O relay deve buscar no banco de dados local e enviar eventos via WebSocket (compatibilidade NIP-01).
- Interseção de Cache: O relay consulta seu
CacheTracker(um mapa em memória deevent_id -> Set<peer_id>). - Seleção de Peers: Se o filtro do
REQbater com eventos indexados noCacheTracker:- Selecione até 3 Super Peers online para cada evento.
- Envie
PEER_OFFERao cliente solicitante.
5.2 O Processo de Promoção (Super Peer)
O Relay deve manter um loop de monitoramento (ex: a cada 1 min):
- Se
peer.uptime > 30minANDpeer.reputation > thresholdANDpeer.cache_size > 0:- Envie
["PEER_PROMOTED", { "peer_id": "..." }].
- Envie
- Se o peer desconectar ou falhar em responder heartbeats:
- Remova-o de todos os índices de
CacheTracker.
- Remova-o de todos os índices de
5.3 O Handshake WebRTC (Signaling)
- Peer B (Requester) recebe
PEER_OFFERcontendoPeer A. - Peer B gera um SDP Offer e envia ao Relay:
["PEER_SIGNAL", { target: "Peer A", signal: offer }]. - Relay localiza a conexão WebSocket de Peer A e repassa:
["PEER_SIGNAL_RELAY", { from: "Peer B", signal: offer }]. - Peer A gera um SDP Answer e envia ao Relay:
["PEER_SIGNAL", { target: "Peer B", signal: answer }]. - Relay repassa ao Peer B.
- Conexão P2P estabelecida via STUN/TURN.
6. Segurança e Integridade
- Assinaturas Schnorr: Clientes OBRIGATORIAMENTE devem validar a assinatura (
sig) de cada evento recebido via P2P usando a bibliotecanoble-curvesou similar. Se o ID do evento ou a assinatura forem inválidos, o peer remetente deve ser desconectado e reportado. - Reputação de Peer:
- O Relay deve manter um score de reputação no Redis/DB.
+1para cada evento validado e entregue (reportado viaPEER_STATS).-100(banimento) para envio de assinaturas falsas.- Decay temporal: Reputação diminui se o peer ficar offline.
- Privacidade: O uso de WebRTC expõe o endereço IP entre os peers. Clientes devem solicitar consentimento do usuário antes de ativar o modo P2P ("Enable P2P Acceleration").
7. Extensão NIP-11 (Relay Information)
Relays compatíveis devem anunciar os limites do protocolo:
{
"supported_nips": [1, 11, 95],
"limitation": {
"p2p_enabled": true,
"p2p_max_connections_per_peer": 10,
"p2p_heartbeat_ms": 30000
}
}
8. Pseudocódigo para Implementação em LLM
Handler de Mensagem (Servidor)
function handleMessage(client, message) {
const [type, payload] = message;
switch(type) {
case "PEER_REGISTER":
return registerPeer(client, payload);
case "PEER_CACHE_HAVE":
return updateCacheTracker(client.id, payload.events);
case "PEER_SIGNAL":
const target = getClientById(payload.target_peer);
if (target) {
send(target, ["PEER_SIGNAL_RELAY", { from: client.id, signal: payload.signal_data }]);
}
break;
case "REQ":
handleStandardNip01(client, payload);
triggerSmartRouting(client, payload); // NIP-95 logic
break;
}
}
Handler de Recebimento P2P (Cliente)
peer.on('data', data => {
const msg = JSON.parse(data);
if (msg.type === 'P2P_EVENTS') {
if (nostr.verifySignature(msg.event)) {
displayEvent(msg.event);
localCache.save(msg.event);
} else {
reportMaliciousPeer(msg.peer_id);
}
}
});
9. Fluxograma de Decisão (LLM Reasoning)
- Input: Filtro Nostr (ex:
{ "kinds": [1], "limit": 10 }). - Relay Query: Busca no DB local (Fast).
- P2P Query: Verifica se algum Super Peer enviou
PEER_CACHE_HAVErecentemente comkind: 1. - Action: Envia resultados do DB via WebSocket +
PEER_OFFERpara conectar aos Super Peers que têm mais eventos do mesmo tipo, visando futuras atualizações. - Result: O cliente recebe os primeiros 10 eventos via WebSocket (baixa latência) e estabelece P2P para o fluxo contínuo (sustentabilidade).
Raw JSON
{
"kind": 30023,
"id": "108f443daba652bfc133accaba4cd183b830f98a29f27b0a595f6daad738f5de",
"pubkey": "91bea5cd9361504c409aaf459516988f68a2fcd482762fd969a7cdc71df4451c",
"created_at": 1780607504,
"tags": [
[
"d",
"nostr-nip-95"
],
[
"alt",
"Blog post: NIP-95: Protocolo Híbrido Relay-P2P via WebRTC"
],
[
"title",
"NIP-95: Protocolo Híbrido Relay-P2P via WebRTC"
],
[
"summary",
"Esta é uma especificação técnica exaustiva para a **NIP-95 (Hybrid Peer-to-Peer Relay Protocol)**. Ela foi desenhada para ser \"LLM-ready\", fornecendo definições de mensagens, fluxos lógicos, esquemas de dados e regras de estado que permitem a um modelo de IA gerar código funcional para clientes ou servidores Nostr."
],
[
"published_at",
"1780607309"
],
[
"t",
"nostr"
],
[
"t",
"nip95"
],
[
"t",
"nip"
],
[
"t",
"webrtc"
],
[
"t",
"relay"
],
[
"t",
"relay-p2p"
],
[
"t",
"p2p"
],
[
"r",
"https://updatecachetracker(client.id/"
],
[
"r",
"https://payload.signal/"
],
[
"r",
"https://client.id/"
],
[
"r",
"https://`peer.reputation/"
],
[
"r",
"https://`peer.uptime/"
],
[
"r",
"https://`peer.cache/"
],
[
"r",
"https://peer.on/"
],
[
"r",
"https://json.parse/"
],
[
"r",
"https://displayevent(msg.event/"
],
[
"r",
"https://getclientbyid(payload.target/"
],
[
"r",
"https://reportmaliciouspeer(msg.peer/"
],
[
"r",
"https://msg.type/"
],
[
"r",
"https://msg.event/"
],
[
"r",
"https://localcache.save/"
],
[
"r",
"https://payload.events/"
],
[
"r",
"https://nostr.verifysignature/"
],
[
"r",
"https://\"[email protected]/"
],
[
"client",
"Amethyst"
]
],
"content": "## 1. Resumo Executivo\nA NIP-95 estende o protocolo Nostr (NIP-01) para permitir uma arquitetura de distribuição de eventos híbrida. O Relay deixa de ser a única fonte de verdade e entrega, passando a atuar também como um **Seed Node** e **Signaling Server**. Clientes estáveis tornam-se **Super Peers**, servindo eventos recentes via WebRTC Data Channels para outros peers, reduzindo drasticamente o consumo de banda e a carga de processamento do relay central.\n\n---\n\n## 2. Taxonomia de Nós (Papéis)\n\n| Papel | Descrição | Requisitos Técnicos |\n| :--- | :--- | :--- |\n| **Relay Seed** | Nó de autoridade e coordenação. Armazena histórico completo. | Suporte a WebSocket + Lógica de Signaling + Indexação de Cache de Peers. |\n| **Super Peer** | Cliente estável que atua como servidor P2P temporário. | Conexão \u003e30min, Uptime alto, Cache Local (IndexedDB/LevelDB). |\n| **Casual Peer** | Cliente padrão (mobile ou instável). Consumidor prioritário. | Suporte a WebRTC e fallback para WebSocket. |\n\n---\n\n## 3. Mensagens do Protocolo (Client \u003c-\u003e Relay)\n\nAs mensagens seguem o formato JSON padrão do Nostr: `[\"COMANDO\", \u003cpayload\u003e]`.\n\n### 3.1 Registro e Gerenciamento de Peer\n\n#### `PEER_REGISTER` (Client -\u003e Relay)\nAnuncia a intenção do cliente de participar da rede P2P.\n```json\n[\n \"PEER_REGISTER\",\n {\n \"bandwidth\": 10, // Mbps (opcional)\n \"storage\": 500, // MB disponíveis para cache (opcional)\n \"publicKey\": \"hex\", // Identidade para reputação/pagamentos\n \"lightningAddress\": \"[email protected]\" // Incentivos futuros\n }\n]\n```\n\n#### `PEER_REGISTERED` (Relay -\u003e Client)\nConfirmação de registro e atribuição de ID de sessão.\n```json\n[\n \"PEER_REGISTERED\",\n {\n \"peer_id\": \"uuid-v4\",\n \"heartbeat_interval\": 30000,\n \"status\": \"registered\"\n }\n]\n```\n\n### 3.2 Sincronização de Cache\n\n#### `PEER_CACHE_HAVE` (Client -\u003e Relay)\nInforma ao relay quais eventos o cliente possui em seu cache local para que o relay possa rotear requisições.\n```json\n[\n \"PEER_CACHE_HAVE\",\n {\n \"events\": [\n { \"id\": \"hex\", \"pubkey\": \"hex\", \"kind\": 1, \"created_at\": 162... },\n ...\n ]\n }\n]\n```\n\n### 3.3 Descoberta e Signaling (Smart Routing)\n\n#### `PEER_OFFER` (Relay -\u003e Client)\nEnviado pelo relay quando um cliente faz um `REQ` e o relay identifica que Super Peers possuem os dados.\n```json\n[\n \"PEER_OFFER\",\n {\n \"subscription\": \"sub_id_123\",\n \"offers\": {\n \"event_id_abc\": [\"peer_id_1\", \"peer_id_2\"],\n \"event_id_def\": [\"peer_id_3\"]\n },\n \"source\": \"smart_routing\"\n }\n]\n```\n\n#### `PEER_SIGNAL` (Client -\u003e Relay -\u003e Client)\nEncapsula o handshake WebRTC (SDP/ICE Candidates). O Relay apenas repassa.\n```json\n[\n \"PEER_SIGNAL\",\n {\n \"target_peer\": \"uuid-do-outro-peer\",\n \"signal_data\": { \"type\": \"offer/answer\", \"sdp\": \"...\" } // ou ice candidate\n }\n]\n```\n\n---\n\n## 4. Comunicação P2P (Peer \u003c-\u003e Peer via WebRTC)\n\nUma vez aberto o **WebRTC Data Channel**, os peers trocam mensagens binárias ou JSON diretamente.\n\n### 4.1 Requisição de Evento (`P2P_REQUEST`)\n```json\n{\n \"type\": \"P2P_REQUEST\",\n \"event_id\": \"hex-id-do-evento\"\n}\n```\n\n### 4.2 Entrega de Evento (`P2P_EVENTS`)\n```json\n{\n \"type\": \"P2P_EVENTS\",\n \"event\": {\n \"id\": \"...\",\n \"pubkey\": \"...\",\n \"sig\": \"...\",\n \"content\": \"...\",\n ...\n }\n}\n```\n\n---\n\n## 5. Algoritmos e Lógica de Implementação (LLM Guide)\n\n### 5.1 O Algoritmo de \"Smart Routing\" (No Relay)\nAo receber uma mensagem `[\"REQ\", sub_id, filter]`:\n1. **Processamento Padrão:** O relay deve buscar no banco de dados local e enviar eventos via WebSocket (compatibilidade NIP-01).\n2. **Interseção de Cache:** O relay consulta seu `CacheTracker` (um mapa em memória de `event_id -\u003e Set\u003cpeer_id\u003e`).\n3. **Seleção de Peers:** Se o filtro do `REQ` bater com eventos indexados no `CacheTracker`:\n - Selecione até 3 Super Peers online para cada evento.\n - Envie `PEER_OFFER` ao cliente solicitante.\n\n### 5.2 O Processo de Promoção (Super Peer)\nO Relay deve manter um loop de monitoramento (ex: a cada 1 min):\n- Se `peer.uptime \u003e 30min` AND `peer.reputation \u003e threshold` AND `peer.cache_size \u003e 0`:\n - Envie `[\"PEER_PROMOTED\", { \"peer_id\": \"...\" }]`.\n- Se o peer desconectar ou falhar em responder heartbeats:\n - Remova-o de todos os índices de `CacheTracker`.\n\n### 5.3 O Handshake WebRTC (Signaling)\n1. **Peer B (Requester)** recebe `PEER_OFFER` contendo `Peer A`.\n2. **Peer B** gera um SDP Offer e envia ao Relay: `[\"PEER_SIGNAL\", { target: \"Peer A\", signal: offer }]`.\n3. **Relay** localiza a conexão WebSocket de **Peer A** e repassa: `[\"PEER_SIGNAL_RELAY\", { from: \"Peer B\", signal: offer }]`.\n4. **Peer A** gera um SDP Answer e envia ao Relay: `[\"PEER_SIGNAL\", { target: \"Peer B\", signal: answer }]`.\n5. **Relay** repassa ao **Peer B**.\n6. Conexão P2P estabelecida via STUN/TURN.\n\n---\n\n## 6. Segurança e Integridade\n\n1. **Assinaturas Schnorr:** Clientes **OBRIGATORIAMENTE** devem validar a assinatura (`sig`) de cada evento recebido via P2P usando a biblioteca `noble-curves` ou similar. Se o ID do evento ou a assinatura forem inválidos, o peer remetente deve ser desconectado e reportado.\n2. **Reputação de Peer:**\n - O Relay deve manter um score de reputação no Redis/DB.\n - `+1` para cada evento validado e entregue (reportado via `PEER_STATS`).\n - `-100` (banimento) para envio de assinaturas falsas.\n - Decay temporal: Reputação diminui se o peer ficar offline.\n3. **Privacidade:** O uso de WebRTC expõe o endereço IP entre os peers. Clientes devem solicitar consentimento do usuário antes de ativar o modo P2P (\"Enable P2P Acceleration\").\n\n---\n\n## 7. Extensão NIP-11 (Relay Information)\n\nRelays compatíveis devem anunciar os limites do protocolo:\n\n```json\n{\n \"supported_nips\": [1, 11, 95],\n \"limitation\": {\n \"p2p_enabled\": true,\n \"p2p_max_connections_per_peer\": 10,\n \"p2p_heartbeat_ms\": 30000\n }\n}\n```\n\n---\n\n## 8. Pseudocódigo para Implementação em LLM\n\n### Handler de Mensagem (Servidor)\n```typescript\nfunction handleMessage(client, message) {\n const [type, payload] = message;\n switch(type) {\n case \"PEER_REGISTER\":\n return registerPeer(client, payload);\n case \"PEER_CACHE_HAVE\":\n return updateCacheTracker(client.id, payload.events);\n case \"PEER_SIGNAL\":\n const target = getClientById(payload.target_peer);\n if (target) {\n send(target, [\"PEER_SIGNAL_RELAY\", { from: client.id, signal: payload.signal_data }]);\n }\n break;\n case \"REQ\":\n handleStandardNip01(client, payload);\n triggerSmartRouting(client, payload); // NIP-95 logic\n break;\n }\n}\n```\n\n### Handler de Recebimento P2P (Cliente)\n```javascript\npeer.on('data', data =\u003e {\n const msg = JSON.parse(data);\n if (msg.type === 'P2P_EVENTS') {\n if (nostr.verifySignature(msg.event)) {\n displayEvent(msg.event);\n localCache.save(msg.event);\n } else {\n reportMaliciousPeer(msg.peer_id);\n }\n }\n});\n```\n\n---\n\n## 9. Fluxograma de Decisão (LLM Reasoning)\n\n1. **Input:** Filtro Nostr (ex: `{ \"kinds\": [1], \"limit\": 10 }`).\n2. **Relay Query:** Busca no DB local (Fast).\n3. **P2P Query:** Verifica se algum Super Peer enviou `PEER_CACHE_HAVE` recentemente com `kind: 1`.\n4. **Action:** Envia resultados do DB via WebSocket + `PEER_OFFER` para conectar aos Super Peers que têm mais eventos do mesmo tipo, visando futuras atualizações.\n5. **Result:** O cliente recebe os primeiros 10 eventos via WebSocket (baixa latência) e estabelece P2P para o fluxo contínuo (sustentabilidade).",
"sig": "f05a3226845cae304c874d4def02dd15e083e88894328242362a61794bfbe0e6f28f971e979e09e56941d816841a0918e4405cf902458a8f9e4d6e1e39def38f"
}