Эксплуатация релея
Для вашего релея nostrfy: запуск и остановка, журналы и статистика, горячая перезагрузка конфигурации, несколько экземпляров и тюнинг на масштабе.
Запуск и остановка
Запустите релей как фоновый демон:
nostrfy --config nostrfy.toml start=> nostrfy started (pid 12345)
Или запустите в терминале на переднем плане:
nostrfy --config nostrfy.toml start --foregroundОстановить:
nostrfy --config nostrfy.toml stopУбедитесь, что релей запущен:
curl http://127.0.0.1:8080/health=> {"status":"ok"}
Логи и статистика
Демон пишет в daemon.log_file. Когда файл превышает max_log_size_bytes, он автоматически ротируется (nostrfy.log.1, .2… до max_log_files поколений):
tail -f nostrfy.logУровень журналов задаётся переменной окружения RUST_LOG (например, RUST_LOG=debug).
Статистика
Живая статистика из CLI:
nostrfy statsИли по HTTP:
curl http://127.0.0.1:8080/relay/statsПоказывает соединения, принятые/отклонённые события, размер БД и другое.
Метрики Prometheus
curl http://127.0.0.1:8080/metricsГорячая перезагрузка (SIGHUP)
Отредактировав файл конфигурации, перезагрузите его без перезапуска:
kill -HUP $(cat nostrfy.pid)| Применяется при перезагрузке | Требуется перезапуск |
|---|---|
| Идентичность релея, public_url | private_key |
| большинство [limits], reject_ephemeral, enabled_git, enabled_nip78_auth | api_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:
[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_memory | 1 или 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) пропускаются при индексации; событие всё равно сохраняется.