diff --git a/README.md b/README.md
index 1fa724f..201dfc0 100644
--- a/README.md
+++ b/README.md
@@ -19,9 +19,10 @@ control-api.
| Документ | Для чего |
|---|---|
-| [docs/SETUP.md](docs/SETUP.md) | Сборка, конфигурация, первый запуск стенда — с нуля |
+| [docs/SETUP.md](docs/SETUP.md) | Развёртывание из готовых бинарников (`bin/`) или сборка из исходников, конфигурация, первый запуск стенда — с нуля |
| [docs/USAGE.md](docs/USAGE.md) | Повседневная работа: постановка адресов в очередь, наблюдение за статусом, разбор результатов |
| [docs/API.md](docs/API.md) | Спецификация HTTP API control-api и примеры запросов (curl) |
+| [docs/DIAGRAMS.md](docs/DIAGRAMS.md) | Диаграммы потоков данных: control plane, поток проверки до целевого сервера, поток телеметрии |
| [docs/LOCAL_E2E.md](docs/LOCAL_E2E.md) | Полностью офлайн-прогон всей системы одним скриптом — без реального облака и интернета |
## Быстрый старт (60 секунд, без OpenStack)
@@ -40,8 +41,9 @@ scripts/run-local-e2e.sh
## Быстрый старт (реальный стенд)
-1. Соберите три бинарника и подготовьте конфиги —
- [docs/SETUP.md](docs/SETUP.md).
+1. Возьмите готовые бинарники из `bin/` (Linux x86_64, статические, без
+ зависимостей) или соберите из исходников, подготовьте конфиги —
+ [docs/SETUP.md](docs/SETUP.md#получение-бинарников).
2. Разверните `control-api` на управляющей машине, `validator-agent` —
на каждой ВМ-валидаторе, `prober` — на каждой из трёх площадок
([пошагово в docs/SETUP.md](docs/SETUP.md#развёртывание-control-api)).
diff --git a/bin/SHA256SUMS b/bin/SHA256SUMS
new file mode 100644
index 0000000..03d9480
--- /dev/null
+++ b/bin/SHA256SUMS
@@ -0,0 +1,3 @@
+f2649ce9ceb052b9d3658cb655b62ef6db4df679bd845850699017a40567f1f9 control-api
+e28841e4956b098ccd8c276356ecab3e2a2c278889c6ac1b197163f3928a146e validator-agent
+481a6c7fa79b12c9aeecf93411b9329b3023331be2aeb8031bda780b204e5eb1 prober
diff --git a/bin/control-api b/bin/control-api
new file mode 100755
index 0000000..ae16ea6
Binary files /dev/null and b/bin/control-api differ
diff --git a/bin/prober b/bin/prober
new file mode 100755
index 0000000..99da874
Binary files /dev/null and b/bin/prober differ
diff --git a/bin/validator-agent b/bin/validator-agent
new file mode 100755
index 0000000..0e6d152
Binary files /dev/null and b/bin/validator-agent differ
diff --git a/docs/DIAGRAMS.md b/docs/DIAGRAMS.md
new file mode 100644
index 0000000..6669b5e
--- /dev/null
+++ b/docs/DIAGRAMS.md
@@ -0,0 +1,207 @@
+# Диаграммы потоков данных
+
+Три диаграммы, поясняющие устройство системы на уровне потоков данных —
+дополнение к [API.md](API.md) (протокол) и [USAGE.md](USAGE.md) (работа
+оператора). Диаграммы — в формате Mermaid, рендерятся нативно на
+GitHub/GitLab и в большинстве современных Markdown-просмотрщиков.
+
+## Как читать диаграммы
+
+Единое условное обозначение стрелок для всех диаграмм:
+
+| Обозначение | Значение |
+|---|---|
+| `-->` тонкая сплошная | Управляющий вызов / API-запрос |
+| `-.->` пунктирная | Ответ, уведомление, асинхронный результат |
+| `==>` жирная сплошная | Реальный сетевой трафик проверки (data-plane) — именно то, что валидируется, а не служебный вызов |
+
+## Содержание
+
+- [1. Control plane сервиса](#1-control-plane-сервиса)
+- [2. Поток данных: от валидатора к целевому серверу (egress-проверка)](#2-поток-данных-от-валидатора-к-целевому-серверу-egress-проверка)
+- [3. Поток данных телеметрии](#3-поток-данных-телеметрии)
+
+---
+
+## 1. Control plane сервиса
+
+Кто кем управляет и через какой канал: конфигурация оператора, HTTP API
+control-api, фоновый оркестратор, база данных, вызовы к OpenStack и опрос
+со стороны validator-agent/prober.
+
+```mermaid
+flowchart TB
+ subgraph OP["Оператор"]
+ CFG["control-api.yaml
(validators, sites, targets,
ip_addresses, check_types)"]
+ ENV["control-api.env
(OS_AUTH_URL, OS_TOKEN, ...)"]
+ ADMIN["curl /api/v1/admin/*"]
+ end
+
+ subgraph CAPI["control-api (управляющая машина, 1 экземпляр)"]
+ HTTP["HTTP API
/api/v1/agents/*
/api/v1/probers/*
/api/v1/admin/*
/healthz · /whatsmyip"]
+ ORCH["Оркестратор: Tick раз в
poll_interval_seconds
claim → associate FIP →
ожидание self-check →
checking → aggregate → release
+ lease sweep + heartbeat sweep"]
+ DB[("SQLite
validators / ip_queue
checks / events")]
+ OSCLIENT["OpenStack-клиент
(mode: mock | real)"]
+ end
+
+ OS["OpenStack Neutron
Floating IP API"]
+
+ VA["validator-agent ×N
(на каждой ВМ-валидаторе)"]
+ PR["prober ×3
(на каждой внешней площадке)"]
+
+ CFG -->|"читается при старте
(инициализация validators, ip_queue)"| CAPI
+ ENV -->|"переменные окружения процесса"| OSCLIENT
+ ADMIN --> HTTP
+ HTTP --> ORCH
+ ORCH <--> DB
+ ORCH --> OSCLIENT
+ OSCLIENT -->|"associate / disassociate
floating ip"| OS
+
+ VA <-->|"register, heartbeat,
GET assignment,
POST self-check/events/
results/complete"| HTTP
+ PR <-->|"register,
GET assignments,
POST results"| HTTP
+```
+
+**Пояснение.** `control-api` — единственный компонент с состоянием и
+единственная точка принятия решений (какой IP кому назначить, когда
+считать проверку завершённой). Конфигурация читается один раз при
+старте процесса (горячей перезагрузки нет — изменения требуют
+`systemctl restart control-api`, см. [SETUP.md](SETUP.md)). Оркестратор
+работает по таймеру независимо от HTTP-запросов — назначение IP
+валидаторам и агрегация результатов не привязаны к конкретному входящему
+запросу, а выполняются фоновым циклом `Tick`. `validator-agent` и
+`prober` — активная сторона: они сами инициируют все HTTP-запросы к
+control-api (pull-модель), сам control-api к ним не обращается.
+
+---
+
+## 2. Поток данных: от валидатора к целевому серверу (egress-проверка)
+
+Как валидатор проверяет, что через назначенный ему публичный адрес
+реально работает исходящий доступ в интернет.
+
+```mermaid
+flowchart LR
+ subgraph VM["ВМ-валидатор (сервисный проект OpenStack)"]
+ AGENT["validator-agent"]
+ end
+
+ CAPI["control-api"]
+ FIP(["Floating IP
203.0.113.10
(адрес под проверкой,
привязан оркестратором заранее)"])
+
+ subgraph TARGETS["Целевые серверы (targets из конфига)"]
+ T1["hub.docker.com"]
+ T2["github.com"]
+ T3["packages.ubuntu.com"]
+ end
+
+ AGENT -->|"1 GET /api/v1/whatsmyip
(self-check)"| CAPI
+ CAPI -.->|"2 наблюдаемый исходящий IP"| AGENT
+ AGENT ==>|"3 весь исходящий трафик ВМ
идёт через FIP (SNAT облака)"| FIP
+ FIP ==>|"4 HTTPS GET"| T1
+ FIP ==>|"4 HTTPS GET"| T2
+ FIP ==>|"4 ICMP echo"| T3
+```
+
+**Пояснение.** Шаги 1–2 — самопроверка (self-check): агент обращается к
+`control-api` и сравнивает адрес, с которого пришёл его собственный
+запрос, с адресом, который ему назначен. Поскольку весь исходящий трафик
+ВМ реально идёт через привязанный Floating IP (шаг 3, SNAT на стороне
+облака), совпадение подтверждает, что назначение применилось корректно —
+только после этого агент переходит к шагу 4 и выполняет проверки из
+`check_config` (HTTPS/ICMP/опционально SSH) против целей из конфига.
+Каждый результат отправляется обратно в control-api сразу после
+выполнения (см. диаграмму телеметрии ниже) — сам этот data-plane трафик
+(шаги 3–4) в control-api не проходит и им не наблюдается напрямую,
+control-api видит только заявленный агентом результат.
+
+### Дополнительно: обратное направление (пробер к валидатору)
+
+Тот же Floating IP одновременно проверяется и «снаружи» — с трёх внешних
+площадок, независимо от исходящих проверок агента:
+
+```mermaid
+flowchart LR
+ subgraph SITES["3 внешние площадки"]
+ P1["prober site-1"]
+ P2["prober site-2"]
+ P3["prober site-3"]
+ end
+
+ FIP(["тот же Floating IP
203.0.113.10"])
+ VM["ВМ-валидатор
(слушает 22/80/443/8080)"]
+
+ P1 ==>|"TCP connect + ICMP echo"| FIP
+ P2 ==>|"TCP connect + ICMP echo"| FIP
+ P3 ==>|"TCP connect + ICMP echo"| FIP
+ FIP -.-> VM
+```
+
+Именно сочетание двух направлений (egress от валидатора и inbound от трёх
+площадок) и даёт полную картину: адрес может нормально работать «наружу»,
+но быть заблокирован для конкретных внешних сетей — это увидит только
+inbound-проверка, и наоборот.
+
+---
+
+## 3. Поток данных телеметрии
+
+Как результаты проверок и события аудита от validator-agent и трёх
+проберов попадают в базу данных control-api, агрегируются в итоговый
+результат и становятся видны оператору.
+
+```mermaid
+flowchart TB
+ subgraph SOURCES["Источники телеметрии"]
+ VA["validator-agent
(egress-проверки + self-check)"]
+ P1["prober site-1"]
+ P2["prober site-2"]
+ P3["prober site-3"]
+ end
+
+ subgraph API["HTTP API control-api"]
+ EP1["POST /agents/{id}/events"]
+ EP2["POST /agents/{id}/self-check"]
+ EP3["POST /agents/{id}/results"]
+ EP4["POST /probers/{site_id}/results"]
+ end
+
+ subgraph DB["SQLite (control-api)"]
+ EVENTS[("events
журнал аудита")]
+ CHECKS[("checks
результаты проверок,
source=egress|inbound-site-N")]
+ IPQ[("ip_queue
state, *_complete,
overall_result")]
+ end
+
+ AGG["Агрегация (оркестратор):
по достижении всех *_complete
или по таймауту checking_window_seconds"]
+
+ ADMIN["Оператор:
GET /api/v1/admin/status
GET /api/v1/admin/ips
GET /api/v1/admin/ips/{ip}"]
+
+ VA -->|"config_received,
self_check_result, ..."| EP1 --> EVENTS
+ VA -->|"success, detected_egress_ip"| EP2 --> EVENTS
+ EP2 -.->|"успех → egress_complete"| IPQ
+ VA -->|"check_type, target,
success, latency_ms"| EP3 --> CHECKS
+ P1 & P2 & P3 -->|"check_type, success,
latency_ms, complete"| EP4 --> CHECKS
+ EP4 -.->|"complete=true → siteN_complete"| IPQ
+
+ CHECKS --> AGG
+ IPQ --> AGG
+ AGG -->|"overall_result:
pass / partial / fail"| IPQ
+
+ EVENTS --> ADMIN
+ CHECKS --> ADMIN
+ IPQ --> ADMIN
+```
+
+**Пояснение.** Телеметрия стекается в БД из четырёх независимых потоков
+(один от агента-валидатора, три — по одному с каждой площадки),
+записываясь в две таблицы: `events` — сырой журнал аудита (что и когда
+произошло), `checks` — результат каждой отдельной проверки с указанием
+источника (`source`: `egress` для валидатора, `inbound-site-1/2/3` для
+площадок). Отдельно, по мере поступления данных, выставляются флаги
+завершения (`egress_complete`, `site1/2/3_complete`) в `ip_queue`. Как
+только все четыре флага выставлены — либо истекло время ожидания
+(`checking_window_seconds`) — фоновая агрегация суммирует все строки
+`checks` по текущей попытке и записывает итог (`pass`/`partial`/`fail`)
+обратно в `ip_queue`. Оператор в любой момент читает уже накопленные
+данные через административные `GET`-методы, не дожидаясь завершения
+проверки — подробнее о значениях полей см.
+[USAGE.md](USAGE.md#значения-полей-ip).
diff --git a/docs/SETUP.md b/docs/SETUP.md
index 7b81a96..3288641 100644
--- a/docs/SETUP.md
+++ b/docs/SETUP.md
@@ -1,16 +1,21 @@
# Подготовка стенда и первичная инициализация
-Документ описывает, как собрать компоненты, подготовить конфигурацию и
-запустить стенд с нуля — от чистой машины до работающего control-api,
-валидаторов и проберов. Если нужно просто быстро посмотреть систему в
-работе без реального OpenStack — сразу переходите к разделу
+Документ описывает, как подготовить конфигурацию и запустить стенд с нуля
+— от чистой машины до работающего control-api, валидаторов и проберов.
+Бинарники брать не обязательно из исходников: в репозитории уже лежат
+готовые сборки для Linux x86_64 (`bin/`) — это самый быстрый путь к
+развёртыванию, см. [«Получение бинарников»](#получение-бинарников). Если
+нужно просто быстро посмотреть систему в работе без реального OpenStack —
+сразу переходите к разделу
[«Быстрая проверка без OpenStack»](#быстрая-проверка-без-openstack-offline-режим).
## Содержание
- [Компоненты и роли машин](#компоненты-и-роли-машин)
- [Требования](#требования)
-- [Сборка бинарников](#сборка-бинарников)
+- [Получение бинарников](#получение-бинарников)
+ - [Вариант A: готовые бинарники из репозитория (рекомендуется)](#вариант-a-готовые-бинарники-из-репозитория-рекомендуется)
+ - [Вариант B: сборка из исходников](#вариант-b-сборка-из-исходников)
- [Быстрая проверка без OpenStack (offline-режим)](#быстрая-проверка-без-openstack-offline-режим)
- [Подготовка конфигурации для реального стенда](#подготовка-конфигурации-для-реального-стенда)
- [Развёртывание control-api](#развёртывание-control-api)
@@ -33,9 +38,12 @@ control-api (см. [API.md](API.md)).
## Требования
-- **Go 1.22+** для сборки (проверено на Go 1.26). Собранные бинарники —
- статические, дополнительных зависимостей на целевых машинах не требуют
- (используется чистый Go-драйвер SQLite, без cgo).
+- Целевые серверы (control-api, валидаторы, площадки) — **Linux
+ x86_64 (amd64)**. Для этой платформы в репозитории уже лежат готовые
+ бинарники (см. ниже) — устанавливать Go на целевые машины не требуется.
+- **Go 1.22+** нужен только если вы пересобираете бинарники из
+ исходников (проверено на Go 1.26) — например, для другой платформы
+ (arm64, другая ОС) или после изменения кода.
- Для `control-api` в боевом режиме (`openstack.mode: real`) — учётная
запись OpenStack с правами на чтение/изменение floating IP (Neutron)
в сервисном проекте, и заранее выделенные (allocated) floating IP —
@@ -46,15 +54,65 @@ control-api (см. [API.md](API.md)).
- `curl`, `sqlite3` (опционально, для ручной инспекции БД) на машине с
control-api пригодятся для диагностики.
-## Сборка бинарников
+## Получение бинарников
+
+### Вариант A: готовые бинарники из репозитория (рекомендуется)
+
+В директории `bin/` репозитория уже лежат три готовых бинарника —
+собирать их на целевых серверах не нужно, разворачивание сразу
+начинается с копирования и запуска (раздел
+[«Развёртывание control-api»](#развёртывание-control-api) и далее).
+
+```
+bin/
+├── control-api # ~11 МБ
+├── validator-agent # ~7 МБ
+├── prober # ~7 МБ
+└── SHA256SUMS
+```
+
+Характеристики сборки:
+- Платформа: `GOOS=linux GOARCH=amd64`.
+- `CGO_ENABLED=0` — статическая линковка, без cgo (используется чистый
+ Go-драйвер SQLite `modernc.org/sqlite`). Дополнительные `.so`-библиотеки
+ и конкретная версия glibc на целевом сервере **не требуются**.
+- Собрано флагами `-trimpath -ldflags="-s -w"` (без путей сборки и
+ отладочной информации — компактнее и без утечки информации о машине
+ сборки).
+
+Убедиться в отсутствии динамических зависимостей и целостности файлов
+после переноса на целевой сервер:
+
+```bash
+ldd bin/control-api # => "not a dynamic executable"
+file bin/control-api # => ELF 64-bit LSB executable, x86-64, statically linked
+
+# После scp/rsync на целевой сервер — проверить, что файлы не повреждены:
+sha256sum -c bin/SHA256SUMS
+```
+
+Перенос на целевые серверы, например:
+
+```bash
+scp bin/control-api control-api-host:/tmp/
+scp bin/validator-agent validator-host-01:/tmp/
+scp bin/prober probe-site-1:/tmp/
+```
+
+> Если целевая платформа отличается от linux/amd64 (например, ВМ на
+> arm64) — готовые бинарники не подойдут, используйте
+> [вариант B](#вариант-b-сборка-из-исходников).
+
+### Вариант B: сборка из исходников
Из корня репозитория:
```bash
export PATH=$PATH:/usr/local/go/bin # если go не в PATH
-go build -o bin/control-api ./cmd/control-api
-go build -o bin/validator-agent ./cmd/validator-agent
-go build -o bin/prober ./cmd/prober
+export CGO_ENABLED=0 GOOS=linux GOARCH=amd64 # поменяйте GOARCH для другой платформы
+go build -trimpath -ldflags="-s -w" -o bin/control-api ./cmd/control-api
+go build -trimpath -ldflags="-s -w" -o bin/validator-agent ./cmd/validator-agent
+go build -trimpath -ldflags="-s -w" -o bin/prober ./cmd/prober
```
Каждый бинарник самодостаточен — скопируйте нужный файл на
@@ -67,6 +125,14 @@ go build -o bin/prober ./cmd/prober
go build ./... && go test ./...
```
+Пересобирайте из исходников и обновляйте `bin/` с зафиксированными
+`SHA256SUMS`, если меняли код — готовые бинарники в репозитории **не
+обновляются автоматически**:
+
+```bash
+sha256sum bin/control-api bin/validator-agent bin/prober | sed 's#bin/##' > bin/SHA256SUMS
+```
+
## Быстрая проверка без OpenStack (offline-режим)
Прежде чем разворачивать реальный стенд, рекомендуется убедиться, что всё