Устранение неполадок

Ошибки, с которыми вы столкнётесь чаще всего, — порты, права, TLS, отсутствующие NIP, публикация и тайм-ауты — с пошаговыми исправлениями.

Три вещи, которые нужно проверить в первую очередь:

  • nostrfy check проверяет конфигурацию (большинство ошибок — это ошибки в конфигурации).
  • tail -f nostrfy.log показывает журнал — причина почти всегда там.
  • nostrfy restart корректно перезапускает демона.

Не запускается

error: cannot bind to 0.0.0.0:80: Permission denied

Причина: порт 80 может занимать только root.

Исправление: запустите с sudo или смените порт, например на 8080.

sh
# Смените port = 8080 в файле конфигурации, затем:
nostrfy --config nostrfy.toml start

error: cannot bind to ...: Address already in use

Причина: другой процесс (старый nostrfy или другой сервер) уже использует этот порт.

Исправление:

sh
ss -tlnp | grep :8080
sh
# Если nostrfy запущен, перезапустите его
nostrfy --config nostrfy.toml restart

already running (pid 1234); use 'nostrfy stop' or 'nostrfy restart'

Причина: nostrfy уже запущен; start отказывается запускать второй экземпляр.

Исправление: используйте nostrfy restart или просто используйте уже работающий экземпляр.

nostrfy stop зависает / did not stop in time

Причина: демон завис или не отвечает.

Исправление:

sh
ps aux | grep nostrfy
kill -9 <PID>
# Удалите устаревший pid-файл, если он есть
rm -f nostrfy.pid

error: invalid nostrfy.toml: TOML parse error

Причина: файл конфигурации — некорректный TOML. Типичные ошибки: забытые кавычки вокруг строки или дважды записанный один и тот же ключ.

Исправление: сообщение об ошибке содержит номер строки. Проверьте и исправьте эту строку.

toml
# Правильные примеры
name = "my relay"        # строки — в кавычках "
port = 8080              # числа — без кавычек
enabled_nips = [1, 50]   # списки — в квадратных скобках [ ]

error: cannot read nostrfy.toml: No such file or directory

Причина: файл конфигурации не существует.

Исправление:

sh
nostrfy --config nostrfy.toml init

error: relay.private_key is not a valid secp256k1 secret key

Причина: relay.private_key — невалидный 64-символьный hex-ключ.

Исправление: выполните nostrfy genkey, чтобы сгенерировать корректный ключ (или задайте private_key = "").

Много предупреждений в журнале при запуске

Строки журнала [WARN] сообщают о проблемах конфигурации. Основные:

ПредупреждениеЗначение и исправление
relay.public_url is empty and server.host is "0.0.0.0"...public_url не задан — NIP-42 auth, NIP-62 vanish и NIP-98 admin auth не будут работать. Задайте wss://your-public-url.
relay.private_key is empty while NIP-29 is enabled...Группам нужен секретный ключ. Выполните nostrfy genkey.
unknown config key [relay].software is ignoredНеиспользуемый устаревший ключ (или опечатка) в конфигурации. Проверьте имя ключа.
unknown config section [serve] is ignoredОпечатка в имени секции (например, [serve] вместо [server]). Исправьте её.
relay.require_auth is true but relay.send_auth_challenge is false...Такая комбинация блокирует всех. Измените один из двух параметров.
relay.require_pow = 64 ... practically unmineableТребование PoW настолько высокое, что никто не может публиковать. Понизьте require_pow.

Не подключается или работает странно

Клиент получает connection refused

Причина: релей не запущен или брандмауэр блокирует порт.

Исправление:

sh
curl http://127.0.0.1:8080/health

# Снаружи (через IP/порт сервера)
curl http://YOUR_SERVER_IP:8080/health

# Проверьте брандмауэр (например: ufw)
sudo ufw status
# Откройте порт при необходимости
sudo ufw allow 8080

Внешние клиенты не подключаются, локальные — да

Причина: server.host всё ещё 127.0.0.1 (по умолчанию), который принимает только локальные подключения.

Исправление: задайте host = "0.0.0.0" в конфигурации и перезапустите.

Не подключается через туннель Cloudflare

При использовании Cloudflare Tunnel:

  • Релей работает по обычному HTTP; Cloudflare терминирует TLS, поэтому клиенты используют wss://. Задайте public_url = "wss://..." на релее (тогда заработает NIP-42 auth).
  • Cloudflare добавляет заголовок X-Forwarded-Proto. nostrfy одинаково трактует значения ws / wss / http / https, поэтому дополнительная настройка обычно не нужна.

error: message too large, и соединение закрывается

Причина: одно сообщение превышает max_ws_message_bytes (по умолчанию 1 МБ).

Исправление: увеличьте limits.max_ws_message_bytes, если нужны более крупные события, — но проверьте и собственные лимиты клиента.

Ошибки too many subscriptions / too many filters

Причина: достигнуты лимиты на подключения (подписок по умолчанию 20, фильтров по умолчанию 20).

Исправление: увеличьте limits.max_subscriptions / limits.max_filters (и проверьте настройки клиента).

Новые подключения отклоняются под нагрузкой

Причина: достигнут max_connections (по умолчанию 10000), сработал лимит на IP (max_connections_per_ip, по умолчанию 64) или лимит скорости подключений в секунду на IP (max_connections_per_sec_per_ip) отклонил всплеск. Лимиты применяются к каждому подключению — и WebSocket, и обычному HTTP.

Исправление: проверьте и настройте параметры. max_connections_per_ip = 0 отключает лимит на IP; max_connections_per_sec_per_ip = 0 отключает лимит скорости. Эти три настройки требуют перезапуска.

Соединения разрываются через некоторое время

Причина: если задан ws_idle_timeout_secs, простаивающие соединения закрываются. Здоровые клиенты отвечают PONG на PING релея и остаются подключёнными; удаляются только мёртвые пиры.

Исправление: это задумано — по умолчанию 300 секунд. Задайте ws_idle_timeout_secs = 0, чтобы полностью отключить.

Подписка завершается с CLOSED ... response too large

Причина: сохранённые события одного REQ превысили max_req_response_bytes (по умолчанию 32 МиБ). Бывает только при очень крупных событиях или очень широких фильтрах.

Исправление: сузьте фильтр (более строгие since / until, меньший limit) или увеличьте max_req_response_bytes (0 отключает бюджет).

NIP отсутствует в списке NIP-11 supported_nips

Причина: публикуемый список динамический — NIP скрывается, когда все определяемые им kinds отклонены: все они в blocked_kinds, ни один из них не в allowed_kinds, или эфемерные kinds отклонены через reject_ephemeral. NIP-29/43/66 дополнительно требуют relay.private_key, а NIP-86 требует rpc.management_token или rpc.admin_pubkey.

Исправление: проверьте активные списки доступа — NIP-86 listallowedkinds показывает список разрешённых kind, а GET / сразу показывает действующий supported_nips. Уберите блокирующий kind или настройку reject_ephemeral.

Ошибки при публикации

Когда публикация не удаётся, 4-й элемент сообщения OK объясняет причину. Самые частые:

ОшибкаЗначение и исправление
invalid: signature verification failedНедействительная подпись события (возможно, сломанный ключ клиента).
invalid: content too largeКонтент превышает max_content_bytes (по умолчанию 64K символов). Сократите его или увеличьте лимит.
invalid: too many tagsТегов больше, чем max_tags (по умолчанию 2000).
invalid: event creation date is in the futureМетка времени слишком далеко в будущем (за пределами max_created_at_future_secs).
mute: event contains secret key materialКонтент или теги содержат строку, похожую на nsec. Никогда не публикуйте секретные ключи. Удалите строку, и событие будет принято.
duplicate: event already storedТакое же событие уже сохранено (нормально).
blocked: pubkey not allowedПубличный ключ забанен (banpubkey) или вне списка разрешённых.
blocked: kind not allowedЭтот kind запрещён.
rate-limited: too many eventsПубличный ключ превысил max_events_per_min_per_pubkey (скользящее 60-секундное окно). Подождите минуту и повторите, либо увеличьте/отключите лимит.
blocked: event has been bannedID события забанен.
blocked: event has been deletedПовторная публикация удалённого события.
auth-required: ...Требуется аутентификация (когда включён relay.require_auth).
restricted: your account is too newАккаунт создан в пределах new_pubkey_min_age_secs. Подождите и повторите.
restricted: unknown groupГруппа не существует (сначала создайте её).
restricted: this group is closedГруппа closed; запросы на вступление без пригласительного кода не удовлетворяются.

Файловый сервер Blossom

Загрузка завершается с ошибкой 401

Событие авторизации загрузки (kind 24242) отклонено. Проверьте, что:

  • тег expiration токена присутствует и задан как unix-метка в будущем,
  • для upload/media/delete токен несёт тег x с sha256 блоба,
  • тег server (если есть) точно называет настроенный blossom.host (только hostname, без схемы/пути),
  • токен подписан в последние 10 минут (окно свежести против повторов),
  • и ключ подписи принадлежит самому загрузчику.

Загрузка завершается с ошибкой 403

Задано blossom.restrict_uploads = true, и публичного ключа нет в списке разрешённых — добавьте его командой nostrfy blossom allow npub1... (демон перезагрузится автоматически). Если список выглядит неверно, nostrfy blossom list покажет его.

Загрузка завершается с ошибкой 409

Клиент отправил заголовок X-SHA-256, который не совпадает с фактическим телом запроса (объявленный хеш вычислен по другим байтам — например, файл изменился между хешированием и отправкой). Клиенты могут вообще опустить заголовок.

GET / на медиа-хосте отдаёт NIP-11 документ

Запрос не дошёл до релея с заголовком Blossom Host. Направьте media.example.com (или то, что задано как blossom.host) на тот же порт в обратном прокси, затем nostrfy restart.

Блоб отдаёт 404 сразу после загрузки

Файл адресуется по содержимому через SHA-256: запрашивайте его по точному хешу, возвращённому в ответе загрузки (/<sha256> или /<sha256>.<ext>). Несовпадение означает, что клиент запросил другой хеш, чем отправленные байты.

Поиск, группы и auth

Поиск возвращает 0 результатов / неожиданные результаты

Поиск nostrfy совпадает по целым словам. Обратите внимание, что:

  • search = "rust" совпадает с событиями, содержащими слово «rust», но "ru" НЕ совпадает с «rust» как подстрока.
  • Ищутся только слова в контенте событий.
  • Если search_index = false, поиск всё равно работает, но медленнее.
  • Если NIP-50 отключён (disabled_nips = [50]), search игнорируется (отправляется NOTICE).

Метаданные групп (39000-39005) не генерируются

Причина: relay.private_key не задан. Снапшоты групп подписываются собственным ключом релея, поэтому без него ничего не генерируется.

Исправление:

sh
nostrfy --config nostrfy.toml genkey
nostrfy --config nostrfy.toml restart

restricted: unknown group отклоняет события групп

Причина: группа не существует. В NIP-29 события модерации и запросы на вступление (9021) не могут быть направлены группе до её создания (kind 9007).

Исправление: сначала создайте группу событием 9007.

restricted: you are not an admin of this group

Причина: модерация (добавление участников и т. д.) требует администратора (участника с ролью). Создатель — администратор.

Исправление: попросите администратора выдать вам роль или создайте собственную группу.

restricted: this group is closed

Причина: группа closed; запросы на вступление без пригласительного кода не одобряются автоматически.

Исправление: попросите у администратора пригласительный код (9009) и вступайте с тегом code.

Случайно покинули группу, или в группе нет администраторов

Причина: запросы на выход NIP-29 (kind 9022) уважаются для любого участника — включая последнего администратора группы, после ухода которого администраторов не остаётся. Без администратора никто больше не может отправлять события модерации (9000/9001/9002/9008).

Исправление: подпишите событие модерации собственным ключом релея (relay.private_key, публичный ключ, публикуемый как NIP-11 self). Согласно NIP-29, события модерации могут исходить от «мастер-ключа релея или … администраторов групп», поэтому релей принимает модерацию групп, подписанную собственным ключом, даже когда в группе нет администраторов. Например, восстановите администратора с kind:9000:

json
{
  "kind": 9000,
  "pubkey": "<relay self pubkey>",
  "tags": [["h", "<group-id>"], ["p", "<member-hex>", "admin"]]
}

Подпишите и опубликуйте его ключом релея. Либо удалите группу подписанным релеем kind:9008 (её сохранённые события очищаются) и создайте заново с kind:9007. Для этого восстановления нужен настроенный relay.private_key.

Защищённые события отклоняются с auth-required

Причина: защищённые события NIP-70 (с тегом -) могут публиковаться только аутентифицированным автором на том же соединении.

Исправление: включите NIP-42 auth в клиенте перед публикацией.

AUTH (NIP-42) возвращает false

Частые причины:

  1. relay.public_url не задан или неверен — тег relay события AUTH не совпадает с URL релея. Задайте wss://... и перезапустите.
  2. Устаревший challenge — вы отправили AUTH на другом соединении или повторно использовали старый challenge.
  3. Часы клиента сбиты — created_at события AUTH должен быть в пределах ±10 минут от текущего времени.

API управления NIP-86 возвращает 401 unauthorized

Причина: отсутствуют или неверны учётные данные.

Исправление:

  • Задайте management_token и отправляйте Authorization: Bearer <token>.
  • Или задайте admin_pubkey и отправьте NIP-98 auth-событие (тег u должен точно совпадать с URL релея; тег payload обязателен).
  • Если не задано ни то, ни другое, API управления полностью отключён.

Auth-события NIP-98 отклоняются из-за другой схемы или порта

Спецификация NIP-98 требует, чтобы тег u был в точности тем же, что и абсолютный URL запроса, поэтому nostrfy выводит ожидаемый URL из relay.public_url: его authority плюс HTTP-схема, отображённая из WebSocket-схемы (wss:// → https://, ws:// → http://, nostr+ отбрасывается). Без public_url релей ожидает обычный http://host:port, который он обслуживает. Тег с другой схемой, другим/опущенным портом либо другим путём или запросом отклоняется — задайте relay.public_url как публичный адрес, который подписывают клиенты. Каждое auth-событие также одноразовое: повтор той же строки Authorization в пределах 60-секундного окна валидности отклоняется.

База данных и диск

database map is full: increase database.max_map_size

Причина: достигнут потолок memory-map LMDB (по умолчанию 1 ТБ виртуального адресного пространства; реальное использование диска растёт с данными) — фактически база данных переполнена.

Исправление: увеличьте database.max_map_size и перезапустите.

disk is full: refusing to commit N events

Причина: менее 32 МБ свободного места на диске. Записи останавливаются (для защиты данных); чтения продолжаются.

Исправление: освободите место на диске. Записи возобновятся автоматически, как только место появится. (df -h /path/to/data)

nostrfy check сообщает map_size must not exceed max_map_size

Причина: database.map_size больше, чем max_map_size.

Исправление: задайте map_size на уровне или ниже max_map_size (значений по умолчанию достаточно).

Проверка размера базы данных

sh
curl http://127.0.0.1:8080/relay/stats
# => "db_size_bytes" в байтах

Резервное копирование / перенос базы данных

Все данные находятся в каталоге database.path. Остановите релей перед копированием (копирование живой базы может её повредить).

sh
nostrfy --config nostrfy.toml stop
cp -a ./data ./data-backup
# Также скопируйте [blossom].local_path при локальном хранилище Blossom.
nostrfy --config nostrfy.toml start

Работа демона

nostrfy stats говорит nostrfy is not running (no stats file)

Причина: файл статистики не существует — демон не запущен или запущен менее нескольких секунд назад.

Исправление: выполните nostrfy start, подождите несколько секунд и попробуйте снова.

Журнал растёт без ограничений

Причина: max_log_size_bytes равен 0 (ротация отключена).

Исправление: задайте max_log_size_bytes = 52428800 (50 МБ) и max_log_files = 5. Ротация автоматическая.

Изменения конфигурации не применяются после reload

Причина: вы перезагрузили (SIGHUP) настройки, фиксированные при старте: private_key, api_host, metrics_enabled, настройки LiveKit и списки включения/отключения NIP.

Исправление: используйте nostrfy restart. В журнале в этом случае есть предупреждение «a restart is required».

Релей сам завершается

Причина: машина перезагрузилась или у релея закончилась память (OOM).

Исправление:

  1. Проверьте конец журнала: tail -50 nostrfy.log.
  2. Проверьте, перезагружалась ли машина: uptime (очень короткое время работы означает перезагрузку).
  3. Проверьте память: free -h.
  4. Запустите релей снова: nostrfy start.
Совет
Чтобы запускать nostrfy автоматически при загрузке, зарегистрируйте его как systemd-сервис со стартовой командой релея в ExecStart.

systemd не может запустить релей на порту 80

Systemd-сервис, работающий от root, может занимать порт 80. Если в User= задан обычный пользователь, либо используйте более высокий порт (например, 8080), либо добавьте AmbientCapabilities=CAP_NET_BIND_SERVICE в юнит.

Всё ещё не решено?

  1. Проверьте журнал: tail -100 nostrfy.log — там обычно названа прямая причина.
  2. Перепроверьте конфигурацию: nostrfy check — показывает предупреждения и ошибки.
  3. Соберите детали воспроизведения: что вы делали, какой клиент, какая точная ошибка.
  4. Спросите в репозитории проекта: https://github.com/iqbqioza/nostrfy — при подаче issue включите шаги воспроизведения и журнал.