Эксплуатация релея

Для вашего релея nostrfy: запуск и остановка, журналы и статистика, горячая перезагрузка конфигурации, несколько экземпляров и тюнинг на масштабе.

Запуск и остановка

Запустите релей как фоновый демон:

sh
nostrfy --config nostrfy.toml start

=> nostrfy started (pid 12345)

Или запустите в терминале на переднем плане:

sh
nostrfy --config nostrfy.toml start --foreground

Остановить:

sh
nostrfy --config nostrfy.toml stop

Убедитесь, что релей запущен:

sh
curl http://127.0.0.1:8080/health

=> {"status":"ok"}

Логи и статистика

Демон пишет в daemon.log_file. Когда файл превышает max_log_size_bytes, он автоматически ротируется (nostrfy.log.1, .2… до max_log_files поколений):

sh
tail -f nostrfy.log

Уровень журналов задаётся переменной окружения RUST_LOG (например, RUST_LOG=debug).

Статистика

Живая статистика из CLI:

sh
nostrfy stats

Или по HTTP:

sh
curl http://127.0.0.1:8080/relay/stats

Показывает соединения, принятые/отклонённые события, размер БД и другое.

Метрики Prometheus

sh
curl http://127.0.0.1:8080/metrics

Горячая перезагрузка (SIGHUP)

Отредактировав файл конфигурации, перезагрузите его без перезапуска:

sh
kill -HUP $(cat nostrfy.pid)
Применяется при перезагрузкеТребуется перезапуск
Идентичность релея, public_urlprivate_key
большинство [limits], reject_ephemeral, enabled_git, enabled_nip78_authapi_host, metrics_enabled, настройки LiveKit
—enabled_nips / disabled_nips, server.host / port / ws_paths
—database.* (включая search_index), blossom.*

При изменении настройки, требующей перезапуска, в журнале появляется предупреждение.

Запуск нескольких экземпляров

nostrfy поддерживает несколько независимых релеев на одном сервере (разные порты). Каждому экземпляру нужен собственный server.port, [daemon] pid_file/log_file/stats_file (общие значения заставят второй экземпляр завершиться с ошибкой запуска already running), database.path, а также (при использовании) собственные api_host / blossom.host:

toml
[server]
port = 8080

[database]
path = "/var/lib/nostrfy-a"

[daemon]
pid_file = "/var/run/nostrfy-a.pid"
log_file = "/var/log/nostrfy-a.log"
stats_file = "/var/lib/nostrfy-a/stats.json"

[server]
port = 8081

[database]
path = "/var/lib/nostrfy-b"

[daemon]
pid_file = "/var/run/nostrfy-b.pid"
log_file = "/var/log/nostrfy-b.log"
stats_file = "/var/lib/nostrfy-b/stats.json"

Каждый экземпляр управляется своим файлом конфигурации: nostrfy --config /etc/nostrfy/a.toml start и т. д.

Крупномасштабные развёртывания

Релей рассчитан на сотни тысяч соединений на одном хосте — живая доставка будит только тех подписчиков, которые могут совпасть с событием, а память на соединение мала. Для миллионов нужно тюнинг на уровне хоста:

НастройкаЗначениеПричина
ulimit -n / systemd LimitNOFILE≥ 2× целевого числа соединений (+1000)каждое соединение занимает один fd
net.core.somaxconn≥ 1024Очередь ожидающих accept при всплеске соединений
net.ipv4.tcp_fin_timeoutнизкое (например, 10)Быстрее освобождает сокеты TIME_WAIT
vm.overcommit_memory1 или 2Карта LMDB — большая разреженная виртуальная резервация

В FreeBSD соответствующие параметры — kern.maxfiles / kern.maxfilesperproc плюс ulimit -n, а также kern.ipc.somaxconn заменяет net.core.somaxconn. Память ядра на соединение — около 80 KiB, пользовательская — около 10 KiB, поэтому миллион соединений требует примерно 90 GiB памяти ядра и пользователя сверх базы данных.

Пропускная способность (событий в секунду)

Запись событий ограничена двумя затратами: проверкой подписи Schnorr (около 30–50 µs на событие) и синхронным сбросом на диск, который писатель LMDB выполняет после каждого пакета коммитов. Оба настраиваются в файле nostrfy.toml:

НастройкаПричина
database.disabled_fsync = trueКоммит в страничный кэш ОС (микросекунды); при потере питания теряются только записи после последнего сброса — начните с этого
Ядер CPU ≥ 8 vCPUПакетный путь EVENT проверяет подписи параллельно по ядрам
database.search_index = falseУбирает запись словаря NIP-50 на каждое событие для нагруженных записью экземпляров

Параллельная проверка подписей проверяет все подписи ожидающего пакета сразу в пуле рабочих потоков (не более 8; пакеты меньше 16 событий проверяются построчно). Дешёвые проверки каждого события всё равно выполняются первыми, поэтому текст причины отказа совпадает с последовательным путём — по ядрам распределяется только работа Schnorr. Однопоточная сборка остаётся последовательной.

Фиксированные границы защиты от атак

Несколько жёстких границ зафиксированы (не настраиваются), чтобы релей оставался отзывчивым под атаками:

  • Один фильтр содержит не более 512 ids, authors или kinds записей; значения тегов #... используют отдельный бюджет 512 значений. Слишком большие фильтры отклоняются (CLOSED invalid: ...).
  • max_connections_per_sec_per_ip отслеживает не более 10 000 исходных IP; при заполнении невиданные IP отклоняются (запрет по умолчанию).
  • ids в фильтрах могут быть префиксами, но совпадают только полные 32-байтовые id и префиксы чётной длины (записи нечётной длины и пустые игнорируются и в истории, и в живой доставке).
  • Слишком длинные ключи индекса (значения тегов, слова контента, теги d сверх лимита размера ключа LMDB) пропускаются при индексации; событие всё равно сохраняется.