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

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.

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": "user@domain.com" // 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]:

  1. Processamento Padrão: O relay deve buscar no banco de dados local e enviar eventos via WebSocket (compatibilidade NIP-01).
  2. Interseção de Cache: O relay consulta seu CacheTracker (um mapa em memória de event_id -> Set<peer_id>).
  3. Seleção de Peers: Se o filtro do REQ bater com eventos indexados no CacheTracker:
    • Selecione até 3 Super Peers online para cada evento.
    • Envie PEER_OFFER ao 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 > 30min AND peer.reputation > threshold AND peer.cache_size > 0:
    • Envie ["PEER_PROMOTED", { "peer_id": "..." }].
  • Se o peer desconectar ou falhar em responder heartbeats:
    • Remova-o de todos os índices de CacheTracker.

5.3 O Handshake WebRTC (Signaling)

  1. Peer B (Requester) recebe PEER_OFFER contendo Peer A.
  2. Peer B gera um SDP Offer e envia ao Relay: ["PEER_SIGNAL", { target: "Peer A", signal: offer }].
  3. Relay localiza a conexão WebSocket de Peer A e repassa: ["PEER_SIGNAL_RELAY", { from: "Peer B", signal: offer }].
  4. Peer A gera um SDP Answer e envia ao Relay: ["PEER_SIGNAL", { target: "Peer B", signal: answer }].
  5. Relay repassa ao Peer B.
  6. Conexão P2P estabelecida via STUN/TURN.

6. Segurança e Integridade

  1. 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.
  2. Reputação de Peer:
    • O Relay deve manter um score de reputação no Redis/DB.
    • +1 para cada evento validado e entregue (reportado via PEER_STATS).
    • -100 (banimento) para envio de assinaturas falsas.
    • Decay temporal: Reputação diminui se o peer ficar offline.
  3. 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)

  1. Input: Filtro Nostr (ex: { "kinds": [1], "limit": 10 }).
  2. Relay Query: Busca no DB local (Fast).
  3. P2P Query: Verifica se algum Super Peer enviou PEER_CACHE_HAVE recentemente com kind: 1.
  4. 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.
  5. Result: O cliente recebe os primeiros 10 eventos via WebSocket (baixa latência) e estabelece P2P para o fluxo contínuo (sustentabilidade).

Write a comment
No comments yet.