Классический «трёхдневный неуловимый» баг
Нода жива: isConnected, порт открыт, xray в аптайме. Пинг до сервера идёт. Но клиент подключается через раз, соединение рвётся, а в логах ноды — тишина. Это почти всегда рассинхрон одного звена в цепочке Reality-хендшейка, а не «протокол виноват» и не «сеть шалит».
Модель такая. Endpoint = адрес + порт + инбаунд, это одна «дверь». Обрыв живёт внутри одной цепочки, которая обязана сходиться насквозь:
клиент → host (порт / sni / pbk / sid) → inbound (порт / serverNames / privateKey / shortId) → нода
Разъехалось одно звено — Reality рвётся. При этом TCP-пинг остаётся живым: обрыв происходит на уровне TLS-хендшейка, ниже видимости обычного мониторинга.
Порядок диагностики
1. Сервер вообще жив? Нода isConnected, xrayUptime > 0. Порт слушает: bash -c 'cat </dev/null >/dev/tcp/HOST/PORT'. Reality отвечает: openssl s_client -connect host:port -servername <SNI> — должен вернуть сертификат маскировочной цели.
2. SNI-аудит host↔inbound — первым делом. Поле sni хоста — это override, а не дубликат: пустое — клиент наследует SNI из serverNames инбаунда, и рассогласование невозможно в принципе. Заполненное с опечаткой — клиент шлёт чужой SNI, нода ждёт свой, Reality рвётся молча. Классика — опечатка в TLD: инбаунд настроен на www.example-shop.de, в хосте вписано example-shop.ru. Одного символа достаточно, чтобы локация «не работала», и по логам это не ловится — только сверкой двух полей. Поэтому держите host.sni пустым, не копируйте значение «для надёжности».
3. Fingerprint = chrome? randomized — грабли, а не «более скрытный вариант»: uTLS периодически генерирует ClientHello, который Reality-dest отвергает. Получаете handshake fail «через раз» при живой ноде. Первый шаг при таком симптоме — найти выбивающийся randomized среди chrome.
4. Дубль endpoint? Два видимых хоста на один адрес:порт — клиент мечется между ними.
5. Ключи сходятся? Публичный ключ хоста должен равняться производной от приватного ключа инбаунда. Дериватся локально: xray x25519 -i <privateKey> — сверьте с тем, что уходит клиенту.
Поле address: тихий отказ из-за запятой
Отдельный источник отказа при полностью исправной ноде — формат поля address у хоста. Это CSV-строка доменов. Хвостовая запятая ("a.tld, b.tld,") означает, что клиент попытается отрезолвить пустой хвост как hostname — и упадёт. Разделитель только запятая (пробел после — ок), без запятой в конце, все домены должны резолвиться.
Подводные камни
- Общий SNI на нескольких нодах с разными ключами пугает, но сам по себе ничего не ломает. Ломает только mismatch внутри одной пары host↔inbound.
randomizedможет годами маскировать соседний баг: каждый дозвон даёт разный ClientHello, поэтому две сессии на один ключ сосуществуют. Переход наchrome(детерминированный ClientHello) вскрывает скрытую коллизию — например, видимый хост и нога балансировщика на одном reality-инбаунде. Правило: разводить их по разным инбаундам и ключам (разные порты — :443 vision и :8443 xhttp).- Симптом «прямой хост работает всегда, а Авто скачет между странами» — не поломка, а штатная работа балансировщика leastLoad. Это отдельная история, см. гайд про балансировку exit-нод.
Вывод. «Пинг есть — коннекта нет» в Reality почти всегда решается сверкой цепочки host↔inbound, а не сменой протокола. Два самых частых корня: SNI-override с опечаткой и fingerprint randomized. Держите host.sni пустым и fingerprint chrome — целый класс неуловимых обрывов исчезает.