Bases
La configuration est un fichier TOML, par
défaut nommé nostrfy.toml. Créez-le avec init :
nostrfy --config nostrfy.toml init
Validez-le (recommandé avant chaque démarrage) :
nostrfy --config nostrfy.toml check
Chaque commande accepte --config <path> (par défaut nostrfy.toml).
Syntaxe générale :
[section]
key = "string"
key = 8080
key = [1, 2]
key = true
Sections de configuration
| Section | Objet |
|---|
[relay] | Identité, URLs et interrupteurs NIP |
[server] | Liaison réseau, séparation API, métriques |
[rpc] | RPC de gestion NIP-86 (auth, limite de corps) |
[limits] | Toutes les limites et protections contre la surcharge |
[database] | Stockage LMDB, index de recherche, plafonds de file |
[daemon] | Fichiers PID, journal et statistiques, et rotation |
[access] | Listes de contrôle d’accès initiales (modifiables à l’exécution) |
[blossom] | Serveur de fichiers Blossom (hébergement média) |
Chaque clé est optionnelle ; une clé manquante utilise sa valeur par défaut.
Section [relay] — identité du relais
| Clé | Type | Défaut | Description |
|---|
name | string | "nostrfy" | Nom du relais affiché aux clients via NIP-11 |
description | string | "A minimal and stable Nostr relay" | Description du relais (NIP-11) |
pubkey | string (64 hex) | "" | Clé publique de l’administrateur (champ pubkey NIP-11) |
contact | string | "" | URI de contact de l’administrateur (mailto: ou https://) |
icon | string | "" | URL de l’image d’icône du relais |
post_policy | string | "" | URL pointant vers la politique de publication du relais |
private_key | string (64 hex) | "" | Clé secrète propre au relais ; requise pour les groupes NIP-29 |
public_url | string | "" | URL publique, p. ex. wss://relay.example.com |
livekit_url | string | "" | URL du serveur LiveKit pour les salons audio/vidéo NIP-29 |
livekit_api_key | string | "" | Clé API LiveKit |
livekit_api_secret | string | "" | Secret API LiveKit (utilisé pour signer les JWT) |
livekit_participants_interval_secs | integer | 15 | Intervalle en secondes entre les rafraîchissements de la liste des participants LiveKit (l’événement 39004 de NIP-29) |
enabled_nips | array of integers | [] | Liste d’autorisation NIP explicite |
disabled_nips | array of integers | [] | NIPs à désactiver (ignoré quand enabled_nips est non vide) |
reject_ephemeral | boolean | false | Rejeter les événements éphémères NIP-01 (kinds 20000–29999) |
enabled_git | boolean | false | Accepter les événements git NIP-34 (kinds 1617–1633, 30617/30618) |
require_pow | integer | 0 | Preuve de travail requise en bits zéro initiaux |
new_pubkey_min_age_secs | integer | 0 | Refuser les événements des pubkeys plus jeunes que cette valeur (secondes ; 0 = désactivé) |
max_events_per_min_per_pubkey | integer | 0 | Limite de débit de publication par pubkey (par minute ; 0 = sans limite) |
max_groups | integer | 1000 | Plafond du magasin de groupes NIP-29 en mémoire |
require_auth | boolean | false | Exiger l’authentification NIP-42 pour REQ/EVENT/COUNT/NEG (les lectures via l’API REST exigent NIP-98) |
send_auth_challenge | boolean | true | Envoyer le défi AUTH à la connexion |
enabled_nip78_auth | boolean | true | Exiger AUTH NIP-42 avant d’accepter les événements kind 78/30078 |
enabled_command_events | boolean | false | Exécuter les commandes opérateur kind:1 rédigées par la pubkey admin |
Détails des clés
- private_key — clé secrète propre au relais, utilisée pour signer les événements générés par le relais : métadonnées
de groupes NIP-29 (39000–39005) et événements de rôles/membres NIP-43. Générez-la avec
nostrfy genkey ; gardez-la
secrète. Elle est lue une fois au démarrage, donc toute modification exige un redémarrage. Un redémarrage répare aussi les métadonnées de groupes stockées — les événements signés par l’ancienne clé sont supprimés et l’état actuel est republié sous la nouvelle clé (l’état des groupes lui-même est restauré tel quel, pas reconstruit). L’état des rôles NIP-43 (adhésions, rôles et codes d’invitation) est réparé de la même façon. - livekit_participants_interval_secs — fréquence (en secondes) à laquelle le relais rafraîchit les listes de participants NIP-29
kind:39004 en appelant l’API LiveKit Room Service (ListParticipants) avec les identifiants configurés. Les webhooks LiveKit envoyés à /.well-known/nip29/livekit/webhook rafraîchissent immédiatement la liste d’un groupe ; le sondage réconcilie les événements manqués. Doit être au moins 1. Figé au démarrage — exige un redémarrage. - public_url — utilisée pour valider les tags porteurs d’URL des clients : AUTH NIP-42, vanish NIP-62 et
auth admin NIP-98. Quand elle est vide, le relais se replie sur
host:port, qui ne correspond jamais à une vraie
URL client lors de la liaison à 0.0.0.0 ou 127.0.0.1 (un avertissement est journalisé). Définissez-la toujours. - enabled_nips vs disabled_nips — la liste d’autorisation l’emporte : quand
enabled_nips est
non vide, seuls ses NIPs sont annoncés et disabled_nips est ignoré. Les deux exigent un redémarrage. - reject_ephemeral — les kinds 20000–29999 sont rejetés, mais les kinds exemptés que les NIPs exigent de
relayer sont toujours transmis : 22242, 27235, 28934/28935/28936, 24133, 23194/23195, 24242 et 21059. S’applique sur SIGHUP.
- enabled_git — opt-in NIP-34 : accepte les kinds 1617–1633 et 30617/30618 et annonce NIP-34.
Désactivé par défaut car les charges de patch peuvent être volumineuses. S’applique sur SIGHUP.
Section [server] — paramètres serveur
| Clé | Type | Défaut | Description |
|---|
host | string | "127.0.0.1" | Adresse de liaison ; 0.0.0.0 accepte les connexions de partout |
port | integer | 8080 | Port (1–65535) ; le port 80 exige root |
api_host | string | "" | Nom d’hôte dédié à l’API REST |
metrics_enabled | boolean | true | Servir les métriques Prometheus sur /metrics |
ws_paths | string | "root" | Chemins des points de terminaison WebSocket : root, inbox-outbox ou all |
inbox_write_policy | string | "any" | Qui peut écrire sur /inbox : « any » ou « relay » (les événements doivent toujours porter un tag p) |
outbox_write_policy | string | "any" | Qui peut écrire sur /outbox : « any » (propres événements de la pubkey authentifiée NIP-42) ou « relay » |
trusted_proxies | array of strings | [] | Adresses/CIDRs de reverse-proxy dont le X-Forwarded-For est de confiance (vide = aucun proxy de confiance) |
landing_page_file | string | "" | Fichier HTML affiché à un navigateur ouvrant l’URL du relais, à la place de la page leurre intégrée (vide = leurre) |
assets_dir | string | "" | Répertoire servi sous /assets/<name> pour la page d’accueil (vide = pas de route /assets/) |
Détails des clés
- host —
0.0.0.0 lie toutes les interfaces IPv4 ; 127.0.0.1 est local uniquement. - port — 1–65535 ; le port 80 exige root. Ce port unique sert ensemble le relais WebSocket, le document NIP-11, l’API REST et le RPC NIP-86.
- api_host — dédie l’API REST à un seul nom d’hôte pour que l’API et le relais puissent partager un
port derrière un reverse-proxy. Figé au démarrage — exige un redémarrage.
- ws_paths —
root sert uniquement /, inbox-outbox sert uniquement /inbox et
/outbox, all sert les deux. Figé au démarrage — exige un redémarrage. - trusted_proxies — listez uniquement les propres adresses du proxy (loopback pour nginx/Caddy sur le même
hôte, plage source de l’équilibreur dans le cloud). Si défini, l’IP client est dérivée de la dernière entrée
X-Forwarded-For non de confiance pour les plafonds par IP, la limite de débit, blockip et les logs. N’ajoutez jamais
une adresse joignable directement par les clients — ils pourraient falsifier l’en-tête et contourner les limites par IP.
Figé au démarrage — exige un redémarrage. - landing_page_file — par défaut, un navigateur ouvrant l’URL du relais reçoit une page leurre intégrée qui ne révèle rien sur le relais. Pointez ceci vers un fichier HTML pour servir votre propre page (règles du relais, contact, statistiques) ; le document NIP-11 et les connexions WebSocket ne sont pas affectés. Le fichier est lu à la demande et relu lorsqu’il change, donc une tâche externe peut le régénérer sans redémarrage. Il doit être un fichier régulier d’au plus 8 MiB (les liens symboliques ne sont pas suivis) ; s’il manque, la page leurre est servie. S’applique au SIGHUP.
- assets_dir — répertoire dont les fichiers sont servis sous
/assets/<name> (images, CSS, JS de la page d’accueil). Seuls des noms plats en ASCII (lettres, chiffres, ., _, -) sont servis (pas de fichiers cachés, sous-répertoires ni liens symboliques) ; les extensions inconnues sont envoyées en application/octet-stream. Les fichiers sont lus à chaque requête et peuvent donc être remplacés sur place. Figé au démarrage — exige un redémarrage.
Section [rpc] — gestion NIP-86
| Clé | Type | Défaut | Description |
|---|
management_token | string | "" | Jeton Bearer pour les API de gestion |
admin_pubkey | string (64 hex) | "" | Pubkey administrateur pour l’auth de gestion NIP-98 |
max_admin_body_bytes | integer | 65536 | Limite de corps pour le RPC de gestion NIP-86 |
Le RPC NIP-86 est monté sur les routes publiques POST / du relais — il n’y a pas de port de gestion séparé. management_token et admin_pubkey apparaissent parfois sous [server] dans les anciens guides ; ces orthographes sont des alias historiques de ces clés [rpc].
Section [limits] — limites et protections
Connexions et messages
| Clé | Type | Défaut | Description |
|---|
max_connections | integer | 10000 | Connexions simultanées maximales |
max_connections_per_ip | integer | 64 | Max. de connexions par IP source |
max_ws_message_bytes | integer | 1048576 | Max. d’octets par message/frame WebSocket |
socket_recv_buffer_kb | integer | 64 | Tampon de réception noyau par connexion (Kio) |
max_out_queue_bytes | integer | 262144 | Plafond de file sortante par connexion (octets) |
ws_idle_timeout_secs | integer | 300 | Fermer les connexions inactives après ce délai |
http_read_timeout_secs | integer | 30 | Délai d’en-tête de requête HTTP (défense slow-loris) |
max_connections_per_sec_per_ip | integer | 0 | Max. de nouvelles connexions par seconde par IP source |
Abonnements et requêtes
| Clé | Type | Défaut | Description |
|---|
max_filters | integer | 20 | Max. de filtres par REQ |
max_subscriptions | integer | 20 | Max. d’abonnements par connexion |
max_limit | integer | 500 | Plafond pour la limite REQ |
max_count | integer | 2000 | Plafond pour les résultats COUNT |
max_sub_id_len | integer | 64 | Longueur max. d’id d’abonnement (caractères, pas octets) |
max_sub_bytes | integer | 1048576 | Total d’octets de filtres d’abonnement par connexion |
max_req_response_bytes | integer | 33554432 (32 Mo) | Plafond du total d’octets qu’une réponse REQ peut envoyer ; les réponses qui tiennent dans la file sortante sont livrées en entier ; les pages de requête de l’API REST appliquent le même budget |
Événements
| Clé | Type | Défaut | Description |
|---|
max_content_bytes | integer | 65536 | Longueur max. du contenu d’événement en caractères |
max_tags | integer | 2000 | Max. de tags par événement |
max_tag_value_bytes | integer | 1024 | Max. d’octets par valeur de tag |
max_created_at_future_secs | integer | 3600 | Écart futur toléré de created_at |
group_late_publish_secs | integer | 3600 | Retard toléré pour les événements admin de groupes NIP-29 (secondes) |
max_neg_items | integer | 100000 | Max. d’enregistrements par synchronisation negentropy NIP-77 |
Alias historiques : limits.require_pow, limits.new_pubkey_min_age_secs et limits.max_indexed_words sont toujours acceptés comme alias de relay.require_pow, relay.new_pubkey_min_age_secs et database.max_indexed_words.
API REST
| Clé | Type | Défaut | Description |
|---|
max_api_concurrent | integer | 8 | Max. de requêtes /api/v1 simultanées |
max_api_limit | integer | 5000 | Plafond pour le paramètre limit de l’API |
max_api_offset | integer | 50000 | Plafond pour le paramètre offset de l’API |
max_api_fetch | integer | 55001 | Fenêtre de sur-récupération max. pour les requêtes offset — doit couvrir max_api_offset + max_api_limit + 1 (0 = sans borne) |
max_api_search_bytes | integer | 2048 | Max. d’octets du paramètre search de l’API |
Diffusion live
| Clé | Type | Défaut | Description |
|---|
live_batch_interval_ms | integer | 20 | Fréquence de vidage des événements live (ms) |
live_batch_size | integer | 32 | Max. d’événements par lot live |
live_buffer | integer | 65536 | Taille de la file de diffusion live ; au-delà de 10 000 000, refusé au check/démarrage ; au-delà de 1 000 000, avertissement |
Section [database] — base de données
| Clé | Type | Défaut | Description |
|---|
path | string | "./data" | Répertoire de la base de données (LMDB) |
max_dbs | integer | 32 | Max. de bases nommées LMDB |
max_readers | integer | 128 | Max. de lecteurs simultanés LMDB |
map_size | integer | 1073741824 (1 Go) | Plancher pour la taille du memory map (octets) |
max_map_size | integer | 1099511627776 (1 TB) | Plafond du memory-map (octets) |
purge_interval_secs | integer | 300 | Intervalle de purge NIP-40 (secondes) |
search_index | boolean | true | Activer l’index de mots NIP-50 |
reader_threads | integer | 2 | Threads de scan dédiés |
max_indexed_words | integer | 32 | Mots du contenu de chaque événement indexés pour la recherche |
meta_index | boolean | true | Écrire l’en-tête de métadonnées par événement utilisé par le préfiltre de scan |
disabled_fsync | boolean | false | Ignorer le vidage disque synchrone après chaque lot d’écriture |
db_buffer_size | integer | 2048 | Tampon WebSocket initial par connexion (octets) ; doit rester sous la limite de tampon d’écriture dérivée de max_ws_message_bytes / max_neg_items (sinon refusé au check/démarrage) |
db_request_timeout_secs | integer | 30 | Durée d’attente possible d’une requête base avant échec |
max_db_queue_msgs | integer | 4096 | Max. de messages en attente avant échec rapide |
max_db_queue_events | integer | 262144 | Max. d’événements dans les lots en attente avant échec rapide |
max_db_queue_bytes | integer | 268435456 (256 MiB) | Max. d’octets de requêtes base en file avant échec rapide (0 = sans plafond d’octets) |
Détails des clés
- map_size — le plancher pour le memory map : la map est toujours ouverte au moins à cette taille.
- max_map_size — le plafond, ouvert comme réservation virtuelle éparse : le disque physique ne croît qu’avec
les données réellement écrites. Augmentez-le en cas de
database map is full. - search_index = false — la recherche fonctionne toujours (correspondance de mots entiers dans le contenu) mais les scans
sont plus lents ; sur un petit VPS cela divise la base par deux. Recommandé sur les petites instances.
- disabled_fsync — échange la durabilité contre le débit : les écritures sont validées dans le cache de pages OS et
une coupure de courant peut perdre les écritures les plus récentes.
Section [daemon] — démon
| Clé | Type | Défaut | Description |
|---|
pid_file | string | "./nostrfy.pid" | Chemin du fichier PID |
log_file | string | "./nostrfy.log" | Chemin du fichier de log ; créé accessible au propriétaire uniquement (0600) — il contient les IP et pubkeys des clients |
stats_file | string | "./nostrfy.stats.json" | Chemin du fichier de statistiques ; l’instantané est réservé au propriétaire (0600) et un sous-ensemble public est servi sur /relay/stats |
stats_interval_secs | integer | 5 | Intervalle d’écriture des statistiques (secondes) |
max_log_size_bytes | integer | 52428800 (50 Mo) | Taille de rotation des logs (0 = sans rotation) |
max_log_files | integer | 5 | Générations de logs pivotés à conserver |
Les chemins sont résolus par rapport au répertoire du fichier de configuration, donc ils restent valides après que le démon change son répertoire de travail.
/relay/stats ne demande aucun jeton et est lisible en cross-origin, il sert donc une vue plus étroite que le fichier de statistiques : l’espace disque libre de l’hôte et l’indicateur de disque plein, les compteurs d’échecs (db_errors, accept_errors, rebuild_failures, log_errors) et la répartition des refus de connexion par motif sont omis — ensemble, ils constitueraient un oracle de capacité de stockage pour un sondeur anonyme. Ces clés restent dans le fichier de statistiques (nostrfy stats les affiche) et dans /metrics. Notez que /metrics n’est pas authentifié non plus : isolez-le (api_host ou reverse-proxy) si les chiffres de capacité doivent rester privés.
Section [access] — contrôle d’accès
| Clé | Type | Défaut | Description |
|---|
restrict_relay | boolean | false | Seules les pubkeys autorisées peuvent publier |
blocked_kinds | array of integers | [] | Kinds à rejeter |
allowed_kinds | array of integers | [] | Liste d’autorisation de kinds ; seuls ces kinds sont acceptés quand elle est non vide |
blocked_ips | array of strings | [] | Adresses IP refusées au moment de la connexion |
method_grants | table: pubkey → array of strings | {} | Octrois de méthodes NIP-86 pour les pubkeys non-admin (gérés à l’exécution avec assignmethod) |
Les listes d’autorisation/refus de pubkeys ne sont pas des clés de config — elles vivent dans la base du relais (LMDB) et sont
gérées à l’exécution :
nostrfy relay allow npub1...
nostrfy relay deny npub1...
nostrfy relay list
- restrict_relay = true — seules les pubkeys autorisées peuvent publier, tandis que
la lecture reste ouverte à tous (tout client peut toujours s’abonner et récupérer).
- Une pubkey refusée est toujours rejetée à la publication et jamais servie à la lecture.
- method_grants — octrois de méthodes NIP-86 pour les pubkeys non-admin (pubkey → noms de méthodes, p. ex. un
modérateur autorisé avec
banevent et listbannedevents). Initialisés depuis la config au
premier lancement, puis gérés à l’exécution avec NIP-86 assignmethod/unassignmethod (inspectés avec listmethodassignees). Seules les méthodes de modération et de lecture sont octroyables —
la gestion des permissions, rôles, revendications d’invitation et identité du relais reste réservée à l’admin, et une pubkey bannie est
refusée même avec des octrois. Voir l' API de gestion.
Section [blossom] — serveur de fichiers Blossom
| Clé | Type | Défaut | Description |
|---|
host | string | "" | Nom d’hôte pour le serveur Blossom (vide = désactivé) |
storage | string | "local" | Backend : « local » (local_path) ou « s3 » (bucket compatible S3) |
local_path | string | "/var/lib/nostrfy/images" | Racine de stockage local pour les fichiers média |
max_upload_bytes | integer | 20971520 (20 Mo) | Taille max. de fichier média |
min_free_bytes | integer | 33554432 (32 Mo) | Espace disque sous lequel les téléversements sont refusés |
s3_endpoint | string | "" | Point de terminaison compatible S3 (p. ex. R2) |
s3_region | string | "" | Région S3 (R2 utilise « auto ») |
s3_bucket | string | "" | Nom du bucket S3 |
s3_access_key | string | "" | Clé d’accès S3 |
s3_secret_key | string | "" | Clé secrète S3 |
restrict_uploads | boolean | false | Seules les pubkeys autorisées peuvent téléverser |
Rechargement à l’exécution (SIGHUP)
Modifier le fichier et envoyer kill -HUP $(cat nostrfy.pid) recharge la configuration sans
redémarrage. La plupart des réglages prennent effet immédiatement ; quelques-uns sont figés au démarrage :
| S’applique sur SIGHUP | Exige un redémarrage |
|---|
| relay.name, description, pubkey, contact, icon, post_policy, public_url | relay.private_key |
| reject_ephemeral, enabled_git, enabled_nip78_auth | relay.livekit_*, enabled_nips / disabled_nips |
| la plupart des limites, landing_page_file | api_host, trusted_proxies, metrics_enabled, ws_paths, assets_dir, database.*, tailles du démon, plafonds de limites, blossom.* |
[access] n’est pas appliqué par un rechargement — les listes sont initialisées une fois au démarrage puis gérées à l’exécution via NIP-86.
Le log avertit quand un réglage exigeant un redémarrage a changé, et quelques réglages capturés au démarrage ne sont pas vérifiés par le
rechargement.
| Erreur | Correctif |
|---|
| public_url non défini | définissez wss://... |
| host laissé à 127.0.0.1 | les clients externes ne peuvent pas se connecter |
| private_key non définie avec NIP-29 | exécutez nostrfy genkey + redémarrage |
| restrict_relay true avec allowlist vide | tout le monde est verrouillé |
| clés exigeant un redémarrage modifiées avec seulement un SIGHUP | utilisez nostrfy restart |