- Docker-стенд работает с `openstack.mode: mock` и тестовыми адресами `203.0.113.10–12`; реальный OpenStack и учётные данные не нужны.
- Реальный стенд (systemd или Docker на нескольких хостах) — по шагам в [docs/SETUP.md](docs/SETUP.md).
## Конфигурация
Каждый компонент читает свой YAML (примеры — `configs/*.example.yaml`). Учётные данные OpenStack в YAML не хранятся — только *имена* переменных окружения.
| `openstack.mode` | `real` или `mock` (встроенная заглушка OpenStack для разработки и тестов) |
| `openstack.auth_method` | `token` (готовый токен проекта из `OS_TOKEN`, сам не обновляется) или `password` (логин/пароль Keystone, токен перевыпускается автоматически) |
| `openstack.*_env` | Имена переменных окружения: `OS_AUTH_URL`, `OS_PROJECT_ID`, `OS_REGION_NAME`, `OS_INTERFACE`, `OS_TOKEN` либо `OS_USERNAME`/`OS_USER_DOMAIN_NAME`/`OS_PASSWORD` |
| `orchestrator.poll_interval_seconds` | Период такта оркестратора (5) |
| `orchestrator.self_check_timeout_seconds`, `max_self_check_retries` | Ожидание self-check валидатора (60) и число его повторов (3) |
| `orchestrator.checking_window_seconds` | Сколько ждать результаты проверок, прежде чем подвести итог (120) |
| `orchestrator.lease_ttl_seconds`, `max_retries` | Лизинг адреса за валидатором (180) и число возвратов в очередь при его истечении (3) |
| `orchestrator.heartbeat_timeout_seconds` | После скольких секунд тишины валидатор или площадка считаются потерянными (30) |
| `orchestrator.fip_settle_seconds` | Только начальное значение: пауза между привязкой FIP и self-check; далее управляется на лету. Должно выполняться `fip_settle_seconds + self_check_timeout_seconds < lease_ttl_seconds` |
| `orchestrator.fip_scan_interval_seconds` | Периодический скан Floating IP (0 — выключен). Не выполняется, пока включён автоматический цикл |
| `aggregation.missing_counts_as_fail` | Отсутствие ответа источника засчитывается как провал (`true`) |
| `validators`, `sites`, `check_types`, `targets`, `inbound_checks` | Начальная загрузка пустой БД: валидаторы (`validator_id` + `os_port_id`), внешние площадки, типы и цели egress-проверок, порты inbound-проверок. Дальше источник истины — БД, правки через API/UI |
| `ip_addresses` | Адреса, которые доливаются в очередь при каждом старте (только новые) |
Настройки, меняющиеся на лету (пауза перед self-check, глубина истории, типы inbound-проверок, автоматический цикл, валидаторы, площадки, цели), хранятся в БД и правятся через API или страницу `/settings`.
## Архитектура
| Слой | Технологии |
|---|---|
| Backend | Go 1.26, `net/http` (маршруты Go 1.22), SQLite (чистый Go-драйвер `modernc.org/sqlite`, WAL, одно соединение), встроенные миграции `0001`–`0008` |
| `control-api` | Ведёт очередь адресов и реестр их истории, назначает валидаторов, агрегирует результаты. Единственная точка, с которой общаются все остальные компоненты | Хранит (SQLite) | Одна управляющая машина |
| `validator-agent` | Привязывает к себе выданный Floating IP и прогоняет egress-проверки до внешних целей | Без состояния | Каждая ВМ-валидатор в облаке |
| `prober` | Проверяет входящую доступность адреса снаружи (TCP/ICMP) — с независимой от облака сети | Без состояния | Каждая внешняя тестовая площадка |
| `admin-dashboard` | Браузерная админ-панель — то же, что API `control-api`, но графически. Опционален | Без состояния | Любая машина с доступом к `control-api` |
Все четыре общаются **только** через HTTP API `control-api`. Pull-модель: `validator-agent` и `prober` сами опрашивают `control-api`, он к ним не обращается.
- Путь адреса: `queued` → `assigning_fip` → `awaiting_self_check` → `checking` → `aggregating` → `done` или `failed`. Все переходы, кроме команд оператора, выполняет оркестратор сам по таймеру.
- Терминальные состояния: `done`, `failed`, `occupied`. `occupied` — Floating IP к моменту привязки уже занят чужим портом: цикл проверки не стартует, `overall_result` пуст, это не `fail`.
- Оператор может отменить проверку (`failed` + `cancelled`), перепроверить завершённый адрес (возврат в `queued`) или удалить адрес из очереди.
- Адреса обрабатываются в порядке `sequence`; каждый свободный валидатор получает следующий `queued`-адрес под лизинг. Истёкший лизинг возвращает адрес в очередь, после `max_retries` — в `failed`.
**Итоговый результат**
-`pass` — прошли все проверки: исходящие и все настроенные площадки по всем портам и ICMP. `partial` — часть прошла, часть нет. `fail` — не прошла ни одна проверка или адрес не дошёл до проверок (self-check не подтвердился). `cancelled` — остановлено оператором.
- Итог подводится, когда отчитались валидатор и все настроенные площадки либо истекло `checking_window_seconds`. Молчание источника — провал (`aggregation.missing_counts_as_fail`).
- Площадки опциональны: с пустым списком `sites` итог строится только по egress-проверкам.
**Реестр**
- Запись реестра создаётся при первой постановке адреса и не удаляется: она переживает удаление из очереди и повторное добавление.
- История проверок хранится по циклам; `history_retention_cycles` ограничивает глубину (0 — без ограничения), сама запись реестра остаётся.
**Автоматический цикл** (опционально, по умолчанию выключен)
- Один цикл: очистить очередь → просканировать Floating IP → дождаться, пока все адреса станут терминальными → пауза `interval_seconds` → заново. Пауза считается от завершения цикла.
-`interval_seconds` — по умолчанию 3600, минимум 60; `max_run_seconds` — максимальное ожидание проверок (0 — без лимита), по истечении исход `timeout`.
- Исходы цикла: `completed`, `no_free_ips`, `timeout`, `error`, `stopped`. Опустевшая очередь посреди цикла считается завершением.
- Состояние хранится в БД и переживает перезапуск; выключение не прерывает идущие проверки. Подробности — [docs/USAGE.md](docs/USAGE.md#автоматический-цикл-проверок).
**Удаление и сканирование**
- Удаление (точечное, списком, «очистить всё») убирает строку очереди, но не историю в реестре.
- Скан Floating IP ставит в очередь только свободные адреса (не привязанные ни к одному порту); уже идущие проверки не трогаются.
- Тело запросов и ответов — JSON. Успех — `200`, `204` — когда данных нет (например, у валидатора нет назначения).
- Ошибки — `4xx`/`5xx`с телом `{"error": "..."}`; неизвестная сущность — `404`, конфликт состояния — `409`, неверные данные — `400`, ошибка OpenStack при скане — `502`.
- Времена — RFC 3339. Полная спецификация и примеры `curl` — [docs/API.md](docs/API.md).
## Безопасность
- **Аутентификации API нет** — это известное ограничение текущей версии: эндпоинты, включая административные, доступны любому, кто достучится до порта `control-api`. Доступ ограничивается на уровне сети/файрвола ([docs/SETUP.md](docs/SETUP.md#сетевые-доступы)); bearer-токен — направление доработки.
- Дашборд — тонкий прокси к API без собственных учётных записей и состояния; публиковать его так же нужно только в доверенном сегменте или за reverse-proxy с авторизацией.
- Учётные данные OpenStack передаются только через переменные окружения процесса (`EnvironmentFile=` в systemd, `OS_*` в Docker) и не попадают в YAML; режим `password` перевыпускает токен сам, режим `token` — нет.
- Компоненты работают по pull-модели: на валидаторах и площадках не нужно открывать входящие порты для `control-api`.
- Бинарники статические, без `cgo`; целостность проверяется `sha256sum -c bin/SHA256SUMS`.
## Публикация и эксплуатация
- **Бинарники.** Готовые linux/amd64 лежат в `bin/` и **не обновляются автоматически**: после правок кода пересоберите их и обновите `SHA256SUMS` (команды — [docs/SETUP.md](docs/SETUP.md#вариант-b-сборка-из-исходников)); Dockerfile копируют именно `bin/*`.
- **systemd.** Юниты в `deploy/systemd/`; у`control-api` — `EnvironmentFile` с учётными данными OpenStack.
- **Docker.** `deploy/docker/docker-compose.yml` + `docker-compose.override.yml` (dev, mock) или `docker-compose.prod.yml` (без публикации портов, `restart: unless-stopped`). Какие сервисы поднимаются на хосте, задаёт `COMPOSE_PROFILES`: `control-plane`, `dashboard`, `prober`, `validator`. БД — volume `cloud-ip-validator-db`.
- **Реальный стенд.** `rxprod-compose/` — compose с готовыми образами, собственным `control-api.yaml` и каталогом БД `capi-db/`; `.env` с учётными данными в репозиторий не входит.
- **Миграции** применяются при старте `control-api`; версия схемы — `PRAGMA user_version`. Начальная загрузка (`validators`, `sites`, `targets`, `check_types`, `inbound_checks`) выполняется только в пустые таблицы.
- Остановка (`SIGTERM`) корректно завершает HTTP-сервер и фоновые циклы. Состояние автоцикла и очереди сохраняется в БД.
| `/overview` | Счётчики по состояниям, «текущая» и «последние завершённые» проверки, поиск по IP и фильтр по статусу, индикатор автоцикла; обновляется без перезагрузки |
| `/ips`, `/ips/{ip}` | Очередь: добавление адресов, «Сканировать Floating IP», перепроверка, отмена, удаление (в том числе списком и «Очистить всё»); детали и события адреса |
| `/registry`, `/registry/{ip}` | Реестр всех адресов и полная история проверок адреса; поиск и фильтр сохраняются в адресной строке |
| `/validators`, `/sites`, `/targets`, `/check-types` | Управление валидаторами, внешними площадками, группами целей и типами проверок |
| `/settings` | Панель «Автоматический цикл», пауза перед self-check, глубина истории, TCP-порты и ICMP для inbound-проверок |
- Порядок блоков на `/overview` фиксирован: статистика → фильтр → таблицы; поллится только блок таблиц, поэтому набранный в фильтре текст не сбрасывается.
- Ошибки control-api показываются баннером; при недоступном API страница остаётся рабочей.
- Тёмная и светлая темы, переключатель в шапке.
## Тесты
```bash
go build ./... && go vet ./... && go test ./... # юнит-тесты: db, orchestrator, httpapi, dashboard, agentcore, probercore, checkrunner, openstack
go test -race ./internal/orchestrator ./internal/httpapi ./internal/dashboard ./internal/db
scripts/run-local-e2e.sh # сквозной прогон: lease-reclaim, перепроверка, автоматический цикл
```
Юнит-тесты используют временную SQLite и `MockClient`, внешних ресурсов не требуют. E2E поднимает все компоненты локальными процессами и завершается ненулевым кодом при провале проверок автоцикла — [docs/LOCAL_E2E.md](docs/LOCAL_E2E.md).
| Динамическое управление конфигурацией и очередью через API | [план](docs/PLAN_API_CONFIG_MANAGEMENT.md) · [API](docs/API.md#управление-очередью-и-конфигурацией) |
| Пауза перед self-check после привязки Floating IP (`fip_settle_seconds`) | [план](docs/PLAN_FIP_SETTLE_DELAY.md) · [USAGE](docs/USAGE.md#пауза-перед-self-check-fip_settle_seconds) |
| Механизм аутентификации OpenStack-клиента (token / password) | [план](docs/PLAN_OPENSTACK_AUTH.md) |
| 2026-09-23 | Сканирование Floating IP и устойчивый реестр адресов с настраиваемой глубиной истории | [USAGE](docs/USAGE.md#реестр-адресов-и-глубина-истории) |
| 2026-09-23 | Поиск по IP и фильтр по статусу на «Обзоре» и «Реестре» | [DASHBOARD](docs/DASHBOARD.md) |
| 2026-09-23 | Пошаговое руководство по развёртыванию в Docker | [SETUP](docs/SETUP.md) |