Solução de problemas

Os erros mais prováveis — portas, permissões, TLS, NIP ausentes, publicação e tempos de espera — com correções passo a passo.

Três coisas para verificar primeiro:

  • nostrfy check valida sua configuração (a maioria dos erros são erros de configuração).
  • tail -f nostrfy.log mostra o log — a causa está quase sempre lá.
  • nostrfy restart reinicia o daemon de forma limpa.

Não é possível iniciar

error: cannot bind to 0.0.0.0:80: Permission denied

Causa: A porta 80 só pode ser vinculada pelo root.

Correção: Execute com sudo ou mude a porta para algo como 8080.

sh
# Troque port = 8080 no arquivo de configuração, depois:
nostrfy --config nostrfy.toml start

error: cannot bind to ...: Address already in use

Causa: Outro processo (um nostrfy antigo ou um servidor diferente) já está usando a porta.

Correção:

sh
ss -tlnp | grep :8080
sh
# Se o nostrfy já está rodando, reinicie
nostrfy --config nostrfy.toml restart

already running (pid 1234); use 'nostrfy stop' or 'nostrfy restart'

Causa: o nostrfy já está em execução; start se recusa a iniciar uma segunda instância.

Correção: Use nostrfy restart ou apenas use a instância em execução.

nostrfy stop trava / did not stop in time

Causa: O daemon está travado ou não está respondendo.

Correção:

sh
ps aux | grep nostrfy
kill -9 <PID>
# Remova o arquivo pid obsoleto, se existir
rm -f nostrfy.pid

error: invalid nostrfy.toml: TOML parse error

Causa: O arquivo de configuração não é um TOML válido. Erros comuns: esquecer aspas em uma string ou escrever a mesma chave duas vezes.

Correção: A mensagem de erro inclui um número de linha. Verifique e corrija essa linha.

toml
# Exemplos corretos
name = "my relay"        # strings usam aspas duplas
port = 8080              # números vão sem aspas
enabled_nips = [1, 50]   # listas ficam entre [ ]

error: cannot read nostrfy.toml: No such file or directory

Causa: O arquivo de configuração não existe.

Correção:

sh
nostrfy --config nostrfy.toml init

error: relay.private_key is not a valid secp256k1 secret key

Causa: relay.private_key não é uma chave hexadecimal válida de 64 caracteres.

Correção: Execute nostrfy genkey para gerar uma chave correta (ou defina private_key = "").

Muitos avisos no log ao iniciar

Linhas de log [WARN] indicam problemas de configuração. As principais:

AvisoSignificado e correção
relay.public_url is empty and server.host is "0.0.0.0"...public_url não está definido — autenticação NIP-42, vanish NIP-62 e autenticação admin NIP-98 não funcionarão. Defina wss://your-public-url.
relay.private_key is empty while NIP-29 is enabled...Grupos precisam de uma chave secreta. Execute nostrfy genkey.
unknown config key [relay].software is ignoredUma chave legada não utilizada (ou um erro de digitação) na configuração. Verifique o nome da chave.
unknown config section [serve] is ignoredUm erro de digitação no nome de uma seção (por ex. [serve] em vez de [server]). Corrija.
relay.require_auth is true but relay.send_auth_challenge is false...Esta combinação bloqueia todo mundo. Altere uma das duas.
relay.require_pow = 64 ... practically unmineableO requisito de PoW é tão alto que ninguém consegue publicar. Reduza require_pow.

Não conecta ou se comporta de forma estranha

O cliente recebe connection refused

Causa: O relay não está em execução ou um firewall está bloqueando a porta.

Correção:

sh
curl http://127.0.0.1:8080/health

# De fora (usando o IP/porta do servidor)
curl http://YOUR_SERVER_IP:8080/health

# Verifique o firewall (exemplo: ufw)
sudo ufw status
# Abra a porta se preciso
sudo ufw allow 8080

Clientes externos não conectam, os locais conectam

Causa: server.host ainda é 127.0.0.1 (o padrão), que aceita apenas conexões locais.

Correção: Defina host = "0.0.0.0" na configuração e reinicie.

Não é possível conectar através de um túnel Cloudflare

Ao usar o Cloudflare Tunnel:

  • O relay executa HTTP simples; a Cloudflare termina o TLS, então os clientes usam wss://. Defina public_url = "wss://..." no relay (isso faz a autenticação NIP-42 funcionar).
  • A Cloudflare adiciona um cabeçalho X-Forwarded-Proto. O nostrfy trata valores ws/wss/http/https da mesma forma, então normalmente nenhuma configuração extra é necessária.

error: message too large e a conexão é fechada

Causa: Uma única mensagem excede max_ws_message_bytes (padrão 1 MB).

Correção: Aumente limits.max_ws_message_bytes se precisar de eventos maiores — mas verifique também os limites do próprio cliente.

Erros too many subscriptions / too many filters

Causa: Os limites por conexão foram atingidos (assinaturas padrão 20, filtros padrão 20).

Correção: Aumente limits.max_subscriptions / limits.max_filters (e verifique as configurações do cliente).

Novas conexões são recusadas sob carga

Causa: max_connections (padrão 10000) foi atingido, o limite por IP (max_connections_per_ip, padrão 64) foi acionado, ou o limite de taxa de conexão por segundo (max_connections_per_sec_per_ip) recusou o pico. Os limites se aplicam a todas as conexões — WebSocket e HTTP simples igualmente.

Correção: Revise e ajuste as configurações. max_connections_per_ip = 0 desativa o limite por IP; max_connections_per_sec_per_ip = 0 desativa o limite de taxa. Essas três configurações exigem reinicialização.

Conexões caem depois de um tempo

Causa: Se ws_idle_timeout_secs estiver definido, conexões ociosas são fechadas. Clientes saudáveis respondem ao PING do relay com um PONG e permanecem conectados; apenas pares mortos são removidos.

Correção: Isso é intencional — o padrão é 300 segundos. Defina ws_idle_timeout_secs = 0 para desativar completamente.

Uma assinatura termina com CLOSED ... response too large

Causa: Os eventos armazenados de um REQ excederam max_req_response_bytes (padrão 32 MiB). Só acontece com eventos muito grandes ou filtros muito amplos.

Correção: Restrinja o filtro (since/until mais apertados, um limit menor) ou aumente max_req_response_bytes (0 desativa o orçamento).

Um NIP está ausente da lista NIP-11 supported_nips

Causa: A lista anunciada é dinâmica — um NIP fica oculto quando todos os kinds que ele define são rejeitados: todos estão em blocked_kinds, nenhum deles está em allowed_kinds, ou são kinds efêmeros rejeitados por reject_ephemeral. NIP-29/43/66 exigem adicionalmente relay.private_key e NIP-86 exige rpc.management_token ou rpc.admin_pubkey.

Correção: Verifique as listas de acesso ativas — NIP-86 listallowedkinds mostra a lista de kinds permitidos, e GET / mostra o supported_nips efetivo imediatamente. Remova o kind bloqueador ou a configuração reject_ephemeral.

Erros ao publicar

Quando a publicação falha, o 4º elemento da mensagem OK explica o motivo. Os comuns:

ErroSignificado e correção
invalid: signature verification failedA assinatura do evento é inválida (possivelmente uma chave de cliente quebrada).
invalid: content too largeO conteúdo excede max_content_bytes (padrão 64K caracteres). Encurte-o ou aumente o limite.
invalid: too many tagsMais tags do que max_tags (padrão 2000).
invalid: event creation date is in the futureTimestamp muito no futuro (além de max_created_at_future_secs).
mute: event contains secret key materialO conteúdo ou as tags contêm uma string com aparência de nsec. Nunca publique chaves secretas. Remova a string e o evento será aceito.
duplicate: event already storedO mesmo evento já está armazenado (normal).
blocked: pubkey not allowedA pubkey está banida (banpubkey) ou fora da lista de permissão.
blocked: kind not allowedEste kind não é permitido.
rate-limited: too many eventsA pubkey excedeu max_events_per_min_per_pubkey (janela deslizante de 60 segundos). Aguarde um minuto e tente novamente, ou aumente/desative o limite.
blocked: event has been bannedO id do evento está banido.
blocked: event has been deletedRepublicação de um evento excluído.
auth-required: ...Autenticação é necessária (quando relay.require_auth está ativado).
restricted: your account is too newA conta foi criada dentro de new_pubkey_min_age_secs. Aguarde e tente novamente.
restricted: unknown groupO grupo não existe (crie-o primeiro).
restricted: this group is closedO grupo é closed; solicitações de participação sem código de convite não são atendidas.

Servidor de arquivos Blossom

Upload falha com 401

O evento de autorização de upload (kind 24242) foi rejeitado. Verifique se:

  • a tag expiration do token está presente e definida como um timestamp unix no futuro,
  • para upload/media/delete o token carrega uma tag x com o sha256 do blob,
  • a tag server (quando presente) nomeia exatamente o blossom.host configurado (somente hostname, sem esquema/caminho),
  • o token foi assinado nos últimos 10 minutos (uma janela de frescor contra replay),
  • e a chave de assinatura é do próprio uploader.

Upload falha com 403

blossom.restrict_uploads = true está definido e a pubkey não está na lista de permissão — adicione-a com nostrfy blossom allow npub1... (o daemon recarrega automaticamente). Se a lista parecer errada, nostrfy blossom list a mostra.

Upload falha com 409

O cliente enviou um cabeçalho X-SHA-256 que não corresponde ao corpo real da requisição (o hash declarado foi calculado sobre bytes diferentes — por ex. o arquivo mudou entre o cálculo do hash e o envio). Os clientes podem omitir o cabeçalho completamente.

GET / no host de mídia serve o documento NIP-11

A requisição não chegou ao relay com o cabeçalho Blossom Host. Aponte media.example.com (ou qualquer que seja o blossom.host definido) para a mesma porta no proxy reverso, então nostrfy restart.

Um blob retorna 404 logo após o upload

O arquivo é endereçado pelo conteúdo via seu SHA-256: busque-o pelo hash exato retornado na resposta do upload (/<sha256> ou /<sha256>.<ext>). Uma incompatibilidade significa que o cliente solicitou um hash diferente dos bytes que enviou.

Busca, grupos e autenticação

A busca retorna 0 resultados / resultados inesperados

A busca do nostrfy corresponde a palavras inteiras. Observe que:

  • search = "rust" corresponde a eventos contendo a palavra "rust", mas "ru" NÃO corresponde a "rust" como substring.
  • Apenas palavras no conteúdo do evento são pesquisadas.
  • Se search_index = false, a busca ainda funciona, mas é mais lenta.
  • Se o NIP-50 estiver desativado (disabled_nips = [50]), search é ignorado (um NOTICE é enviado).

Metadados de grupo (39000-39005) não são gerados

Causa: relay.private_key não está definido. Os snapshots de grupo são assinados pela própria chave do relay, então sem ela nada é gerado.

Correção:

sh
nostrfy --config nostrfy.toml genkey
nostrfy --config nostrfy.toml restart

restricted: unknown group rejeita eventos de grupo

Causa: O grupo não existe. No NIP-29, eventos de moderação e solicitações de participação (9021) não podem visar um grupo antes que ele seja criado (kind 9007).

Correção: Crie o grupo primeiro com um evento 9007.

restricted: you are not an admin of this group

Causa: A moderação (adicionar membros, etc.) exige um admin (um membro com uma função). O criador é um admin.

Correção: Peça a um admin para conceder uma função a você ou crie seu próprio grupo.

restricted: this group is closed

Causa: O grupo é closed; solicitações de participação sem código de convite não são aprovadas automaticamente.

Correção: Peça a um admin um código de convite (9009) e participe com uma tag code.

Saiu acidentalmente de um grupo, ou o grupo não tem admins

Causa: Solicitações de saída NIP-29 (kind 9022) são atendidas para qualquer membro — incluindo o último admin do grupo, que não deixa admins para trás. Sem admin, ninguém pode enviar eventos de moderação (9000/9001/9002/9008) mais.

Correção: Assine um evento de moderação com a própria chave do relay (relay.private_key, a pubkey anunciada como self do NIP-11). Pelo NIP-29, eventos de moderação podem vir da "chave mestra do relay ou ... dos admins do grupo", então o relay aceita moderação de grupo assinada pela própria chave mesmo quando o grupo não tem admins. Por exemplo, restaure um admin com um kind:9000:

json
{
  "kind": 9000,
  "pubkey": "<relay self pubkey>",
  "tags": [["h", "<group-id>"], ["p", "<member-hex>", "admin"]]
}

Assine e publique com a chave do relay. Alternativamente, exclua o grupo com um kind:9008 assinado pelo relay (seus eventos armazenados são purgados) e recrie-o com kind:9007. Esta recuperação precisa que relay.private_key esteja configurado.

Eventos protegidos são rejeitados com auth-required

Causa: Eventos protegidos NIP-70 (com tag -) só podem ser publicados pelo autor autenticado na mesma conexão.

Correção: Ative a autenticação NIP-42 no cliente antes de publicar.

AUTH (NIP-42) retorna false

Causas comuns:

  1. relay.public_url não definido ou errado — a tag relay do evento AUTH não corresponde à URL do relay. Defina wss://... e reinicie.
  2. Challenge expirado — você enviou AUTH em uma conexão diferente ou reutilizou um challenge antigo.
  3. O relógio do cliente está errado — o created_at do evento AUTH deve estar dentro de ±10 minutos de agora.

API de gerenciamento NIP-86 retorna 401 unauthorized

Causa: Credenciais ausentes ou erradas.

Correção:

  • Defina management_token e envie Authorization: Bearer <token>.
  • Ou defina admin_pubkey e envie um evento de autenticação NIP-98 (a tag u deve corresponder exatamente à URL do relay; uma tag payload é necessária).
  • Se nenhum estiver definido, a API de gerenciamento está totalmente desativada.

Eventos de autenticação NIP-98 são rejeitados por esquema ou porta diferentes

A especificação NIP-98 diz que a tag u deve ser exatamente igual à URL absoluta da requisição, então o nostrfy deriva a URL esperada de relay.public_url: sua autoridade mais o esquema HTTP mapeado do esquema WebSocket (wss:// → https://, ws:// → http://, nostr+ removido). Sem public_url o relay espera o simples http://host:port que serve. Uma tag com outro esquema, uma porta diferente/omitida, ou um caminho ou query diferente é rejeitada — defina relay.public_url para o endereço público que os clientes assinam. Cada evento de autenticação também é de uso único: repetir o mesmo cabeçalho Authorization dentro de sua janela de validade de 60 segundos é recusado.

Banco de dados e disco

database map is full: increase database.max_map_size

Causa: O teto do memory-map do LMDB (padrão 1 TB de espaço de endereço virtual; o uso real de disco cresce com os dados) foi atingido — efetivamente, o banco de dados está cheio.

Correção: Aumente database.max_map_size e reinicie.

disk is full: refusing to commit N events

Causa: Menos de 32 MB de espaço livre em disco. As escritas param (para proteger os dados); as leituras continuam.

Correção: Libere espaço em disco. As escritas são retomadas automaticamente quando houver espaço disponível. (df -h /path/to/data)

nostrfy check relata map_size must not exceed max_map_size

Causa: database.map_size é maior que max_map_size.

Correção: Defina map_size igual ou abaixo de max_map_size (os padrões estão bons).

Verificando o tamanho do banco de dados

sh
curl http://127.0.0.1:8080/relay/stats
# => "db_size_bytes" em bytes

Fazer backup / mover o banco de dados

Todos os dados ficam no diretório database.path. Pare o relay antes de copiar (copiar um banco de dados ativo pode corrompê-lo).

sh
nostrfy --config nostrfy.toml stop
cp -a ./data ./data-backup
# Faça também backup de [blossom].local_path ao usar Blossom local.
nostrfy --config nostrfy.toml start

Operação do daemon

nostrfy stats diz nostrfy is not running (no stats file)

Causa: O arquivo de stats não existe — o daemon não está em execução ou iniciou há menos de alguns segundos.

Correção: Execute nostrfy start, aguarde alguns segundos e tente novamente.

O log cresce sem parar

Causa: max_log_size_bytes é 0 (rotação desativada).

Correção: Defina max_log_size_bytes = 52428800 (50 MB) e max_log_files = 5. A rotação é automática.

Mudanças na configuração não têm efeito após recarregar

Causa: Você recarregou (SIGHUP) configurações que são fixas na inicialização: private_key, api_host, metrics_enabled, configurações LiveKit e as listas de ativação/desativação de NIPs.

Correção: Use nostrfy restart. O log contém um aviso "a restart is required" neste caso.

O relay continua morrendo sozinho

Causa: A máquina reiniciou ou o relay ficou sem memória (OOM).

Correção:

  1. Verifique o final do log: tail -50 nostrfy.log.
  2. Verifique se a máquina reiniciou: uptime (um uptime muito curto significa reinicialização).
  3. Verifique a memória: free -h.
  4. Inicie o relay novamente: nostrfy start.
Dica
Para iniciar o nostrfy automaticamente na inicialização, registre-o como um serviço systemd com o comando de inicialização do relay como ExecStart.

systemd não consegue iniciar o relay na porta 80

Um serviço systemd executando como root pode vincular a porta 80. Se você definiu User= para um usuário comum, use uma porta mais alta (por ex. 8080) ou adicione AmbientCapabilities=CAP_NET_BIND_SERVICE à unit.

Ainda não resolveu?

  1. Verifique o log: tail -100 nostrfy.log — geralmente indica a causa direta.
  2. Revalide a configuração: nostrfy check — mostra avisos e erros.
  3. Reúna detalhes de reprodução: o que você estava fazendo, qual cliente, qual erro exato.
  4. Pergunte no repositório do projeto: https://github.com/iqbqioza/nostrfy — ao abrir uma issue, inclua as etapas de reprodução e o log.