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 check valide votre configuration (la plupart des erreurs sont des erreurs de configuration).
  • tail -f nostrfy.log affiche le journal — la cause s’y trouve presque toujours.
  • nostrfy restart redé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.

sh
# Modifiez port = 8080 dans le fichier de configuration, puis :
nostrfy --config nostrfy.toml start

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

Cause : Un autre processus (un ancien nostrfy ou un autre serveur) utilise déjà le port.

Correctif :

sh
ss -tlnp | grep :8080
sh
# Si nostrfy tourne, redémarrez-le
nostrfy --config nostrfy.toml restart

already 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 :

sh
ps aux | grep nostrfy
kill -9 <PID>
# Supprimez le fichier pid obsolète si présent
rm -f nostrfy.pid

error: 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.

toml
# 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 :

sh
nostrfy --config nostrfy.toml init

error: 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 :

AvertissementSignification 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 ignoredUne 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 ignoredUne 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 unmineableL’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 :

sh
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 8080

Les 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éfinissez public_url = "wss://..." sur le relais (cela fait fonctionner l’authentification NIP-42).
  • Cloudflare ajoute un en-tête X-Forwarded-Proto. nostrfy traite les valeurs ws/wss/http/https de 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 :

ErreurSignification et correctif
invalid: signature verification failedLa signature de l’événement est invalide (peut-être une clé client défectueuse).
invalid: content too largeLe contenu dépasse max_content_bytes (64K caractères par défaut). Raccourcissez-le ou augmentez la limite.
invalid: too many tagsPlus de tags que max_tags (2000 par défaut).
invalid: event creation date is in the futureHorodatage trop loin dans le futur (au-delà de max_created_at_future_secs).
mute: event contains secret key materialLe 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 storedLe même événement est déjà stocké (normal).
blocked: pubkey not allowedLa clé publique est bannie (banpubkey) ou hors de la liste d’autorisation.
blocked: kind not allowedCe kind n’est pas autorisé.
rate-limited: too many eventsLa 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 bannedL’identifiant de l’événement est banni.
blocked: event has been deletedRepublication d’un événement supprimé.
auth-required: ...L’authentification est requise (quand relay.require_auth est activé).
restricted: your account is too newLe compte a été créé il y a moins de new_pubkey_min_age_secs. Attendez et réessayez.
restricted: unknown groupLe groupe n’existe pas (créez-le d’abord).
restricted: this group is closedLe 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 expiration du token est présent et défini à un horodatage unix dans le futur,
  • pour upload/media/delete, le token porte un tag x avec le sha256 du blob,
  • le tag server (le cas échéant) nomme exactement le blossom.host configuré (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]), search est 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 :

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

restricted: 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 :

json
{
  "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 :

  1. relay.public_url n’est pas défini ou est incorrect — le tag relay de l’événement AUTH ne correspond pas à l’URL du relais. Définissez wss://... et redémarrez.
  2. Challenge obsolète — vous avez envoyé AUTH sur une autre connexion, ou réutilisé un ancien challenge.
  3. L’horloge du client est décalée — le created_at de 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_token et envoyez Authorization: Bearer <token>.
  • Ou définissez admin_pubkey et envoyez un événement d’authentification NIP-98 (le tag u doit correspondre exactement à l’URL du relais ; un tag payload est 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

sh
curl http://127.0.0.1:8080/relay/stats
# => "db_size_bytes" en octets

Sauvegarder / 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).

sh
nostrfy --config nostrfy.toml stop
cp -a ./data ./data-backup
# Sauvegardez aussi [blossom].local_path avec le stockage Blossom local.
nostrfy --config nostrfy.toml start

Fonctionnement 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 :

  1. Vérifiez la fin du journal : tail -50 nostrfy.log.
  2. Vérifiez si la machine a redémarré : uptime (une disponibilité très courte signifie un redémarrage).
  3. Vérifiez la mémoire : free -h.
  4. Redémarrez le relais : nostrfy start.
Astuce
Pour démarrer nostrfy automatiquement au démarrage, enregistrez-le comme service systemd avec la commande de démarrage du relais comme ExecStart.

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 ?

  1. Vérifiez le journal : tail -100 nostrfy.log — il nomme généralement la cause directe.
  2. Revalidez la configuration : nostrfy check — affiche les avertissements et les erreurs.
  3. Rassemblez les détails de reproduction : ce que vous faisiez, quel client, quelle erreur exacte.
  4. 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.