Conceptos básicos
La configuración es un archivo TOML, llamado por defecto nostrfy.toml. Créalo con init:
nostrfy --config nostrfy.toml init
Valídalo (recomendado antes de cada inicio):
nostrfy --config nostrfy.toml check
Cada comando acepta --config <path> (por defecto nostrfy.toml).
Sintaxis general:
[section]
key = "string"
key = 8080
key = [1, 2]
key = true
Secciones de configuración
| Sección | Propósito |
|---|
[relay] | Identidad, URL e interruptores NIP |
[server] | Enlace de red, separación de API, métricas |
[rpc] | RPC de gestión NIP-86 (autenticación, límite del cuerpo) |
[limits] | Todos los límites y protecciones contra sobrecarga |
[database] | Almacenamiento LMDB, índice de búsqueda, límites de cola |
[daemon] | Archivos PID, de registro y de estadísticas y rotación |
[access] | Listas iniciales de control de acceso (modificables en tiempo de ejecución) |
[blossom] | Servidor de archivos Blossom (alojamiento de medios) |
Cada clave es opcional; una clave ausente usa su valor predeterminado.
Sección [relay] — identidad del relé
| Clave | Tipo | Predeterminado | Descripción |
|---|
name | string | "nostrfy" | Nombre del relé mostrado a los clientes vía NIP-11 |
description | string | "A minimal and stable Nostr relay" | Descripción del relé (NIP-11) |
pubkey | string (64 hex) | "" | Clave pública del administrador (campo pubkey de NIP-11) |
contact | string | "" | URI de contacto del administrador (mailto: o https://) |
icon | string | "" | URL de la imagen del icono del relé |
post_policy | string | "" | URL que apunta a la política de publicación del relé |
private_key | string (64 hex) | "" | Clave secreta propia del relé; necesaria para grupos NIP-29 |
public_url | string | "" | URL pública, p. ej. wss://relay.example.com |
livekit_url | string | "" | URL del servidor LiveKit para salas de audio/vídeo NIP-29 |
livekit_api_key | string | "" | Clave API de LiveKit |
livekit_api_secret | string | "" | Secreto API de LiveKit (usado para firmar JWT) |
enabled_nips | array of integers | [] | Lista explícita de NIP permitidos |
disabled_nips | array of integers | [] | NIP a desactivar (se ignora cuando enabled_nips no está vacío) |
reject_ephemeral | boolean | false | Rechazar eventos efímeros NIP-01 (kinds 20000-29999) |
enabled_git | boolean | false | Aceptar eventos git NIP-34 (kinds 1617-1633, 30617/30618) |
require_pow | integer | 0 | Prueba de trabajo requerida en bits cero iniciales |
new_pubkey_min_age_secs | integer | 0 | Rechazar eventos de pubkeys más recientes que esto (segundos; 0 = desactivado) |
max_events_per_min_per_pubkey | integer | 0 | Límite de publicación por pubkey (por minuto; 0 = sin límite) |
max_groups | integer | 1000 | Límite del almacén de grupos NIP-29 en memoria |
require_auth | boolean | false | Requerir autenticación NIP-42 para REQ/EVENT/COUNT/NEG |
send_auth_challenge | boolean | true | Enviar el desafío AUTH al conectar |
enabled_nip78_auth | boolean | true | Requerir AUTH NIP-42 antes de aceptar eventos kind 78/30078 |
enabled_command_events | boolean | false | Ejecutar comandos de operador kind:1 firmados por la pubkey del administrador |
Detalles de claves
- private_key — la clave secreta propia del relé, usada para firmar eventos generados por el relé: metadatos de grupos NIP-29 (39000-39005) y eventos de rol/miembros NIP-43. Genérala con
nostrfy genkey; mantenla en secreto. Se lee una vez al iniciar, por lo que cambiarla requiere reiniciar. - public_url — se usa para validar etiquetas con URL de los clientes: AUTH NIP-42, vanish NIP-62 y auth de administración NIP-98. Cuando está vacía, el relé recurre a
host:port, que nunca coincide con una URL real de cliente al enlazar 0.0.0.0 o 127.0.0.1 (se registra una advertencia). Configúrala siempre. - enabled_nips frente a disabled_nips — la lista de permitidos gana: cuando
enabled_nips no está vacío, solo se anuncian sus NIP y disabled_nips se ignora. Ambos requieren reinicio. - reject_ephemeral — los kinds 20000-29999 se rechazan, pero los kinds exentos que los NIP exigen retransmitir siguen reenviándose: 22242, 27235, 28934/28935/28936, 24133, 23194/23195, 24242 y 21059. Se aplica con SIGHUP.
- enabled_git — NIP-34 opcional: acepta los kinds 1617-1633 y 30617/30618 y anuncia NIP-34. Desactivado por defecto porque los parches pueden ser grandes. Se aplica con SIGHUP.
Sección [server] — ajustes del servidor
| Clave | Tipo | Predeterminado | Descripción |
|---|
host | string | "127.0.0.1" | Dirección de enlace; 0.0.0.0 acepta conexiones de cualquier lugar |
port | integer | 8080 | Puerto (1-65535); el puerto 80 requiere root |
api_host | string | "" | Nombre de host dedicado a la API REST |
metrics_enabled | boolean | true | Servir métricas Prometheus en /metrics |
ws_paths | string | "root" | Rutas del endpoint WebSocket: root, inbox-outbox o all |
inbox_write_policy | string | "any" | Quién puede escribir en /inbox: "any" o "relay" (los eventos deben llevar etiqueta p) |
outbox_write_policy | string | "any" | Quién puede escribir en /outbox: "any" (los propios eventos de la pubkey autenticada NIP-42) o "relay" |
trusted_proxies | array of strings | [] | Direcciones/CIDR de proxies inversos cuyo X-Forwarded-For es confiable (vacío = no confiar en ningún proxy) |
Detalles de claves
- host —
0.0.0.0 enlaza todas las interfaces IPv4; 127.0.0.1 es solo local. - port — 1-65535; el puerto 80 requiere root. Este único puerto sirve el relé WebSocket, el documento NIP-11, la API REST y el RPC NIP-86 juntos.
- api_host — dedica la API REST a un único nombre de host para que la API y el relé puedan compartir un puerto detrás de un proxy inverso. Fijo al iniciar — requiere reinicio.
- ws_paths —
root sirve solo /, inbox-outbox sirve solo /inbox y /outbox, all sirve ambos. Fijo al iniciar — requiere reinicio. - trusted_proxies — incluye solo las direcciones propias del proxy (loopback para nginx/Caddy en el mismo host, el rango de origen del balanceador en la nube). Con esta opción, la IP del cliente se deriva de la última entrada no confiable de
X-Forwarded-For para los límites por IP, el límite de tasa, blockip y los registros. Nunca añadas una dirección accesible directamente por los clientes — podrían falsificar la cabecera y eludir los límites por IP. Fijo al iniciar — requiere reinicio.
Sección [rpc] — gestión NIP-86
| Clave | Tipo | Predeterminado | Descripción |
|---|
management_token | string | "" | Token Bearer para las API de gestión |
admin_pubkey | string (64 hex) | "" | Pubkey del administrador para la auth de gestión NIP-98 |
max_admin_body_bytes | integer | 65536 | Límite del cuerpo para el RPC de gestión NIP-86 |
El RPC NIP-86 está montado en las rutas públicas POST / del relé — no hay un puerto de gestión separado. management_token y admin_pubkey a veces aparecen bajo [server] en guías antiguas; esas grafías son alias heredados de estas claves [rpc].
Sección [limits] — límites y protecciones
Conexiones y mensajes
| Clave | Tipo | Predeterminado | Descripción |
|---|
max_connections | integer | 10000 | Conexiones concurrentes máximas |
max_connections_per_ip | integer | 64 | Conexiones máximas por IP de origen |
max_ws_message_bytes | integer | 1048576 | Bytes máximos por mensaje/frame WebSocket |
socket_recv_buffer_kb | integer | 64 | Búfer de recepción del kernel por conexión (KiB) |
max_out_queue_bytes | integer | 262144 | Límite de cola de salida por conexión (bytes) |
ws_idle_timeout_secs | integer | 300 | Cerrar conexiones inactivas tras este tiempo |
http_read_timeout_secs | integer | 30 | Tiempo de espera de cabecera HTTP (defensa slow-loris) |
max_connections_per_sec_per_ip | integer | 0 | Conexiones nuevas máximas por segundo por IP de origen |
Suscripciones y consultas
| Clave | Tipo | Predeterminado | Descripción |
|---|
max_filters | integer | 20 | Filtros máximos por REQ |
max_subscriptions | integer | 20 | Suscripciones máximas por conexión |
max_limit | integer | 500 | Tope para el limit de REQ |
max_count | integer | 2000 | Tope para resultados COUNT |
max_sub_id_len | integer | 64 | Longitud máxima del id de suscripción (caracteres, no bytes) |
max_sub_bytes | integer | 1048576 | Bytes totales de filtros de suscripción por conexión |
max_req_response_bytes | integer | 33554432 (32 MB) | Tope de bytes totales que una sola respuesta REQ puede enviar |
Eventos
| Clave | Tipo | Predeterminado | Descripción |
|---|
max_content_bytes | integer | 65536 | Longitud máxima del contenido del evento en caracteres |
max_tags | integer | 2000 | Etiquetas máximas por evento |
max_tag_value_bytes | integer | 1024 | Bytes máximos por valor de etiqueta |
max_created_at_future_secs | integer | 3600 | Desviación futura tolerada de created_at |
group_late_publish_secs | integer | 3600 | Retraso tolerado para eventos de administración de grupos NIP-29 (segundos) |
max_neg_items | integer | 100000 | Registros máximos por sincronización de negentropía NIP-77 |
Alias heredados: limits.require_pow, limits.new_pubkey_min_age_secs y limits.max_indexed_words aún se aceptan como alias de relay.require_pow, relay.new_pubkey_min_age_secs y database.max_indexed_words.
API REST
| Clave | Tipo | Predeterminado | Descripción |
|---|
max_api_concurrent | integer | 8 | Solicitudes /api/v1 concurrentes máximas |
max_api_limit | integer | 5000 | Tope para el parámetro limit de la API |
max_api_offset | integer | 50000 | Tope para el parámetro offset de la API |
max_api_fetch | integer | 55001 | Ventana máxima de sobrecarga para consultas con offset — debe cubrir max_api_offset + max_api_limit + 1 (0 = sin límite) |
max_api_search_bytes | integer | 2048 | Bytes máximos del parámetro search de la API |
Distribución en vivo
| Clave | Tipo | Predeterminado | Descripción |
|---|
live_batch_interval_ms | integer | 20 | Frecuencia de vaciado de eventos en vivo (ms) |
live_batch_size | integer | 32 | Eventos máximos por lote en vivo |
live_buffer | integer | 65536 | Tamaño de la cola de distribución en vivo |
Sección [database] — base de datos
| Clave | Tipo | Predeterminado | Descripción |
|---|
path | string | "./data" | Directorio de la base de datos (LMDB) |
max_dbs | integer | 32 | Máximo de bases de datos nombradas LMDB |
max_readers | integer | 128 | Máximo de lectores concurrentes LMDB |
map_size | integer | 1073741824 (1 GB) | Piso del tamaño del mapa de memoria (bytes) |
max_map_size | integer | 1099511627776 (1 TB) | Techo del mapa de memoria (bytes) |
purge_interval_secs | integer | 300 | Intervalo de purga NIP-40 (segundos) |
search_index | boolean | true | Habilitar el índice de palabras NIP-50 |
reader_threads | integer | 2 | Hilos dedicados de escaneo |
max_indexed_words | integer | 32 | Palabras del contenido de cada evento indexadas para búsqueda |
meta_index | boolean | true | Escribir la cabecera de metadatos por evento usada por el prefiltro de escaneo |
disabled_fsync | boolean | false | Omitir el vaciado síncrono a disco tras cada lote de escritura |
db_buffer_size | integer | 2048 | Búfer WebSocket inicial por conexión (bytes) |
db_request_timeout_secs | integer | 30 | Tiempo máximo que una solicitud a la base de datos puede esperar antes de fallar |
max_db_queue_msgs | integer | 4096 | Mensajes pendientes máximos en cola antes de fallar rápido |
max_db_queue_events | integer | 262144 | Eventos máximos en lotes en cola antes de fallar rápido |
max_db_queue_bytes | integer | 268435456 (256 MiB) | Bytes máximos de solicitudes a la base de datos en cola antes de fallar rápido (0 = sin límite de bytes) |
Detalles de claves
- map_size — el piso del mapa de memoria: el mapa siempre se abre al menos con este tamaño.
- max_map_size — el techo, abierto como reserva virtual dispersa: el disco físico solo crece con los datos realmente escritos. Auméntalo cuando veas
database map is full. - search_index = false — la búsqueda sigue funcionando (coincidencia de palabras completas contra el contenido) pero los escaneos son más lentos; en un VPS pequeño reduce la base de datos a la mitad. Recomendado en instancias pequeñas.
- disabled_fsync — cambia durabilidad por rendimiento: las escrituras se confirman en la caché de páginas del SO y un corte de energía puede perder las escrituras más recientes.
Sección [daemon] — demonio
| Clave | Tipo | Predeterminado | Descripción |
|---|
pid_file | string | "./nostrfy.pid" | Ruta del archivo PID |
log_file | string | "./nostrfy.log" | Ruta del archivo de registro |
stats_file | string | "./nostrfy.stats.json" | Ruta del archivo de estadísticas |
stats_interval_secs | integer | 5 | Intervalo de escritura de estadísticas (segundos) |
max_log_size_bytes | integer | 52428800 (50 MB) | Tamaño de rotación del registro (0 = sin rotación) |
max_log_files | integer | 5 | Generaciones de registros rotados a conservar |
Las rutas se resuelven respecto al directorio del archivo de configuración, por lo que siguen siendo válidas después de que el demonio cambie su directorio de trabajo.
Sección [access] — control de acceso
| Clave | Tipo | Predeterminado | Descripción |
|---|
restrict_relay | boolean | false | Solo las pubkeys en lista permitida pueden publicar |
blocked_kinds | array of integers | [] | Kinds a rechazar |
allowed_kinds | array of integers | [] | Lista de kinds permitidos; solo se aceptan estos kinds cuando no está vacía |
blocked_ips | array of strings | [] | Direcciones IP rechazadas al conectar |
method_grants | table: pubkey → array of strings | {} | Concesiones de métodos NIP-86 para pubkeys no administradoras (gestionadas en tiempo de ejecución con assignmethod) |
Las listas de pubkeys permitidas/denegadas no son claves de configuración — viven en la base de datos del relé (LMDB) y se gestionan en tiempo de ejecución:
nostrfy relay allow npub1...
nostrfy relay deny npub1...
nostrfy relay list
- restrict_relay = true — solo las pubkeys en lista permitida pueden publicar, mientras la lectura sigue abierta para todos (cualquier cliente aún puede suscribirse y obtener datos).
- Una pubkey denegada siempre se rechaza al publicar y nunca se sirve al leer.
- method_grants — concesiones de métodos NIP-86 para pubkeys no administradoras (pubkey → nombres de método, p. ej. un moderador con
banevent y listbannedevents). Se siembran desde la configuración en la primera ejecución, luego se gestionan en tiempo de ejecución con assignmethod/unassignmethod de NIP-86 (inspeccionadas con listmethodassignees). Solo los métodos de moderación y lectura son delegables — la gestión de permisos, roles, invitaciones e identidad del relé sigue siendo solo de admin, y una pubkey baneada se rechaza incluso con concesiones. Ver la API de gestión.
Sección [blossom] — servidor de archivos Blossom
| Clave | Tipo | Predeterminado | Descripción |
|---|
host | string | "" | Nombre de host para el servidor Blossom (vacío = desactivado) |
storage | string | "local" | Backend: "local" (local_path) o "s3" (bucket compatible con S3) |
local_path | string | "/var/lib/nostrfy/images" | Raíz de almacenamiento local para archivos multimedia |
max_upload_bytes | integer | 20971520 (20 MB) | Tamaño máximo de archivo multimedia |
min_free_bytes | integer | 33554432 (32 MB) | Espacio en disco por debajo del cual se rechazan subidas |
s3_endpoint | string | "" | Endpoint compatible con S3 (p. ej. R2) |
s3_region | string | "" | Región S3 (R2 usa "auto") |
s3_bucket | string | "" | Nombre del bucket S3 |
s3_access_key | string | "" | Clave de acceso S3 |
s3_secret_key | string | "" | Clave secreta S3 |
restrict_uploads | boolean | false | Solo las pubkeys en lista permitida pueden subir archivos |
Recarga en caliente (SIGHUP)
Editar el archivo y enviar kill -HUP $(cat nostrfy.pid) recarga la configuración sin reiniciar. La mayoría de los ajustes surten efecto de inmediato; unos pocos están fijados al iniciar:
| Se aplica con SIGHUP | Requiere reinicio |
|---|
| 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 mayoría de [limits] | api_host, trusted_proxies, metrics_enabled, ws_paths, database.*, tamaños de demonio, topes de límites, blossom.* |
[access] no se aplica con una recarga — las listas se siembran una vez al iniciar y luego se gestionan en tiempo de ejecución vía NIP-86. El registro avisa cuando cambia un ajuste que requiere reinicio, y algunos ajustes capturados al iniciar no son comprobados por la recarga.
| Error | Solución |
|---|
| public_url sin definir | define wss://... |
| host dejado en 127.0.0.1 | los clientes externos no pueden conectar |
| private_key sin definir con NIP-29 | ejecuta nostrfy genkey + reinicia |
| restrict_relay true con allowlist vacía | todos bloqueados |
| cambiar claves que solo requieren reinicio y solo hacer SIGHUP | usa nostrfy restart |