Dépannage
Les erreurs les plus courantes — ports, permissions, TLS, NIP manquants, publication et délais d’attente — avec des correctifs étape par étape.
Trois choses à vérifier en premier :
nostrfy checkvalide votre configuration (la plupart des erreurs sont des erreurs de configuration).tail -f nostrfy.logaffiche le journal — la cause s’y trouve presque toujours.nostrfy restartredémarre le démon proprement.
Impossible de démarrer
error: cannot bind to 0.0.0.0:80: Permission denied
Cause : Le port 80 ne peut être lié que par root.
Correctif : Exécutez avec sudo, ou changez le port pour un port comme 8080.
# Modifiez port = 8080 dans le fichier de configuration, puis :
nostrfy --config nostrfy.toml starterror: cannot bind to ...: Address already in use
Cause : Un autre processus (un ancien nostrfy ou un autre serveur) utilise déjà le port.
Correctif :
ss -tlnp | grep :8080# Si nostrfy tourne, redémarrez-le
nostrfy --config nostrfy.toml restartalready running (pid 1234); use 'nostrfy stop' or 'nostrfy restart'
Cause : nostrfy est déjà en cours d’exécution ; start refuse de démarrer
une seconde instance.
Correctif : Utilisez nostrfy restart, ou utilisez simplement l’instance en cours d’exécution.
nostrfy stop se bloque / did not stop in time
Cause : Le démon est bloqué ou ne répond pas.
Correctif :
ps aux | grep nostrfy
kill -9 <PID>
# Supprimez le fichier pid obsolète si présent
rm -f nostrfy.piderror: invalid nostrfy.toml: TOML parse error
Cause : Le fichier de configuration n’est pas du TOML valide. Erreurs courantes : oublier les guillemets autour d’une chaîne, ou écrire deux fois la même clé.
Correctif : Le message d’erreur inclut un numéro de ligne. Vérifiez et corrigez cette ligne.
# Exemples corrects
name = "my relay" # les chaînes sont citées avec "
port = 8080 # les nombres sont simples
enabled_nips = [1, 50] # les listes sont entre [ ]error: cannot read nostrfy.toml: No such file or directory
Cause : Le fichier de configuration n’existe pas.
Correctif :
nostrfy --config nostrfy.toml initerror: relay.private_key is not a valid secp256k1 secret key
Cause : relay.private_key n’est pas une clé hexadécimale valide de 64
caractères.
Correctif : Exécutez nostrfy genkey pour générer une clé correcte (ou
définissez private_key = "").
Beaucoup d’avertissements dans le journal au démarrage
Les lignes de journal [WARN] signalent des problèmes de configuration. Les principales :
| Avertissement | Signification et correctif |
|---|---|
relay.public_url is empty and server.host is "0.0.0.0"... | public_url n’est pas défini — l’authentification NIP-42, l’effacement NIP-62
et l’authentification admin NIP-98 ne fonctionneront pas. Définissez wss://your-public-url. |
relay.private_key is empty while NIP-29 is enabled... | Les groupes ont besoin d’une clé secrète. Exécutez nostrfy genkey. |
unknown config key [relay].software is ignored | Une clé héritée inutilisée (ou une faute de frappe) dans la configuration. Vérifiez le nom de la clé. |
unknown config section [serve] is ignored | Une faute de frappe dans un nom de section (p. ex. [serve] au lieu de [server]). Corrigez-la. |
relay.require_auth is true but relay.send_auth_challenge is false... | Cette combinaison bloque tout le monde. Changez l’un des deux. |
relay.require_pow = 64 ... practically unmineable | L’exigence de preuve de travail est si élevée que personne ne peut publier. Réduisez require_pow. |
Impossible de se connecter ou comportement étrange
Le client reçoit connection refused
Cause : Le relais n’est pas en cours d’exécution, ou un pare-feu bloque le port.
Correctif :
curl http://127.0.0.1:8080/health
# Depuis l’extérieur (avec l’IP/port du serveur)
curl http://YOUR_SERVER_IP:8080/health
# Vérifiez le pare-feu (exemple : ufw)
sudo ufw status
# Ouvrez le port si nécessaire
sudo ufw allow 8080Les clients externes ne peuvent pas se connecter, les clients locaux le peuvent
Cause : server.host est toujours 127.0.0.1 (la valeur par
défaut), qui n’accepte que les connexions locales.
Correctif : Définissez host = "0.0.0.0" dans la configuration et
redémarrez.
Impossible de se connecter via un tunnel Cloudflare
Lors de l’utilisation de Cloudflare Tunnel :
- Le relais fonctionne en HTTP simple ; Cloudflare termine TLS, donc les clients utilisent
wss://. Définissezpublic_url = "wss://..."sur le relais (cela fait fonctionner l’authentification NIP-42). - Cloudflare ajoute un en-tête
X-Forwarded-Proto. nostrfy traite les valeursws/wss/http/httpsde la même façon, donc aucune configuration supplémentaire n’est normalement nécessaire.
error: message too large et la connexion se ferme
Cause : Un seul message dépasse max_ws_message_bytes (1 Mo par défaut).
Correctif : Augmentez limits.max_ws_message_bytes si vous avez besoin
d’événements plus volumineux — mais vérifiez aussi les limites propres du client.
Erreurs too many subscriptions / too many filters
Cause : Les plafonds par connexion ont été atteints (abonnements 20 par défaut, filtres 20 par défaut).
Correctif : Augmentez limits.max_subscriptions / limits.max_filters (et vérifiez les paramètres du client).
Les nouvelles connexions sont refusées sous charge
Cause : max_connections (10000 par défaut) a été atteint, le plafond par
IP (max_connections_per_ip, 64 par défaut) s’est déclenché, ou la limite de débit de
connexion par seconde et par IP (max_connections_per_sec_per_ip) a refusé la rafale. Ces
plafonds s’appliquent à chaque connexion — WebSocket comme HTTP simple.
Correctif : Vérifiez et ajustez les paramètres. max_connections_per_ip = 0 désactive le plafond par IP ; max_connections_per_sec_per_ip = 0 désactive la limite de
débit. Ces trois paramètres nécessitent un redémarrage.
Les connexions se coupent après un moment
Cause : Si ws_idle_timeout_secs est défini, les connexions inactives sont
fermées. Les clients sains répondent au PING du relais par un PONG et restent connectés ; seuls les
pairs morts sont supprimés.
Correctif : C’est intentionnel — la valeur par défaut est de 300 secondes. Définissez ws_idle_timeout_secs = 0 pour le désactiver entièrement.
Un abonnement se termine par CLOSED ... response too large
Cause : Les événements stockés d’un REQ ont dépassé max_req_response_bytes (32 Mo par défaut). Cela n’arrive qu’avec des événements très
volumineux ou des filtres très larges.
Correctif : Restreignez le filtre (since/until plus serrés, un limit plus bas) ou augmentez max_req_response_bytes (0 désactive le budget).
Un NIP manque dans la liste supported_nips de NIP-11
Cause : La liste annoncée est dynamique — un NIP est masqué lorsque tous les kinds
qu’il définit sont rejetés : ils sont tous dans blocked_kinds, aucun d’eux n’est dans allowed_kinds, ou ce sont des kinds éphémères rejetés par reject_ephemeral.
NIP-29/43/66 exigent en outre relay.private_key et NIP-86 exige rpc.management_token ou rpc.admin_pubkey.
Correctif : Vérifiez les listes d’accès actives — listallowedkinds de
NIP-86 affiche la liste d’autorisation des kinds, et GET / affiche immédiatement le supported_nips effectif. Supprimez le kind bloquant ou le paramètre reject_ephemeral.
Erreurs lors de la publication
Lorsque la publication échoue, le 4e élément du message OK explique pourquoi. Les plus
courants :
| Erreur | Signification et correctif |
|---|---|
invalid: signature verification failed | La signature de l’événement est invalide (peut-être une clé client défectueuse). |
invalid: content too large | Le contenu dépasse max_content_bytes (64K caractères par défaut).
Raccourcissez-le ou augmentez la limite. |
invalid: too many tags | Plus de tags que max_tags (2000 par défaut). |
invalid: event creation date is in the future | Horodatage trop loin dans le futur (au-delà de max_created_at_future_secs). |
mute: event contains secret key material | Le contenu ou les tags contiennent une chaîne ressemblant à nsec. Ne publiez jamais de clés secrètes. Supprimez la chaîne et l’événement sera accepté. |
duplicate: event already stored | Le même événement est déjà stocké (normal). |
blocked: pubkey not allowed | La clé publique est bannie (banpubkey) ou hors de la liste d’autorisation. |
blocked: kind not allowed | Ce kind n’est pas autorisé. |
rate-limited: too many events | La clé publique a dépassé max_events_per_min_per_pubkey (fenêtre glissante de
60 secondes). Attendez une minute et réessayez, ou augmentez/désactivez la limite. |
blocked: event has been banned | L’identifiant de l’événement est banni. |
blocked: event has been deleted | Republication d’un événement supprimé. |
auth-required: ... | L’authentification est requise (quand relay.require_auth est activé). |
restricted: your account is too new | Le compte a été créé il y a moins de new_pubkey_min_age_secs. Attendez et
réessayez. |
restricted: unknown group | Le groupe n’existe pas (créez-le d’abord). |
restricted: this group is closed | Le groupe est closed ; les demandes d’adhésion sans code d’invitation ne sont
pas honorées. |
Serveur de fichiers Blossom
L’envoi échoue avec 401
L’événement d’autorisation d’envoi (kind 24242) a été rejeté. Vérifiez que :
- le tag
expirationdu token est présent et défini à un horodatage unix dans le futur, - pour upload/media/delete, le token porte un tag
xavec le sha256 du blob, - le tag
server(le cas échéant) nomme exactement leblossom.hostconfiguré (nom d’hôte uniquement, sans schéma/chemin), - le token a été signé au cours des 10 dernières minutes (fenêtre de fraîcheur contre le rejeu),
- et la clé de signature est bien celle de l’expéditeur.
L’envoi échoue avec 403
blossom.restrict_uploads = true est défini et la clé publique n’est pas sur la liste
d’autorisation — ajoutez-la avec nostrfy blossom allow npub1... (le démon recharge
automatiquement). Si la liste semble incorrecte, nostrfy blossom list l’affiche.
L’envoi échoue avec 409
Le client a envoyé un en-tête X-SHA-256 qui ne correspond pas au corps réel de la requête
(le hash déclaré a été calculé sur des octets différents — p. ex. le fichier a changé entre le hachage
et l’envoi). Les clients peuvent omettre entièrement l’en-tête.
GET / sur l’hôte média sert le document NIP-11
La requête n’a pas atteint le relais avec l’en-tête Blossom Host. Pointez media.example.com (ou la valeur définie pour blossom.host) vers le même port
dans le proxy inverse, puis nostrfy restart.
Un blob renvoie 404 juste après l’envoi
Le fichier est adressé par son SHA-256 : récupérez-le via le hash exact renvoyé dans la réponse
d’envoi (/<sha256> ou /<sha256>.<ext>). Une discordance
signifie que le client a demandé un hash différent des octets envoyés.
Recherche, groupes et authentification
La recherche renvoie 0 résultat / des résultats inattendus
La recherche nostrfy correspond à des mots entiers. Notez que :
search = "rust"correspond aux événements contenant le mot « rust », mais"ru"ne correspond PAS à « rust » comme sous-chaîne.- Seuls les mots du contenu de l’événement sont recherchés.
- Si
search_index = false, la recherche fonctionne toujours mais est plus lente. - Si NIP-50 est désactivé (
disabled_nips = [50]),searchest ignoré (un NOTICE est envoyé).
Les métadonnées de groupe (39000-39005) ne sont pas générées
Cause : relay.private_key n’est pas défini. Les instantanés de groupe sont
signés par la propre clé du relais, donc sans elle rien n’est généré.
Correctif :
nostrfy --config nostrfy.toml genkey
nostrfy --config nostrfy.toml restartrestricted: unknown group rejette les événements de groupe
Cause : Le groupe n’existe pas. Dans NIP-29, les événements de modération et les demandes d’adhésion (9021) ne peuvent pas cibler un groupe avant sa création (kind 9007).
Correctif : Créez d’abord le groupe avec un événement 9007.
restricted: you are not an admin of this group
Cause : La modération (ajout de membres, etc.) exige un administrateur (un membre avec un rôle). Le créateur est administrateur.
Correctif : Demandez à un administrateur de vous accorder un rôle, ou créez votre propre groupe.
restricted: this group is closed
Cause : Le groupe est closed ; les demandes d’adhésion sans code
d’invitation ne sont pas approuvées automatiquement.
Correctif : Demandez à un administrateur un code d’invitation (9009) et rejoignez avec un tag code.
Quitté accidentellement un groupe, ou le groupe n’a plus d’administrateurs
Cause : Les demandes de départ NIP-29 (kind 9022) sont honorées pour tout membre — y compris le dernier administrateur du groupe, qui ne laisse aucun administrateur derrière lui. Sans administrateur, plus personne ne peut envoyer d’événements de modération (9000/9001/9002/9008).
Correctif : Signez un événement de modération avec la propre clé du relais
(relay.private_key, la clé publique annoncée comme self de NIP-11). Selon
NIP-29, les événements de modération peuvent venir de « la clé maîtresse du relais ou ... des
administrateurs du groupe », donc le relais accepte la modération de groupe signée par sa propre clé
même quand le groupe n’a pas d’administrateurs. Par exemple, restaurez un administrateur avec un kind:9000 :
{
"kind": 9000,
"pubkey": "<relay self pubkey>",
"tags": [["h", "<group-id>"], ["p", "<member-hex>", "admin"]]
}Signez-le et publiez-le avec la clé du relais. Vous pouvez aussi supprimer le groupe avec un kind:9008 signé par le relais (ses événements stockés sont purgés) puis le recréer avec kind:9007. Cette récupération exige que relay.private_key soit configuré.
Les événements protégés sont rejetés avec auth-required
Cause : Les événements protégés NIP-70 (avec un tag -) ne peuvent être
publiés que par l’auteur authentifié sur la même connexion.
Correctif : Activez l’authentification NIP-42 dans le client avant de publier.
AUTH (NIP-42) renvoie false
Causes courantes :
relay.public_urln’est pas défini ou est incorrect — le tagrelayde l’événement AUTH ne correspond pas à l’URL du relais. Définissezwss://...et redémarrez.- Challenge obsolète — vous avez envoyé AUTH sur une autre connexion, ou réutilisé un ancien challenge.
- L’horloge du client est décalée — le
created_atde l’événement AUTH doit être à ±10 minutes de l’heure actuelle.
L’API de gestion NIP-86 renvoie 401 unauthorized
Cause : Identifiants manquants ou incorrects.
Correctif :
- Définissez
management_tokenet envoyezAuthorization: Bearer <token>. - Ou définissez
admin_pubkeyet envoyez un événement d’authentification NIP-98 (le tagudoit correspondre exactement à l’URL du relais ; un tagpayloadest requis). - Si aucun des deux n’est défini, l’API de gestion est entièrement désactivée.
Les événements d’authentification NIP-98 sont rejetés pour un schéma ou un port différent
La spécification NIP-98 dit que le tag u doit être exactement identique à l’URL
absolue de la requête, donc nostrfy dérive l’URL attendue de relay.public_url : son autorité
plus le schéma HTTP mappé depuis le schéma WebSocket (wss:// → https://, ws:// → http://, nostr+ retiré). Sans public_url, le relais attend le simple http://host:port qu’il sert. Un tag avec
un autre schéma, un port différent/omis, ou un chemin ou une requête différents est rejeté — définissez relay.public_url à l’adresse publique que les clients signent. Chaque événement
d’authentification est aussi à usage unique : rejouer le même en-tête Authorization pendant sa fenêtre de validité de 60 secondes est refusé.
Base de données et disque
database map is full: increase database.max_map_size
Cause : Le plafond de la projection mémoire LMDB (1 To d’espace d’adressage virtuel par défaut ; l’utilisation réelle du disque croît avec les données) a été atteint — en pratique, la base de données est pleine.
Correctif : Augmentez database.max_map_size et redémarrez.
disk is full: refusing to commit N events
Cause : Moins de 32 Mo d’espace disque libre. Les écritures s’arrêtent (pour protéger les données) ; les lectures continuent.
Correctif : Libérez de l’espace disque. Les écritures reprennent automatiquement une
fois l’espace disponible. (df -h /path/to/data)
nostrfy check signale map_size must not exceed max_map_size
Cause : database.map_size est supérieur à max_map_size.
Correctif : Définissez map_size à une valeur égale ou inférieure à max_map_size (les valeurs par défaut conviennent).
Vérifier la taille de la base de données
curl http://127.0.0.1:8080/relay/stats
# => "db_size_bytes" en octetsSauvegarder / déplacer la base de données
Toutes les données se trouvent dans le répertoire database.path. Arrêtez le relais avant de copier (copier une base de données active peut la
corrompre).
nostrfy --config nostrfy.toml stop
cp -a ./data ./data-backup
# Sauvegardez aussi [blossom].local_path avec le stockage Blossom local.
nostrfy --config nostrfy.toml startFonctionnement du démon
nostrfy stats indique nostrfy is not running (no stats file)
Cause : Le fichier de statistiques n’existe pas — le démon n’est pas en cours d’exécution, ou il a démarré il y a moins de quelques secondes.
Correctif : Exécutez nostrfy start, attendez quelques secondes, puis réessayez.
Le journal grossit sans limite
Cause : max_log_size_bytes vaut 0 (rotation désactivée).
Correctif : Définissez max_log_size_bytes = 52428800 (50 Mo) et max_log_files = 5. La rotation est automatique.
Les modifications de la configuration ne prennent pas effet après le rechargement
Cause : Vous avez rechargé (SIGHUP) des paramètres fixés au démarrage : private_key, api_host, metrics_enabled, les paramètres LiveKit et
les listes d’activation/désactivation des NIP.
Correctif : Utilisez nostrfy restart. Le journal contient un avertissement
« a restart is required » dans ce cas.
Le relais ne cesse de s’arrêter tout seul
Cause : La machine a redémarré, ou le relais a manqué de mémoire (OOM).
Correctif :
- Vérifiez la fin du journal :
tail -50 nostrfy.log. - Vérifiez si la machine a redémarré :
uptime(une disponibilité très courte signifie un redémarrage). - Vérifiez la mémoire :
free -h. - Redémarrez le relais :
nostrfy start.
systemd ne peut pas démarrer le relais sur le port 80
Un service systemd exécuté en tant que root peut lier le port 80. Si vous définissez User= à un utilisateur ordinaire, utilisez soit un port plus élevé (p. ex. 8080), soit
ajoutez AmbientCapabilities=CAP_NET_BIND_SERVICE à l’unité.
Toujours pas résolu ?
- Vérifiez le journal :
tail -100 nostrfy.log— il nomme généralement la cause directe. - Revalidez la configuration :
nostrfy check— affiche les avertissements et les erreurs. - Rassemblez les détails de reproduction : ce que vous faisiez, quel client, quelle erreur exacte.
- Demandez dans le dépôt du projet : https://github.com/iqbqioza/nostrfy — lors du dépôt d’un ticket, incluez les étapes de reproduction et le journal.