📡 Внешние диагностические проберы
August 9, 2026 · View on GitHub
Read this in English.
Пробер — небольшой Go-бинарник, который вы ставите на свой сервер. Он получает от панели скрытую подписку, поднимает то же ядро, что и клиенты Click Connect, подключается к вашим нодам так же, как это делает клиент, и присылает результат.
📖 Зачем это нужно
Панель видит свой контур управления: жив ли агент ноды, запущен ли процесс, сколько трафика учтено. Нода может выглядеть там здоровой, пока при этом:
- порт режется конкретным оператором;
- маскировочный destination REALITY мёртв, и TLS-хендшейк не завершается;
- пользователь не доехал до запущенного Xray, и креды отвергаются;
- туннель поднят, но данные не идут из-за сломанного outbound или правила ACL;
- IP выхода попал в чёрный список, и конкретный ресурс блокируется.
Пробер проходит клиентский путь целиком и называет, что именно из этого произошло.
🧭 Как это работает
┌──────────────────────┐ ┌──────────────────────┐
│ ПРОБЕР │ 1. enroll (разовый) │ ПАНЕЛЬ │
│ ваш сервер │ ───────────────────────────▶ │ │
│ │ │ │
│ ┌────────────────┐ │ 2. профиль + подписка │ │
│ │ sing-box │ │ ◀─────────────────────────── │ │
│ └───────┬────────┘ │ │ │
│ │ │ 4. gzip NDJSON │ │
│ │ │ ───────────────────────────▶ │ │
└──────────┼───────────┘ └──────────────────────┘
│
│ 3. реальные подключения — свой SOCKS-порт на каждый инбаунд
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ нода A нода B нода C виртуальная нода (группа urltest) │
└────────────────────────────────────────────────────────────────────────────┘
- Регистрация. Панель выдаёт одноразовый токен, действующий 24 часа. Пробер один раз меняет его на постоянный; для проверки хранятся только SHA-256-хеши.
- Профиль. Пробер спрашивает, что проверять: ноды, инбаунды, чек-лист ресурсов, периодичность и бюджет замера скорости.
- Подписка. У каждого пробера есть скрытый пользователь со своей подпиской. Он исключён из всех списков и статистики, поэтому трафик пробера никогда не смешивается с клиентским.
- Проверки. На каждый проверяемый инбаунд поднимается отдельный локальный SOCKS-листенер, поэтому каждое измерение привязано к конкретному инбаунду конкретной ноды.
- Отчёты. Результаты агрегируются локально в окна и отправляются в gzip. Недоставленные батчи складываются в спул на диске, поэтому недоступность панели не стоит вам ни одного измерения.
Панель никогда не подключается к проберу: всё инициирует он сам, поэтому пробер работает за NAT и на домашних каналах.
🚀 Установка
Сначала включите фичу: Настройки → Проберы → Включить внешние проберы. Пока она выключена, панель отклоняет и регистрацию, и отчёты.
Дальше Проберы → Добавить пробер. Имя стоит давать по точке наблюдения (Москва, МТС полезно, probe-1 — нет), после чего выполните сгенерированную команду на том хосте, откуда хотите проверять.
Linux / macOS:
curl -fsSL https://github.com/ClickDevTech/CELERITY-panel/releases/latest/download/celerity-probe-install.sh \
| sudo PANEL_URL='https://panel.example.com' ENROLL_TOKEN='<токен>' sh
Windows (PowerShell от администратора):
$env:PANEL_URL='https://panel.example.com'; $env:ENROLL_TOKEN='<токен>'
irm https://github.com/ClickDevTech/CELERITY-panel/releases/latest/download/celerity-probe-install.ps1 | iex
Установщик скачивает пробер и ядро, один раз проходит регистрацию и ставит службу (systemd, launchd или служба Windows). Токен регистрации передаётся через окружение, потому что таблица процессов доступна другим локальным пользователям.
Ядро — sing-box-lx, та же сборка, что стоит в приложениях Click Connect: апстрим sing-box плюс транспорты, которые умеет публиковать панель. Апстримное ядро отвергает целиком конфигурацию, где есть нода на XHTTP, — пробер на нём ослеп бы разом по всем нодам. Установка поверх апстримного ядра заменяет его, а если у запущенного ядра нет with_xhttp, при наличии XHTTP-нод пробер пишет об этом в лог. Переменная CORE_REPO позволяет взять ядро из другого репозитория.
| Платформа | Служба | Каталог данных |
|---|---|---|
| Linux | systemd, работает от аккаунта celerity-probe | /var/lib/celerity-probe |
| macOS | launchd | /usr/local/var/celerity-probe |
| Windows | служба Windows, ACL каталога ограничен SYSTEM и администраторами | %ProgramData%\celerity-probe |
PANEL_URL обязан быть HTTPS. Токен пробера аутентифицирует каждый запрос, а отчёты описывают весь ваш парк нод, поэтому обычный HTTP отклоняется — исключения только для панели на loopback или при PROBE_ALLOW_INSECURE=1 для лабораторного стенда.
⚙️ Настройки
| Настройка | По умолчанию | Что задаёт |
|---|---|---|
| Интервал проверки подключения | 300 с | Как часто прозванивается каждый инбаунд каждой ноды |
| Интервал проверки ресурсов | 3600 с | Как часто чек-лист запрашивается через каждую ноду |
| Интервал отчётов | 900 с | Как часто окна уезжают в панель |
| Хранение | 30 дней | Для сырых окон; часовые роллапы живут втрое дольше |
| Лимит трафика пробера | 5 ГБ | Ограничение скрытого пользователя, применяется и к уже созданным проберам |
| Замер скорости | выключен | Каждая нода раз в 180 мин, 20 МБ и 5 с на прогон, 1 ГБ в сутки |
| Чек-лист ресурсов | пусто | Пара id + url в строке, проверяется через каждую ноду |
У скорости своя периодичность: вы задаёте, как часто замеряется одна нода, пробер размазывает этот период по своим инбаундам, а дневной бюджет остаётся предохранителем. Объём замера задаёт и потолок измерения — первая четверть прогона, но не больше 512 КБ, отбрасывается как разгон, а прогон, выбравший объём раньше таймаута, показывается как нижняя граница ≥ X Мбит/с; страница настроек считает этот потолок и суточный расход прямо по ходу ввода.
Про масштаб. Количество хранимых рядов — это проберы × ноды × ресурсы. Панель предупреждает, когда произведение переваливает за несколько тысяч; в этот момент стоит увеличить интервалы.
🔍 Как читать результаты
Результаты всегда показываются по точкам наблюдения, и вердикт пробера не меняет node.status. Неуспешная проверка может быть вызвана и аплинком самого пробера; чтобы различать эти случаи, нужен кворум проберов в разных сетях.
Проберы → История открывает отчёт по одной точке наблюдения за 6 часов, 24 часа, 7 или 30 дней. Сверху — числа по всему периоду: доля успешных проверок, задержка p50/p95, хендшейк и время до первого байта, скорость, сколько нод с проблемами, сколько ресурсов из чек-листа заблокировано и сколько времени пробер вообще ничего не присылал. Следом — разбивка отказов по кодам: именно она превращает красную полоску в конкретное действие.
Ниже — по секции на ноду, худшие сверху, здоровые свёрнуты. Каждый инбаунд получает полоску на фиксированной сетке времени: один сегмент — одно окно, цвет — вердикт, под полоской на той же сетке нарисована задержка. Сетка фиксированная, поэтому промежуток, когда пробер молчал, остаётся видимой дырой, а не схлопывается. Клик по сегменту раскрывает окно целиком: попытки и успехи, счётчики отказов, p50/p95, хендшейк, время до первого байта, скорость, адрес выхода, а для виртуальной ноды — какой лист реально выбрал балансировщик.
Периоды длиннее 12 часов читаются из часовых сводок, поэтому месяц обходится так же дёшево, как сутки. Если парк настолько велик, что данные не помещаются в одно чтение, теряется дальний конец периода, а не свежие окна, и панель об этом предупреждает.
Коды отказов указывают на конкретную причину:
| Код | Что значит | Куда смотреть |
|---|---|---|
net_unreachable | TCP/UDP не доходит | Порт режется оператором, фаервол или нода лежит |
handshake_failed | TLS/REALITY не завершается | Мёртвый маскировочный destination, неверный SNI, вмешательство DPI |
auth_rejected | Туннель есть, креды отвергнуты | Пользователь не доехал до запущенного ядра — проблема синхронизации |
tunnel_no_data | Авторизация прошла, но данные не идут | Сломанный outbound или правило ACL |
degraded | Работает, но медленно | Перегрузка или плохой маршрут именно до этой точки |
core_down | Не работал собственный sing-box пробера | Проблема на хосте пробера |
Результаты по ресурсам вынесены отдельно: заблокированный ресурс указывает на гео-блок или адрес выхода в чёрном списке. Блокировкой считаются только 403 и 451; 500 или транспортная ошибка записывается как неуспешная проверка. У каждого ресурса своя полоска, URL, по которому он проверялся, последний HTTP-статус и текст последней ошибки.
Замеры скорости вынесены в отдельный блок, потому что они идут по кругу в рамках дневного бюджета и потому редки и неравномерны. Вместо полоски, которая была бы почти пустой, показаны только те ноды, где замеры реально были — медленные сверху: медиана по настоящим замерам, пик, их количество и время последнего, а сами точки стоят там, где замер произошёл. Окна без замера в медиану не входят и не считаются нулями, часовая сводка хранит медиану часа, а не его лучшую минуту, иначе затяжная просадка спряталась бы за одним удачным всплеском, а ≥ перед числом означает, что замер выбрал объём раньше таймаута.
Два предупреждения, на которые стоит реагировать:
- Тот же хост. Если IP выхода пробера совпадает с одной из ваших нод, его трафик до этой ноды не покидает машину. Интерфейс это помечает.
- Виртуальные ноды. Виртуальная нода — это группа
urltest. Проверяются и группа, и её листья, а в результате группы записывается, какой лист реально выбрал балансировщик.
🪝 Оповещения
Вебхуки срабатывают на переходах состояния, поэтому оповещение приходит, не дожидаясь роллапа:
| Событие | Когда срабатывает |
|---|---|
probe.node_unreachable | Инбаунд ноды перешёл в состояние отказа из этой точки |
probe.target_unreachable | Ресурс из чек-листа стал недоступен через ноду |
probe.offline | Пробер пропустил три интервала отчёта |
Локальный отказ ядра (core_down) никогда не поднимает алерт по ноде.
🤖 AI-ассистент
MCP-инструмент query_probes отдаёт те же данные AI-клиенту. Ему нужен отдельный скоуп probes:read, потому что данные проберов раскрывают точки наблюдения и адреса выхода. Подробности — в руководстве по MCP.
🔐 Модель безопасности
- У пробера есть только клиентские креды. Никаких прав на нодах, никакого SSH, никакой сессии панели.
- Токены проверяются по SHA-256-хешам и сравниваются за постоянное время. Постоянный токен дополнительно хранится в зашифрованном виде, чтобы панель могла повторно показать команду установки.
- Удаление пробера немедленно убирает его скрытого пользователя, подписку и результаты и проталкивает удаление в запущенные Xray. Привязки к IP или ASN нет, поэтому срок жизни утёкших кредов ограничен скоростью отзыва.
- Второе ограничение — лимит трафика скрытого пользователя: утёкшая подписка пробера ограничена этим объёмом.
- Приём данных идемпотентен. Повторно доставленный батч подтверждается без повторной записи, что делает безопасной доставку «хотя бы один раз».
🧯 Диагностика проблем
| Симптом | Вероятная причина |
|---|---|
| Пробер остался в статусе «ожидает установки» | Токен регистрации истёк (24 ч) или уже использован — перевыпустите его в панели |
По всем нодам core_down | На хосте пробера нет sing-box или он не исполняемый; смотрите лог службы |
По всем нодам auth_rejected | Пользователь пробера не доехал до нод — запустите синхронизацию и проверьте статус нод |
На одной ноде net_unreachable, на остальных всё в порядке | Порт режется на пути из этой точки наблюдения |
| После установки нет данных | Фича выключена в настройках либо пробер не достучался до PANEL_URL по HTTPS |
Логи: sudo journalctl -u celerity-probe -f (Linux — без root журнал ничего не покажет), tail -f /usr/local/var/celerity-probe/probe.log (macOS), лог службы в каталоге данных (Windows).
📚 Исходники
| Файл | Описание |
|---|---|
probe/ | Сам пробер (отдельный Go-модуль) |
src/routes/probe.js | Эндпоинты регистрации, профиля и приёма данных |
src/services/probes/ | Сервисы регистрации, манифеста, приёма и роллапа |
src/routes/panel/probes.js | Админский интерфейс и JSON API |
src/mcp/tools/probes.js | MCP-инструмент query_probes |