Risoluzione dei problemi

Gli errori più probabili — porte, permessi, TLS, NIP mancanti, pubblicazione e timeout — con soluzioni passo passo.

Tre cose da controllare prima:

  • nostrfy check convalida la configurazione (la maggior parte degli errori sono errori di configurazione).
  • tail -f nostrfy.log mostra il log — la causa è quasi sempre lì.
  • nostrfy restart riavvia il demone in modo pulito.

Impossibile avviare

error: cannot bind to 0.0.0.0:80: Permission denied

Causa: Solo l’utente root può fare bind sulla porta 80.

Correzione: Esegui con sudo oppure cambia la porta con una come 8080.

sh
# Cambia port = 8080 nel file di configurazione, poi:
nostrfy --config nostrfy.toml start

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

Causa: Un altro processo (un vecchio nostrfy o un altro server) sta già usando la porta.

Correzione:

sh
ss -tlnp | grep :8080
sh
# Se nostrfy è in esecuzione, riavvialo
nostrfy --config nostrfy.toml restart

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

Causa: nostrfy è già in esecuzione; start si rifiuta di avviare una seconda istanza.

Correzione: Usa nostrfy restart oppure usa semplicemente l’istanza in esecuzione.

nostrfy stop si blocca / did not stop in time

Causa: Il demone è bloccato o non risponde.

Correzione:

sh
ps aux | grep nostrfy
kill -9 <PID>
# Rimuovi il file pid obsoleto se presente
rm -f nostrfy.pid

error: invalid nostrfy.toml: TOML parse error

Causa: Il file di configurazione non è TOML valido. Errori comuni: dimenticare le virgolette intorno a una stringa o scrivere due volte la stessa chiave.

Correzione: Il messaggio di errore include un numero di riga. Controlla e correggi quella riga.

toml
# Esempi corretti
name = "my relay"        # le stringhe vanno tra virgolette con "
port = 8080              # i numeri vanno senza virgolette
enabled_nips = [1, 50]   # le liste vanno tra [ ]

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

Causa: Il file di configurazione non esiste.

Correzione:

sh
nostrfy --config nostrfy.toml init

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

Causa: relay.private_key non è una chiave esadecimale valida di 64 caratteri.

Correzione: Esegui nostrfy genkey per generare una chiave corretta (oppure imposta private_key = "").

Molti avvisi nel log all’avvio

Le righe di log [WARN] segnalano problemi di configurazione. Le principali:

AvvisoSignificato e soluzione
relay.public_url is empty and server.host is "0.0.0.0"...public_url non è impostato — l’autenticazione NIP-42, NIP-62 vanish e l’autenticazione admin NIP-98 non funzioneranno. Imposta wss://your-public-url.
relay.private_key is empty while NIP-29 is enabled...I gruppi hanno bisogno di una chiave segreta. Esegui nostrfy genkey.
unknown config key [relay].software is ignoredUna chiave legacy inutilizzata (o un refuso) nella configurazione. Controlla il nome della chiave.
unknown config section [serve] is ignoredUn refuso nel nome di una sezione (es. [serve] invece di [server]). Correggilo.
relay.require_auth is true but relay.send_auth_challenge is false...Questa combinazione blocca fuori tutti. Cambia una delle due.
relay.require_pow = 64 ... practically unmineableIl requisito PoW è così alto che nessuno può pubblicare. Abbassa require_pow.

Impossibile connettersi o comportamento anomalo

Il client riceve connection refused

Causa: Il relay non è in esecuzione oppure un firewall sta bloccando la porta.

Correzione:

sh
curl http://127.0.0.1:8080/health

# Dall'esterno (usando IP/porta del server)
curl http://YOUR_SERVER_IP:8080/health

# Controlla il firewall (esempio: ufw)
sudo ufw status
# Apri la porta se necessario
sudo ufw allow 8080

I client esterni non si connettono, quelli locali sì

Causa: server.host è ancora 127.0.0.1 (il valore predefinito), che accetta solo connessioni locali.

Correzione: Imposta host = "0.0.0.0" nella configurazione e riavvia.

Impossibile connettersi tramite un tunnel Cloudflare

Quando si usa Cloudflare Tunnel:

  • Il relay funziona in HTTP in chiaro; Cloudflare termina TLS, quindi i client usano wss://. Imposta public_url = "wss://..." sul relay (così l’autenticazione NIP-42 funziona).
  • Cloudflare aggiunge un’intestazione X-Forwarded-Proto. nostrfy tratta i valori ws/wss/http/https allo stesso modo, quindi normalmente non è necessaria alcuna configurazione aggiuntiva.

error: message too large e la connessione si chiude

Causa: Un singolo messaggio supera max_ws_message_bytes (predefinito 1 MB).

Correzione: Aumenta limits.max_ws_message_bytes se ti servono eventi più grandi — ma controlla anche i limiti del client.

Errori too many subscriptions / too many filters

Causa: Sono stati raggiunti i limiti per connessione (sottoscrizioni predefinite 20, filtri predefiniti 20).

Correzione: Aumenta limits.max_subscriptions / limits.max_filters (e controlla le impostazioni del client).

Le nuove connessioni vengono rifiutate sotto carico

Causa: È stato raggiunto max_connections (predefinito 10000), è scattato il limite per IP (max_connections_per_ip, predefinito 64), oppure il limite di connessioni al secondo per IP (max_connections_per_sec_per_ip) ha rifiutato il picco. I limiti si applicano a ogni connessione — WebSocket e HTTP in chiaro allo stesso modo.

Correzione: Rivedi e regola le impostazioni. max_connections_per_ip = 0 disabilita il limite per IP; max_connections_per_sec_per_ip = 0 disabilita il limite di frequenza. Queste tre impostazioni richiedono un riavvio.

Le connessioni cadono dopo un po’

Causa: Se ws_idle_timeout_secs è impostato, le connessioni inattive vengono chiuse. I client sani rispondono al PING del relay con un PONG e restano connessi; solo i peer morti vengono rimossi.

Correzione: È intenzionale — il valore predefinito è 300 secondi. Imposta ws_idle_timeout_secs = 0 per disabilitarlo del tutto.

Una sottoscrizione termina con CLOSED ... response too large

Causa: Gli eventi memorizzati di una REQ hanno superato max_req_response_bytes (predefinito 32 MiB). Succede solo con eventi molto grandi o filtri molto ampi.

Correzione: Restringi il filtro (since/until più stretti, un limit più basso) oppure aumenta max_req_response_bytes (0 disabilita il budget).

Un NIP manca dall’elenco NIP-11 supported_nips

Causa: L’elenco annunciato è dinamico — un NIP viene nascosto quando tutti i kind che definisce sono rifiutati: sono tutti in blocked_kinds, nessuno di essi è in allowed_kinds, oppure sono kind effimeri rifiutati da reject_ephemeral. NIP-29/43/66 richiedono inoltre relay.private_key e NIP-86 richiede rpc.management_token o rpc.admin_pubkey.

Correzione: Controlla le liste di accesso attive — NIP-86 listallowedkinds mostra la allowlist dei kind, e GET / mostra immediatamente il supported_nips effettivo. Rimuovi il kind bloccante o l’impostazione reject_ephemeral.

Errori durante la pubblicazione

Quando la pubblicazione fallisce, il 4° elemento del messaggio OK spiega il motivo. I più comuni:

ErroreSignificato e soluzione
invalid: signature verification failedLa firma dell’evento non è valida (forse una chiave client danneggiata).
invalid: content too largeIl contenuto supera max_content_bytes (predefinito 64K caratteri). Accorcialo oppure aumenta il limite.
invalid: too many tagsPiù tag rispetto a max_tags (predefinito 2000).
invalid: event creation date is in the futureTimestamp troppo nel futuro (oltre max_created_at_future_secs).
mute: event contains secret key materialIl contenuto o i tag contengono una stringa che assomiglia a un nsec. Non pubblicare mai chiavi segrete. Rimuovi la stringa e l’evento verrà accettato.
duplicate: event already storedLo stesso evento è già memorizzato (normale).
blocked: pubkey not allowedLa pubkey è bannata (banpubkey) o fuori dalla allowlist.
blocked: kind not allowedQuesto kind non è consentito.
rate-limited: too many eventsLa pubkey ha superato max_events_per_min_per_pubkey (finestra scorrevole di 60 secondi). Aspetta un minuto e riprova, oppure aumenta/disabilita il limite.
blocked: event has been bannedL’id dell’evento è bannato.
blocked: event has been deletedRipubblicazione di un evento eliminato.
auth-required: ...È richiesta l’autenticazione (quando relay.require_auth è attivo).
restricted: your account is too newL’account è stato creato entro new_pubkey_min_age_secs. Aspetta e riprova.
restricted: unknown groupIl gruppo non esiste (crealo prima).
restricted: this group is closedIl gruppo è closed; le richieste di partecipazione senza codice di invito non vengono accolte.

Server di file Blossom

Caricamento non riuscito con 401

L’evento di autorizzazione al caricamento (kind 24242) è stato rifiutato. Verifica che:

  • il tag expiration del token sia presente e impostato a un timestamp unix futuro,
  • per upload/media/delete il token contenga un tag x con lo sha256 del blob,
  • il tag server (quando presente) indichi esattamente il blossom.host configurato (solo hostname, senza schema/percorso),
  • il token sia stato firmato negli ultimi 10 minuti (una finestra di freschezza contro il replay),
  • e la chiave di firma sia quella dell’uploader stesso.

Caricamento non riuscito con 403

blossom.restrict_uploads = true è impostato e la pubkey non è nella allowlist — aggiungila con nostrfy blossom allow npub1... (il demone ricarica automaticamente). Se l’elenco sembra sbagliato, nostrfy blossom list lo mostra.

Caricamento non riuscito con 409

Il client ha inviato un’intestazione X-SHA-256 che non corrisponde al corpo effettivo della richiesta (l’hash dichiarato è stato calcolato su byte diversi — ad es. il file è cambiato tra il calcolo dell’hash e l’invio). I client possono omettere del tutto l’intestazione.

GET / sull’host multimediale serve il documento NIP-11

La richiesta non ha raggiunto il relay con l’intestazione Blossom Host. Punta media.example.com (o qualunque sia il blossom.host impostato) alla stessa porta nel reverse proxy, poi nostrfy restart.

Un blob dà 404 subito dopo il caricamento

Il file è indirizzato per contenuto tramite il suo SHA-256: recuperalo con l’hash esatto restituito nella risposta di caricamento (/<sha256> oppure /<sha256>.<ext>). Una mancata corrispondenza significa che il client ha richiesto un hash diverso dai byte inviati.

Ricerca, gruppi e autenticazione

La ricerca restituisce 0 risultati / risultati inattesi

La ricerca di nostrfy corrisponde a parole intere. Nota che:

  • search = "rust" corrisponde agli eventi contenenti la parola "rust", ma "ru" NON corrisponde a "rust" come sottostringa.
  • Vengono cercate solo le parole nel contenuto dell’evento.
  • Se search_index = false, la ricerca funziona comunque ma è più lenta.
  • Se NIP-50 è disabilitato (disabled_nips = [50]), search viene ignorato (viene inviato un NOTICE).

Metadati del gruppo (39000-39005) non generati

Causa: relay.private_key non è impostato. Gli snapshot dei gruppi sono firmati dalla chiave stessa del relay, quindi senza di essa non viene generato nulla.

Correzione:

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

restricted: unknown group rifiuta gli eventi di gruppo

Causa: Il gruppo non esiste. In NIP-29, gli eventi di moderazione e le richieste di partecipazione (9021) non possono fare riferimento a un gruppo prima che sia stato creato (kind 9007).

Correzione: Crea prima il gruppo con un evento 9007.

restricted: you are not an admin of this group

Causa: La moderazione (aggiunta di membri, ecc.) richiede un admin (un membro con un ruolo). Il creatore è un admin.

Correzione: Chiedi a un admin di assegnarti un ruolo oppure crea il tuo gruppo.

restricted: this group is closed

Causa: Il gruppo è closed; le richieste di partecipazione senza codice di invito non vengono approvate automaticamente.

Correzione: Chiedi a un admin un codice di invito (9009) e partecipa con un tag code.

Uscita accidentale da un gruppo, oppure il gruppo non ha admin

Causa: Le richieste di uscita NIP-29 (kind 9022) vengono onorate per qualsiasi membro — incluso l’ultimo admin del gruppo, che non lascia admin dietro di sé. Senza admin, nessuno può più inviare eventi di moderazione (9000/9001/9002/9008).

Correzione: Firma un evento di moderazione con la chiave stessa del relay (relay.private_key, la pubkey annunciata come self di NIP-11). Secondo NIP-29, gli eventi di moderazione possono venire dalla "chiave master del relay o ... dagli admin del gruppo", quindi il relay accetta la moderazione firmata dalla propria chiave anche quando il gruppo non ha admin. Per esempio, ripristina un admin con un kind:9000:

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

Firmalo e pubblicalo con la chiave del relay. In alternativa, elimina il gruppo con un kind:9008 firmato dal relay (i suoi eventi memorizzati vengono eliminati) e ricrealo con kind:9007. Questo ripristino richiede che relay.private_key sia configurato.

Gli eventi protetti vengono rifiutati con auth-required

Causa: Gli eventi protetti NIP-70 (con tag -) possono essere pubblicati solo dall’autore autenticato sulla stessa connessione.

Correzione: Abilita l’autenticazione NIP-42 nel client prima di pubblicare.

AUTH (NIP-42) restituisce false

Cause comuni:

  1. relay.public_url non impostato o errato — il tag relay dell’evento AUTH non corrisponde all’URL del relay. Imposta wss://... e riavvia.
  2. Challenge scaduta — hai inviato AUTH su una connessione diversa oppure hai riutilizzato una vecchia challenge.
  3. L’orologio del client è sballato — il created_at dell’evento AUTH deve essere entro ±10 minuti da adesso.

L’API di gestione NIP-86 restituisce 401 unauthorized

Causa: Credenziali mancanti o errate.

Correzione:

  • Imposta management_token e invia Authorization: Bearer <token>.
  • Oppure imposta admin_pubkey e invia un evento di autenticazione NIP-98 (il tag u deve corrispondere esattamente all’URL del relay; è richiesto un tag payload).
  • Se nessuno dei due è impostato, l’API di gestione è del tutto disabilitata.

Gli eventi di autenticazione NIP-98 vengono rifiutati per schema o porta diversi

La specifica NIP-98 dice che il tag u deve essere esattamente uguale all’URL assoluto della richiesta, quindi nostrfy deriva l’URL atteso da relay.public_url: la sua authority più lo schema HTTP mappato dallo schema WebSocket (wss:// → https://, ws:// → http://, nostr+ rimosso). Senza public_url il relay si aspetta il semplice http://host:port che serve. Un tag con un altro schema, una porta diversa/omessa, oppure un percorso o una query diversi viene rifiutato — imposta relay.public_url all’indirizzo pubblico firmato dai client. Ogni evento di autenticazione è inoltre monouso: ripetere la stessa intestazione Authorization entro la sua finestra di validità di 60 secondi viene rifiutato.

Database e disco

database map is full: increase database.max_map_size

Causa: Il limite di memory-map di LMDB (predefinito 1 TB di spazio di indirizzi virtuali; l’uso reale del disco cresce con i dati) è stato raggiunto — in pratica, il database è pieno.

Correzione: Aumenta database.max_map_size e riavvia.

disk is full: refusing to commit N events

Causa: Meno di 32 MB di spazio libero su disco. Le scritture si fermano (per proteggere i dati); le letture continuano.

Correzione: Libera spazio su disco. Le scritture riprendono automaticamente quando c’è spazio disponibile. (df -h /path/to/data)

nostrfy check riporta map_size must not exceed max_map_size

Causa: database.map_size è maggiore di max_map_size.

Correzione: Imposta map_size pari o inferiore a max_map_size (i valori predefiniti vanno bene).

Controllo della dimensione del database

sh
curl http://127.0.0.1:8080/relay/stats
# => "db_size_bytes" in byte

Backup / spostamento del database

Tutti i dati si trovano nella directory database.path. Ferma il relay prima di copiare (copiare un database attivo può corromperlo).

sh
nostrfy --config nostrfy.toml stop
cp -a ./data ./data-backup
# Fai anche il backup di [blossom].local_path se usi lo storage Blossom locale.
nostrfy --config nostrfy.toml start

Funzionamento del demone

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

Causa: Il file delle statistiche non esiste — il demone non è in esecuzione oppure è stato avviato da meno di qualche secondo.

Correzione: Esegui nostrfy start, aspetta qualche secondo e riprova.

Il log cresce senza limiti

Causa: max_log_size_bytes è 0 (rotazione disabilitata).

Correzione: Imposta max_log_size_bytes = 52428800 (50 MB) e max_log_files = 5. La rotazione è automatica.

Le modifiche alla configurazione non hanno effetto dopo il ricaricamento

Causa: Hai ricaricato (SIGHUP) impostazioni fissate all’avvio: private_key, api_host, metrics_enabled, impostazioni LiveKit e le liste di abilitazione/disabilitazione dei NIP.

Correzione: Usa nostrfy restart. Il log segnala in questo caso che «è necessario un riavvio».

Il relay continua a morire da solo

Causa: La macchina si è riavviata oppure il relay ha esaurito la memoria (OOM).

Correzione:

  1. Controlla la fine del log: tail -50 nostrfy.log.
  2. Controlla se la macchina si è riavviata: uptime (un uptime molto breve significa un riavvio).
  3. Controlla la memoria: free -h.
  4. Avvia di nuovo il relay: nostrfy start.
Suggerimento
Per avviare nostrfy automaticamente all’avvio, registralo come servizio systemd con il comando di avvio del relay come ExecStart.

systemd non riesce ad avviare il relay sulla porta 80

Un servizio systemd in esecuzione come root può associare la porta 80. Se hai impostato User= a un utente normale, usa una porta più alta (es. 8080) oppure aggiungi AmbientCapabilities=CAP_NET_BIND_SERVICE all’unità.

Ancora non risolto?

  1. Controlla il log: tail -100 nostrfy.log — di solito indica la causa diretta.
  2. Riconvalida la configurazione: nostrfy check — mostra avvisi ed errori.
  3. Raccogli i dettagli di riproduzione: cosa stavi facendo, quale client, quale errore esatto.
  4. Chiedi nel repository del progetto: https://github.com/iqbqioza/nostrfy — quando apri una issue, includi i passaggi di riproduzione e il log.