diff --git a/README.md b/README.md index b71c0b5..77416eb 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,11 @@ | `GHARD_PANEL_PORT` | `8000` | host-порт (docker-compose) | | `GHARD_OFFLINE_MULTIPLIER` | `3` | offline = нет пакетов дольше N × interval | | `GHARD_SHARE_INTERVAL` | `60` | период опроса сетевых хранилищ, сек | +| `GHARD_PUBLIC_IP` | *(пусто)* | ожидаемый публичный IP площадки с сервисами — для сверки публичного DNS; пусто — сверка не выполняется | +| `GHARD_PUBLIC_RESOLVER` | `1.1.1.1` | публичный резолвер для внешней проверки (в обход локального split-horizon DNS) | +| `GHARD_EXTERNAL_INTERVAL` | `60` | период внешней проверки сервисов, сек | +| `GHARD_EXTERNAL_TIMEOUT` | `5` | бюджет одной внешней пробы (DNS + TCP + TLS + строка статуса), сек | +| `GHARD_EXTERNAL_ALERT_AFTER` | `300` | столько секунд непрерывного отказа — и только тогда событие в Synapse | | `GHARD_SYNAPSE_URL` | *(пусто)* | URL Synapse (Gnexus Synapse Integration); пусто — отчёт о событиях отключён | | `GHARD_SYNAPSE_API_KEY` | *(пусто)* | ключ `syn_*` источника hard-panel (выдаётся один раз в админке Synapse) | | `GHARD_SYNAPSE_DEFAULT_SOURCE` | `hard-panel` | имя источника, зарегистрированное в Synapse | @@ -339,6 +344,8 @@ - `GET /api/v1/shares/{id}/samples?since=…` — история заполнения хранилища - `GET/POST/PATCH/DELETE /api/v1/services[/{id}]` — health-чеки сервисов (CRUD) - `GET /api/v1/services/{id}/samples?since=…` — история проб +- `GET /api/v1/services/{id}/external_samples?since=…` — история внешних + проверок публичного пути - `GET /api/v1/services/{id}/incidents?limit=50` — журнал моментов недоступности (периоды down+degraded из истории проб; у идущего периода `end=null`) @@ -371,6 +378,34 @@ чипами в карточке). Сырое тело не хранится; известные поля берутся по белому списку. +### Внешняя доступность + +У сервиса может быть необязательное поле **публичного URL** +(`https://…`, порт 443). Тогда панель дополнительно проверяет публичный путь: +публичный DNS (по умолчанию через 1.1.1.1, в обход локального split-horizon) → +ожидаемый публичный IP `GHARD_PUBLIC_IP` → TLS с SNI и полной проверкой +сертификата → `GET` публичного пути. Это ловит поломки, которых не видит +внутренний health-чек: DNS ведёт не на тот адрес (`dns_mismatch`), домена нет +(`dns_fail`), истёк/не тот сертификат (`tls_fail`), снаружи закрыт порт +(`down`), reverse proxy отдаёт 5xx или 421 (`http_fail`). Состояние `up` не +следует редиректам, а 3xx/4xx считаются доступностью — проверяется +достижимость публичного входа, а не health-семантика. Если сам резолвер +недоступен, точка помечается `probe_error` и **не** участвует в алертах. + +В списке и на странице сервиса рядом с внутренним статусом появляется второй +бейдж «снаружи: …» (подсказка — причина, адреса из DNS, срок сертификата); на +странице сервиса — отдельный график времени отклика публичного пути. +История внешних проверок — 7 дней. + +Непрерывный отказ дольше `GHARD_EXTERNAL_ALERT_AFTER` (по умолчанию 5 минут) +даёт ровно одно событие `service_external_down` в журнал и Synapse; после +восстановления — одно `service_external_recovered`, и только если down-событие +действительно отправлялось. Короткие моргания уведомлений не дают. Смена +публичного URL сбрасывает состояние и историю проверок (график не смешивает +два домена). Проба запускается с самой панели, поэтому внешний файрвол +*перед* панелью она не проверяет; для доменов за Cloudflare-прокси ожидаемый +IP не совпадёт с DNS — нужны DNS-only записи. + Карточки сервисов пытаются подтянуть мета с главной страницы origin'а (`