# Health-эндпоинт сервисов

Контракт, по которому любые мониторы экосистемы (hard-panel, скрипты агентов)
опрашивают `/health` сервисов и интерпретируют отклик. Эндпоинты уже есть у всех
сервисов, формата нет — фиксируется **минимальный** контракт, работающий «из
коробки» с любым уже существующим эндпоинтом, и **рекомендуемое расширение**,
к которому сервисы приходят постепенно.

Референсная реализация интерпретации: hard-panel (`panel/backend/app/services_probe.py`).

## 1. Минимальный контракт

Монитор опрашивает эндпоинт методом GET. Достаточно:

| Отклик | Интерпретация монитором |
|---|---|
| HTTP 2xx | сервис **в порядке (up)** |
| HTTP 2xx, тело-JSON с `status` из ok-набора (см. §2) | **up** |
| HTTP 2xx, тело-JSON с `status` из degraded-набора (см. §2) | **деградация (degraded)** |
| HTTP 2xx, тело-JSON, `status` — любая другая строка | **вниз (down)** |
| HTTP 3xx–5xx | **down** (редиректы следуются) |
| таймаут, DNS, отказ соединения | **down**, причина — текст ошибки |

Тело может вообще отсутствовать или быть любым (текст, HTML, JSON без поля
`status`) — чистый 2xx в порядке. Монитор не угадывает глубже: неизвестный
формат не становится «вниз» — кроме незнакомой строки `status` (если сервис
взялся ответить статусом, монитору его не угадать).

## 2. Поле `status`

Если тело — JSON-объект со строковым полем `status`, монитор опирается на него.
Сравнение без учёта регистра.

| Значение `status` | Монитор показывает |
|---|---|
| `ok`, `healthy`, `pass`, `passing`, `up` | в порядке |
| `degraded`, `warn`, `warning`, `partial`, `busy` | деградация |
| `down`, `error`, `fail`, `critical`, незнакомая строка | вниз |

Незнакомая строка — **консервативно вниз**: сервис ответил статусом, монитор
не имеет права считать его здоровым. Если нужна только дихотомия «в порядке /
не в порядке» — просто возвращайте `ok`.

## 3. Рекомендуемое расширение

Одинаковая, машину-читаемая детализация — к ней gnexus-сервисы приходят со временем:

```json
{
  "status": "ok",
  "service": "gnexus-creds",
  "version": "1.4.2",
  "checks": {
    "db":    { "status": "ok", "ms": 3 },
    "redis": { "status": "degraded", "detail": "replica lag 15s" }
  },
  "timestamp": "2026-10-04T12:00:00Z"
}
```

- `status` — агрегат: **худший** из `checks.*.status` (down > degraded > ok).
- `checks.{name}.status` — те же значения, что в §2.
- `checks.{name}.ms` — необязательная длительность подчинённой проверки, ms.
- `checks.{name}.detail` — необязательная причина degraded/down, одной строкой.
- `service`, `version`, `timestamp` — необязательные, для журналов.

Мониторы, написанные по §1–2, работают с таким телом без изменений.
Полный набор слоёв самосвидетельства — §5.

## 4. Требования к эндпоинту

- **Без авторизации** — `/health` не раскрывает данных; auth превращает
  мониторинг в «всегда вниз».
- **Дёшево** — опрос каждые ~30 с постоянно: только доступность (пинг БД —
  `SELECT 1`, не миграции); дороже ~100 ms — кэшируйте максимум на 10 с.
- **Быстро** — ответ < 500 ms; таймаут монитора — 5 s.
- **Идемпотентно** — без счётчиков метрик, писем, записей в БД.
- **Не логируйте каждый пинг** — сотни записей в минуту заслоняют настоящие ошибки.
- **Путь** — `/health` или `/healthz`, на выбор сервиса; монитор берёт полный URL
  из настроек.

## 5. Самосвидетельство (расширенное тело)

Ответ health может нести не только лайвность, но и самосвидетельство — необязательные
слои информации о себе. Всё ниже необязательно; монитор по минимуму (§1–2) обязан
незнакомые поля игнорировать, сервис с богатым телом ничего не ломает.

### Слои самосвидетельства

**Identity — кто я.**
`service` (самоназвание), `version`, `build` (git sha), `environment` (production/…),
`uptime` (секунды) или `since` (ISO — момент старта). Монитор сверяет `service` с
настройкой (защита от перепутанного URL), показывает версию и аптайм в карточке;
сброс `uptime` между опросами — сигнал «сервис перезапустился».

**Mode — сознательные состояния.**
`"mode": "maintenance" | "readonly" | "draining"` — сервис жив, это делается
намеренно (миграция, бэкап). Монитор показывает нейтральный бейдж «обслуживание»:
не degraded и не падение доступности.

**Gauges — рабочие показатели.**
`"gauges": {"queue_depth": 42, "db_pool_in_use": 7, "cache_hit_ratio": 0.87}` —
числа без интерпретаций. Пороги и суждения остаются монитору: `"cpu": 62` — хорошо,
`"cpu_warn": true` — плохо. Очереди, пулы, кэш, счётчики — всё, что сервис считает
полезным для стороннего взгляда.

### Ограничения
- **Без секретов и персональных данных.** Эндпоинт без авторизации — что в health,
  читает любой в сети: имена пользователей, токены, содержимое секретов, внутренняя
  топология — нельзя; версии, счётчики, режимы — можно.
- **Дёшево на пинге** (каждые ~30 с): gauges — из лёгкой статистики в памяти;
  что дороже — кэшировать (§4).
- **timestamp** обязателен для кэшированных ответов — монитор видит свежесть ответа.

## Ссылки

- Референс-потребитель: hard-panel — https://git.gnexus.space/root/hard-panel
  (спека и раздел «Сервисы» в его README).