Solución de problemas
Los errores más comunes — puertos, permisos, TLS, NIP faltantes, publicación y tiempos de espera — con soluciones paso a paso.
Tres cosas que comprobar primero:
nostrfy checkvalida tu configuración (la mayoría de los errores son errores de configuración).tail -f nostrfy.logmuestra el registro — la causa casi siempre está allí.nostrfy restartreinicia el demonio de forma limpia.
No se puede iniciar
error: cannot bind to 0.0.0.0:80: Permission denied
Causa: Solo root puede usar el puerto 80.
Solución: Ejecuta con sudo, o cambia el puerto a uno como 8080.
# Cambia port = 8080 en el archivo de configuración, luego:
nostrfy --config nostrfy.toml starterror: cannot bind to ...: Address already in use
Causa: Otro proceso (un nostrfy antiguo u otro servidor) ya está usando el puerto.
Solución:
ss -tlnp | grep :8080# Si nostrfy se está ejecutando, reinícialo
nostrfy --config nostrfy.toml restartalready running (pid 1234); use 'nostrfy stop' or 'nostrfy restart'
Causa: nostrfy ya se está ejecutando; start se niega a iniciar una
segunda instancia.
Solución: Usa nostrfy restart, o simplemente usa la instancia en ejecución.
nostrfy stop se bloquea / did not stop in time
Causa: El demonio está bloqueado o no responde.
Solución:
ps aux | grep nostrfy
kill -9 <PID>
# Elimina un archivo pid obsoleto si existe
rm -f nostrfy.piderror: invalid nostrfy.toml: TOML parse error
Causa: El archivo de configuración no es TOML válido. Errores comunes: olvidar las comillas alrededor de una cadena, o escribir dos veces la misma clave.
Solución: El mensaje de error incluye un número de línea. Revisa y corrige esa línea.
# Ejemplos correctos
name = "my relay" # las cadenas van entre comillas con "
port = 8080 # los números van sin comillas
enabled_nips = [1, 50] # las listas van entre [ ]error: cannot read nostrfy.toml: No such file or directory
Causa: El archivo de configuración no existe.
Solución:
nostrfy --config nostrfy.toml initerror: relay.private_key is not a valid secp256k1 secret key
Causa: relay.private_key no es una clave hexadecimal válida de 64
caracteres.
Solución: Ejecuta nostrfy genkey para generar una clave correcta (o
establece private_key = "").
Muchas advertencias en el registro al iniciar
Las líneas de registro [WARN] indican problemas de configuración. Las principales:
| Advertencia | Significado y solución |
|---|---|
relay.public_url is empty and server.host is "0.0.0.0"... | public_url no está configurado — la autenticación NIP-42, la eliminación NIP-62
y la autenticación de administrador NIP-98 no funcionarán. Configura wss://your-public-url. |
relay.private_key is empty while NIP-29 is enabled... | Los grupos necesitan una clave secreta. Ejecuta nostrfy genkey. |
unknown config key [relay].software is ignored | Una clave heredada sin usar (o un error tipográfico) en la configuración. Revisa el nombre de la clave. |
unknown config section [serve] is ignored | Un error tipográfico en un nombre de sección (p. ej. [serve] en lugar de [server]). Corríjalo. |
relay.require_auth is true but relay.send_auth_challenge is false... | Esta combinación bloquea a todos. Cambie uno de los dos. |
relay.require_pow = 64 ... practically unmineable | El requisito de prueba de trabajo es tan alto que nadie puede publicar. Reduce require_pow. |
No se puede conectar o comportamiento extraño
El cliente recibe connection refused
Causa: El relé no se está ejecutando, o un cortafuegos está bloqueando el puerto.
Solución:
curl http://127.0.0.1:8080/health
# Desde fuera (usando la IP/puerto del servidor)
curl http://YOUR_SERVER_IP:8080/health
# Comprueba el cortafuegos (ejemplo: ufw)
sudo ufw status
# Abre el puerto si es necesario
sudo ufw allow 8080Los clientes externos no pueden conectarse, los locales sí
Causa: server.host sigue siendo 127.0.0.1 (el valor
predeterminado), que solo acepta conexiones locales.
Solución: Establece host = "0.0.0.0" en la configuración y
reinicia.
No se puede conectar a través de un túnel de Cloudflare
Al usar Cloudflare Tunnel:
- El relé funciona con HTTP sin cifrar; Cloudflare termina TLS, por lo que los clientes usan
wss://. Configurapublic_url = "wss://..."en el relé (esto hace que funcione la autenticación NIP-42). - Cloudflare añade una cabecera
X-Forwarded-Proto. nostrfy trata los valoresws/wss/http/httpsde la misma forma, por lo que normalmente no se necesita configuración adicional.
error: message too large y la conexión se cierra
Causa: Un solo mensaje supera max_ws_message_bytes (1 MB por defecto).
Solución: Aumenta limits.max_ws_message_bytes si necesitas eventos más
grandes — pero comprueba también los límites propios del cliente.
Errores too many subscriptions / too many filters
Causa: Se alcanzaron los límites por conexión (suscripciones 20 por defecto, filtros 20 por defecto).
Solución: Aumenta limits.max_subscriptions / limits.max_filters (y revisa la configuración del cliente).
Las nuevas conexiones se rechazan bajo carga
Causa: Se alcanzó max_connections (10000 por defecto), se activó el límite
por IP (max_connections_per_ip, 64 por defecto), o el límite de conexiones por segundo y
por IP (max_connections_per_sec_per_ip) rechazó la ráfaga. Los límites se aplican a cada
conexión — tanto WebSocket como HTTP sin cifrar.
Solución: Revisa y ajusta la configuración. max_connections_per_ip = 0 desactiva el límite por IP; max_connections_per_sec_per_ip = 0 desactiva el límite de
frecuencia. Estos tres ajustes requieren un reinicio.
Las conexiones se cortan después de un tiempo
Causa: Si ws_idle_timeout_secs está configurado, las conexiones inactivas
se cierran. Los clientes sanos responden al PING del relé con un PONG y permanecen conectados; solo se
eliminan los pares muertos.
Solución: Esto es intencional — el valor predeterminado es 300 segundos. Establece ws_idle_timeout_secs = 0 para desactivarlo por completo.
Una suscripción termina con CLOSED ... response too large
Causa: Los eventos almacenados de un REQ superaron max_req_response_bytes (32 MiB por defecto). Solo ocurre con eventos muy grandes o filtros
muy amplios.
Solución: Reduce el filtro (since/until más estrictos, un limit más bajo) o aumenta max_req_response_bytes (0 desactiva el límite).
Falta un NIP en la lista supported_nips de NIP-11
Causa: La lista anunciada es dinámica — un NIP se oculta cuando todos los kinds que
define son rechazados: todos están en blocked_kinds, ninguno está en allowed_kinds, o son kinds efímeros rechazados por reject_ephemeral.
NIP-29/43/66 además requieren relay.private_key y NIP-86 requiere rpc.management_token o rpc.admin_pubkey.
Solución: Revisa las listas de acceso activas — listallowedkinds de NIP-86
muestra la lista de kinds permitidos, y GET / muestra inmediatamente el supported_nips efectivo. Elimina el kind bloqueante o el ajuste reject_ephemeral.
Errores al publicar
Cuando falla la publicación, el cuarto elemento del mensaje OK explica por qué. Los más
comunes:
| Error | Significado y solución |
|---|---|
invalid: signature verification failed | La firma del evento no es válida (posiblemente una clave de cliente defectuosa). |
invalid: content too large | El contenido supera max_content_bytes (64K caracteres por defecto). Acórtalo o
aumenta el límite. |
invalid: too many tags | Más etiquetas que max_tags (2000 por defecto). |
invalid: event creation date is in the future | Marca de tiempo demasiado lejana en el futuro (más allá de max_created_at_future_secs). |
mute: event contains secret key material | El contenido o las etiquetas contienen una cadena con aspecto de nsec. Nunca publiques claves secretas. Elimina la cadena y el evento será aceptado. |
duplicate: event already stored | El mismo evento ya está almacenado (normal). |
blocked: pubkey not allowed | La clave pública está prohibida (banpubkey) o fuera de la lista de permitidos. |
blocked: kind not allowed | Este kind no está permitido. |
rate-limited: too many events | La clave pública superó max_events_per_min_per_pubkey (ventana deslizante de 60
segundos). Espera un minuto y reintenta, o aumenta/desactiva el límite. |
blocked: event has been banned | El id del evento está prohibido. |
blocked: event has been deleted | Republicación de un evento eliminado. |
auth-required: ... | Se requiere autenticación (cuando relay.require_auth está activado). |
restricted: your account is too new | La cuenta se creó dentro de new_pubkey_min_age_secs. Espera y
reintenta. |
restricted: unknown group | El grupo no existe (créalo primero). |
restricted: this group is closed | El grupo está closed; las solicitudes de unión sin código de invitación no se
aceptan. |
Servidor de archivos Blossom
La subida falla con 401
El evento de autorización de subida (kind 24242) fue rechazado. Comprueba que:
- el tag
expirationdel token está presente y establecido a una marca de tiempo unix en el futuro, - para upload/media/delete el token lleva un tag
xcon el sha256 del blob, - el tag
server(cuando está presente) nombra exactamente elblossom.hostconfigurado (solo nombre de host, sin esquema/ruta), - el token se firmó en los últimos 10 minutos (ventana de frescura contra repetición),
- y la clave de firma es la propia del remitente.
La subida falla con 403
blossom.restrict_uploads = true está configurado y la clave pública no está en la lista de
permitidos — añádela con nostrfy blossom allow npub1... (el demonio recarga
automáticamente). Si la lista parece incorrecta, nostrfy blossom list la muestra.
La subida falla con 409
El cliente envió una cabecera X-SHA-256 que no coincide con el cuerpo real de la solicitud
(el hash declarado se calculó sobre bytes diferentes — p. ej. el archivo cambió entre el cálculo y el
envío). Los clientes pueden omitir la cabecera por completo.
GET / en el host de medios sirve el documento NIP-11
La solicitud no llegó al relé con la cabecera Blossom Host. Apunta media.example.com (o el
valor configurado para blossom.host) al mismo puerto en el proxy inverso, luego nostrfy restart.
Un blob devuelve 404 justo después de la subida
El archivo se direcciona por contenido mediante su SHA-256: obténlo mediante el hash exacto devuelto en la respuesta de
subida (/<sha256> o /<sha256>.<ext>). Una discordancia
significa que el cliente solicitó un hash diferente de los bytes enviados.
Búsqueda, grupos y autenticación
La búsqueda devuelve 0 resultados / resultados inesperados
La búsqueda de nostrfy coincide con palabras completas. Ten en cuenta que:
search = "rust"coincide con eventos que contienen la palabra «rust», pero"ru"NO coincide con «rust» como subcadena.- Solo se buscan palabras en el contenido del evento.
- Si
search_index = false, la búsqueda sigue funcionando pero es más lenta. - Si NIP-50 está desactivado (
disabled_nips = [50]),searchse ignora (se envía un NOTICE).
Los metadatos de grupo (39000-39005) no se generan
Causa: relay.private_key no está configurado. Las instantáneas de grupo
las firma la propia clave del relé, por lo que sin ella no se genera nada.
Solución:
nostrfy --config nostrfy.toml genkey
nostrfy --config nostrfy.toml restartrestricted: unknown group rechaza eventos de grupo
Causa: El grupo no existe. En NIP-29, los eventos de moderación y las solicitudes de unión (9021) no pueden dirigirse a un grupo antes de que se cree (kind 9007).
Solución: Crea primero el grupo con un evento 9007.
restricted: you are not an admin of this group
Causa: La moderación (añadir miembros, etc.) requiere un administrador (un miembro con un rol). El creador es administrador.
Solución: Pide a un administrador que te otorgue un rol, o crea tu propio grupo.
restricted: this group is closed
Causa: El grupo está closed; las solicitudes de unión sin código de
invitación no se aprueban automáticamente.
Solución: Pide a un administrador un código de invitación (9009) y únete con un tag code.
Salió accidentalmente de un grupo, o el grupo no tiene administradores
Causa: Las solicitudes de salida NIP-29 (kind 9022) se aceptan para cualquier miembro — incluido el último administrador del grupo, que no deja administradores. Sin administrador, nadie puede enviar eventos de moderación (9000/9001/9002/9008).
Solución: Firma un evento de moderación con la propia clave del relé
(relay.private_key, la clave pública anunciada como self de NIP-11). Según
NIP-29, los eventos de moderación pueden venir de «la clave maestra del relé o ... los administradores
del grupo», por lo que el relé acepta moderación de grupo firmada por su propia clave incluso cuando el
grupo no tiene administradores. Por ejemplo, restaura un administrador con un kind:9000:
{
"kind": 9000,
"pubkey": "<relay self pubkey>",
"tags": [["h", "<group-id>"], ["p", "<member-hex>", "admin"]]
}Fírmalo y publícalo con la clave del relé. Como alternativa, elimina el grupo con un kind:9008 firmado por el relé (sus eventos almacenados se purgan) y vuelve a crearlo con kind:9007. Esta recuperación necesita que relay.private_key esté configurado.
Los eventos protegidos se rechazan con auth-required
Causa: Los eventos protegidos NIP-70 (con un tag -) solo pueden ser
publicados por el autor autenticado en la misma conexión.
Solución: Activa la autenticación NIP-42 en el cliente antes de publicar.
AUTH (NIP-42) devuelve false
Causas comunes:
relay.public_urlno está configurado o es incorrecto — el tagrelaydel evento AUTH no coincide con la URL del relé. Configurawss://...y reinicia.- Desafío obsoleto — enviaste AUTH en otra conexión, o reutilizaste un desafío antiguo.
- El reloj del cliente está desajustado — el
created_atdel evento AUTH debe estar dentro de ±10 minutos de la hora actual.
La API de gestión NIP-86 devuelve 401 unauthorized
Causa: Credenciales faltantes o incorrectas.
Solución:
- Configura
management_tokeny envíaAuthorization: Bearer <token>. - O configura
admin_pubkeyy envía un evento de autenticación NIP-98 (el tagudebe coincidir exactamente con la URL del relé; se requiere un tagpayload). - Si ninguno está configurado, la API de gestión está desactivada por completo.
Los eventos de autenticación NIP-98 se rechazan por un esquema o puerto diferente
La especificación NIP-98 dice que el tag u debe ser exactamente igual que la URL
absoluta de la solicitud, por lo que nostrfy deriva la URL esperada de relay.public_url: su autoridad más el esquema HTTP asignado desde el esquema WebSocket
(wss:// → https://, ws:// → http://, nostr+ eliminado). Sin public_url, el relé espera el http://host:port sin cifrar que sirve. Un tag con otro esquema, un puerto diferente/omitido, o una ruta
o consulta diferentes se rechaza — configura relay.public_url con la dirección pública que
firman los clientes. Cada evento de autenticación también es de un solo uso: repetir la
misma cabecera Authorization dentro de su ventana de validez de 60 segundos se rechaza.
Base de datos y disco
database map is full: increase database.max_map_size
Causa: Se alcanzó el límite del mapa de memoria LMDB (1 TB de espacio de direcciones virtuales por defecto; el uso real de disco crece con los datos) — en la práctica, la base de datos está llena.
Solución: Aumenta database.max_map_size y reinicia.
disk is full: refusing to commit N events
Causa: Menos de 32 MB de espacio libre en disco. Las escrituras se detienen (para proteger los datos); las lecturas continúan.
Solución: Libera espacio en disco. Las escrituras se reanudan automáticamente cuando
haya espacio disponible. (df -h /path/to/data)
nostrfy check informa map_size must not exceed max_map_size
Causa: database.map_size es mayor que max_map_size.
Solución: Establece map_size igual o por debajo de max_map_size (los valores predeterminados están bien).
Comprobar el tamaño de la base de datos
curl http://127.0.0.1:8080/relay/stats
# => "db_size_bytes" en bytesRespaldar / mover la base de datos
Todos los datos están en el directorio database.path. Detén el relé antes de copiar (copiar una base de datos activa puede corromperla).
nostrfy --config nostrfy.toml stop
cp -a ./data ./data-backup
# Respalda también [blossom].local_path si usas almacenamiento local Blossom.
nostrfy --config nostrfy.toml startFuncionamiento del demonio
nostrfy stats dice nostrfy is not running (no stats file)
Causa: El archivo de estadísticas no existe — el demonio no se está ejecutando, o se inició hace menos de unos segundos.
Solución: Ejecuta nostrfy start, espera unos segundos e inténtalo de nuevo.
El registro crece sin límite
Causa: max_log_size_bytes es 0 (rotación desactivada).
Solución: Establece max_log_size_bytes = 52428800 (50 MB) y max_log_files = 5. La rotación es automática.
Los cambios en la configuración no surten efecto tras la recarga
Causa: Recargaste (SIGHUP) ajustes que son fijos al inicio: private_key, api_host, metrics_enabled, los ajustes de LiveKit y las listas de
activación/desactivación de NIP.
Solución: Usa nostrfy restart. El registro contiene una advertencia
«a restart is required» en este caso.
El relé sigue deteniéndose solo
Causa: La máquina se reinició, o el relé se quedó sin memoria (OOM).
Solución:
- Revisa el final del registro:
tail -50 nostrfy.log. - Comprueba si la máquina se reinició:
uptime(un tiempo de actividad muy corto significa un reinicio). - Comprueba la memoria:
free -h. - Inicia el relé de nuevo:
nostrfy start.
systemd no puede iniciar el relé en el puerto 80
Un servicio systemd ejecutado como root puede usar el puerto 80. Si estableces User= a un
usuario normal, usa un puerto más alto (p. ej. 8080) o añade AmbientCapabilities=CAP_NET_BIND_SERVICE a la unidad.
¿Aún no se ha resuelto?
- Revisa el registro:
tail -100 nostrfy.log— normalmente indica la causa directa. - Vuelve a validar la configuración:
nostrfy check— muestra advertencias y errores. - Recopila los detalles de reproducción: qué estabas haciendo, qué cliente, qué error exacto.
- Pregunta en el repositorio del proyecto: https://github.com/iqbqioza/nostrfy — al abrir un issue, incluye los pasos de reproducción y el registro.