Files
EvoBGP/.cursor/plans/evobgp_архитектура_0e73ef02.plan.md
T
Denozordec acc0608432
CI / changes (push) Successful in 4s
CI / openapi (push) Successful in 24s
CI / go (push) Successful in 22s
CI / bird2 (push) Successful in 13s
CI / docker-images (deploy/docker/bird2/Dockerfile, evobgp-bird2) (push) Successful in 38s
CI / docker-images (deploy/docker/evobgp-agent/Dockerfile, evobgp-agent) (push) Successful in 1m8s
CI / docker-images (deploy/docker/evobgp-web/Dockerfile, evobgp-web) (push) Successful in 49s
CI / docker-images (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, evobgp-all) (push) Successful in 1m30s
CI / docker-images (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, evobgp-api) (push) Successful in 1m31s
CI / docker-images (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, evobgp-ingest) (push) Has been cancelled
CI / docker-images (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, evobgp-deploy) (push) Has been cancelled
CI / docker-images (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, evobgp-node) (push) Has been cancelled
CI / docker-images (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, evobgp-render) (push) Has been cancelled
CI / docker-images (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, evobgp-scheduler) (push) Has been cancelled
docs: update architecture and API documentation to clarify job processing, in-memory registry usage, and future broker integration plans. Enhance OpenAPI specifications for enrollment responses and refine quickstart instructions for Docker setups.
2026-04-05 18:36:32 +07:00

987 lines
75 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: EvoBGP архитектура
overview: Оба профиля по умолчанию на PostgreSQL (один движок, одни миграции). reference — микросервисы + PG + брокер; microVPS — evobgp-all + контейнер PG (жёсткий тюнинг). SQLite — опция только для одного контейнера без PG. REST API проработан в §7 плана (пути /v1, jobs, бандлы нод); OpenAPI — канон при появлении схемы.
todos:
- id: schema-db
content: "Схема БД: PostgreSQL (основной); опционально SQLite (microVPS single-container); модули, ревизии, jobs"
status: completed
- id: bird-generator
content: Формат bird.conf, include-фрагменты, фильтры, birdc configure
status: completed
- id: rest-jobs
content: REST (refresh, apply, preview, rollback, bundle API) и async jobs
status: completed
- id: node-agent
content: evobgp-agent + bird2 в Docker на мастере; evobgp-node + тот же паттерн на реплике (отдельная ВМ)
status: completed
- id: observability
content: Метрики, алерты префиксов и BGP-сессий
status: completed
- id: docker-ms
content: Compose profiles reference + microVPS; сервисы bird2 + evobgp-agent; сеть BGP; лимиты; логи; prune
status: completed
- id: go-modules
content: Monorepo по §1 (дерево репозитория); internal/*, cmd/evobgp-all и cmd/* для reference
status: completed
- id: replica-bundle
content: Подписанный бандл ревизии, API, evobgp-node
status: completed
- id: risk-hardening
content: Двухфазный deploy, LKG, pin BIRD, политика миграций, обязательная подпись бандла на ноде
status: completed
- id: test-bird-matrix
content: Матрица сценариев BIRD + golden-тесты birdfmt + bird -p в CI
status: completed
- id: ci-gitea
content: .gitea/workflows/ci.yaml, документация для act runner
status: completed
- id: web-ui
content: "Web UI (Svelte + shadcn-svelte) в отдельном контейнере: списки, расписание, мониторинг"
status: completed
isProject: false
---
# EvoBGP — архитектурный план
Control-plane на **Go**, анонс префиксов через **BIRD**, политика и история в **SQL-БД**, управление по **REST**. Репозиторий кода пока пустой — документ задаёт целевую архитектуру.
---
## Содержание
1. [Структура репозитория (файлы и пакеты)](#1-структура-репозитория-файлы-и-пакеты)
2. [Два эталонных профиля](#2-два-эталонных-профиля-развёртывания)
3. [Логическая модель и термины](#3-логическая-модель-общая-для-обоих-профилей)
4. [Сервисы и контейнеры](#4-сервисы-сравнение-профилей)
- [4.1. Топология Docker Compose](#41-топология-docker-compose)
5. [База данных, ETL, ER-схема](#5-база-данных-etl-er-схема)
6. [Модули префиксов и FQDN](#6-модули-префиксов-as-cdn-домены)
7. [REST API](#7-rest-api)
- [7.1. Общие соглашения](#71-общие-соглашения)
- [7.2. Системные и служебные](#72-системные-и-служебные)
- [7.3. Модули префиксов](#73-модули-префиксов-module)
- [7.4. DoH-профили](#74-doh-профили-doh_profile)
- [7.5. BGP community](#75-bgp-community-bgp_community)
- [7.6. Пиры](#76-пиры-bgp_peer)
- [7.7. Спикеры BIRD](#77-спикеры-bird-bgp_speaker)
- [7.8. Ревизии конфигурации](#78-ревизии-конфигурации-config_revision)
- [7.9. Применение конфигурации](#79-применение-конфигурации-deploy-bird)
- [7.10. Задачи](#710-задачи-job_audit)
- [7.11. Реплики и бандлы](#711-реплики-evobgp-node-бандлы)
- [7.12. Глобальные настройки](#712-глобальные-настройки-опционально)
- [7.13. Матрица прав](#713-матрица-прав-роли)
- [7.14. Следующие итерации](#714-следующие-итерации-api)
- [7.15. Связь API с разделами плана](#715-связь-api-с-разделами-плана)
8. [Генерация BIRD и ревизии](#8-генерация-bird-и-ревизии)
9. [Эксплуатация и масштаб пиров](#9-эксплуатация-и-масштаб)
10. [Реплика evobgp-node](#10-реплика-evobgp-node)
11. [Выбор СУБД](#11-выбор-субд)
12. [Профиль microVPS — детализация](#12-профиль-microvps-детализация)
13. [Снижение рисков (меры и процессы)](#13-снижение-рисков-меры-и-процессы)
14. [Тестирование BIRD2 и матрица сценариев](#14-тестирование-bird2-и-матрица-сценариев)
15. [CI/CD (Gitea Actions)](#15-cicd-gitea-actions)
16. [Риски и этапы внедрения](#16-риски-и-этапы-внедрения)
17. [Web UI (Svelte, shadcn-svelte)](#17-web-ui-svelte-shadcn-svelte)
---
## 1. Структура репозитория (файлы и пакеты)
Monorepo на **Go**: общая логика в `internal/*`, отдельные бинарники в `cmd/*`. Цель — одна кодовая база для профилей **reference** (несколько процессов) и **microVPS** (`evobgp-all`).
**Принципы:**
- `**cmd/`** — только точки входа: флаги, переменные окружения, сборка зависимостей (DI), запуск; без доменной логики.
- `**internal/`** — весь прикладной код; внешние модули Go не могут импортировать эти пакеты (правило компилятора).
- **Слои:** `domain` (модели и инварианты без I/O) → `repository` и адаптеры к БД/внешним API → пакеты воркеров (оркестрация) → транспорт (`httpapi`, `jobs`, клиент к агенту).
- **Один код — две упаковки:** микросервисы и `evobgp-all` используют одни и те же пакеты `internal/*`; отличается только набор процессов в `cmd/*`.
- `**docs/`**, `**scripts/`** — как сейчас в репозитории (контракт API: [docs/openapi.yaml](docs/openapi.yaml), вспомогательные скрипты).
- `**deploy/**` — Dockerfiles, `docker-compose` с профилями `reference` / `microVPS`, при необходимости entrypoint’ы для **bird2** / **evobgp-agent**; инфраструктура не смешивается с `internal/`.
**Целевое дерево каталогов** (ориентир; имена подпакетов можно уточнить при первой итерации кода, **роли** каталогов зафиксированы):
```
EvoBGP/
├── cmd/
│ ├── evobgp-api/ # REST + постановка jobs (reference)
│ ├── evobgp-scheduler/
│ ├── evobgp-ingest/
│ ├── evobgp-render/
│ ├── evobgp-deploy/
│ ├── evobgp-all/ # microVPS: те же пакеты, один процесс / несколько goroutine
│ ├── evobgp-agent/ # рядом с bird2: запись конфигов, birdc
│ └── evobgp-node/ # pull бандла, проверка подписи, apply
├── internal/
│ ├── config/ # загрузка конфигурации (ENV, файлы)
│ ├── platform/ # логирование, метрики, трассировка, health
│ ├── db/ # пул, транзакции; embed миграций или вызов migrate
│ ├── repository/ # SQL по сущностям (tenant, module, peer, revision, jobs, …)
│ ├── domain/ # типы и правила без I/O (префиксы, community, ревизии)
│ ├── httpapi/ # роуты OpenAPI, middleware, валидация, маппинг в сервисы
│ ├── jobs/ # задачи: сейчас in-memory Registry в процессе API; PG job_audit / брокер — целевое расширение (см. примечание ниже §2)
│ ├── scheduler/ # триггеры по расписанию модулей
│ ├── ingest/ # CDN, DoH, нормализация, запись в БД
│ ├── render/ # материализация префиксов, ревизия, текст артефактов BIRD
│ ├── birdfmt/ # шаблоны и сборка include-фрагментов (альтернатива имени: bird/)
│ ├── deploy/ # доставка на volume, взаимодействие с evobgp-agent / бандлы
│ ├── bundle/ # упаковка и подпись бандла для evobgp-node
│ └── signing/ # ключи, проверка подписи на ноде
├── migrations/ # SQL миграции PostgreSQL (единый набор для обоих профилей)
├── deploy/
│ ├── compose/ # docker-compose с профилями
│ └── docker/ # Dockerfile на бинарь + общие слои
├── docs/
├── scripts/
├── go.mod
├── go.sum
└── README.md
```
**Соответствие сервисам плана:**
| Сервис (план) | Код |
| -------------------- | ------------------------------------------------------------------------------------------------ |
| **evobgp-api** | `cmd/evobgp-api`, `internal/httpapi`, `internal/jobs`, `internal/repository` |
| **evobgp-scheduler** | `cmd/evobgp-scheduler`, `internal/scheduler`, `internal/jobs`, `internal/repository` |
| **evobgp-ingest** | `cmd/evobgp-ingest`, `internal/ingest`, `internal/repository` |
| **evobgp-render** | `cmd/evobgp-render`, `internal/render`, `internal/birdfmt`, `internal/repository` |
| **evobgp-deploy** | `cmd/evobgp-deploy`, `internal/deploy`, `internal/bundle`, `internal/repository` |
| **evobgp-all** | `cmd/evobgp-all` — поднимает HTTP и воркеры, импортируя те же `internal/`* |
| **evobgp-agent** | `cmd/evobgp-agent`, `internal/deploy` (запись на volume, `birdc`) |
| **evobgp-node** | `cmd/evobgp-node`, `internal/bundle`, `internal/signing`, `internal/birdfmt` / `internal/deploy` |
**Поток пакетов (упрощённо):**
```mermaid
flowchart TB
subgraph entry [cmd]
API[evobgp-api]
W[scheduler ingest render deploy]
end
subgraph internal [internal]
HTTP[httpapi]
J[jobs]
R[repository]
DBpkg[db]
REN[render]
BF[birdfmt]
DEP[deploy]
end
PG[(PostgreSQL)]
AG[evobgp-agent]
API --> HTTP
HTTP --> J
HTTP --> R
W --> J
W --> R
R --> DBpkg
DBpkg --> PG
REN --> BF
REN --> R
DEP --> R
DEP --> AG
```
---
## 2. Два эталонных профиля развёртывания
Один и тот же **код** в дереве `internal/` (все подпакеты), две **упаковки** в Docker Compose.
| Критерий | **reference** (эталон) | **microVPS** |
| ---------------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **Назначение** | Production control-plane без жёсткого лимита RAM; горизонтальное масштабирование воркеров | Один хост: маленькая VPS / вложенная ВМ |
| **CPU** | 2+ vCPU (рекомендуется) | **1 vCPU** |
| **RAM** | 4+ ГиБ (ориентир) | **~1 ГиБ** |
| **Диск под Docker + данные** | по объёму проекта | **7–10 ГиБ** (бюджет; не весь диск ОС) |
| **ОС** | Linux (в т.ч. Ubuntu 22.04/24.04) | **Ubuntu 24.04 LTS** (референс) |
| **Контейнеры Go** | 5 образов: `api`, `scheduler`, `ingest`, `render`, `deploy` | **1** образ: `evobgp-all` |
| **БД** | **PostgreSQL** (отдельный сервис или managed) | **PostgreSQL** в контейнере (**жёсткий** тюнинг под 1 ГиБ) |
| **Очередь** | **NATS JetStream** или **Redis Streams** (на выбор) | **Нет брокера** — `job_audit` в **PostgreSQL** + `SKIP LOCKED` / малый пул коннектов |
| **Reverse proxy** | Traefik / Nginx (опционально) | **Нет** — прямой порт API |
| **Object storage** | MinIO / S3 опционально для артефактов | Только **локальный volume** или небольшие BLOB в БД |
| **BIRD** | **BIRD 2** в **Docker** (privileged / нужные capabilities) + контейнер `**evobgp-agent`** с общим volume | То же: **bird2** + **evobgp-agent** + **postgres** + **evobgp-all** |
| **Контейнеры (итого)** | 5×Go + **postgres** + брокер + **bird2** + **evobgp-agent** (+ опц. proxy) | **4:** **evobgp-all** + **postgres** + **bird2** + **evobgp-agent** (+ опц. нода вне VPS) |
```mermaid
flowchart LR
subgraph ref [reference]
R1[5x Go]
R2[(PostgreSQL)]
R3[(Broker)]
R4[bird2]
R5[evobgp-agent]
R1 --> R2
R1 --> R3
R1 --> R5
R5 --> R4
end
subgraph micro [microVPS]
M1[evobgp-all]
M2[(PostgreSQL)]
M3[bird2]
M4[evobgp-agent]
M1 --> M2
M1 --> M4
M4 --> M3
end
```
> **Правило:** логика домена **одинакова**; **один движок БД — PostgreSQL** (одинаковые миграции и SQL). Отличаются число процессов Go, наличие брокера и **настройки** PG.
> **Примечание (текущий код vs целевая очередь):** исполнение async jobs идёт через **in-memory `jobs.Registry` в процессе `evobgp-api` / `evobgp-all`**; таблица `job_audit` в миграциях заложена под будущую персистенцию и идемпотентность между процессами. В **reference** Compose отдельный `evobgp-scheduler` **не** читает эту очередь, а вызывает `POST .../modules/{id}/refresh` по HTTP. NATS в compose — для будущей интеграции; см. [docs/architecture.md](../../docs/architecture.md).
> **Опция `microVPS_sqlite`:** один контейнер `evobgp-all` без PG — только если критичен абсолютный минимум контейнеров; иначе **не рекомендуется** как основной путь.
---
## 3. Логическая модель (общая для обоих профилей)
- **BGP-сервер (ваш)** — процесс **BIRD**, который **анонсирует** префиксы и держит сессии.
- **Клиенты** — **внешние** роутеры (BGP-пиры), подключающиеся **к вам** и **получающие** маршруты. Это не контейнеры EvoBGP.
- **Мастер** — control-plane + **BIRD 2** в **контейнере** и контейнер `**evobgp-agent`** (общий volume: сгенерированные `bird.d`, сокет/канал для `birdc`).
- **EvoBGP-нода (опционально)** — отдельная площадка: **только** pull подписанного **бандла** с мастера + **BIRD 2 в Docker** (тот же паттерн agent + bird2); **своей** полной БД и ingest **нет**.
**Быстрый путь данных:** БД → ingest (CDN / DoH / AS; **IP_RANGES** только читаются при render из БД) → render (префиксы + community + ревизия) → deploy → файлы BIRD → `birdc configure` → клиентские сессии.
---
## 4. Сервисы (сравнение профилей)
| Сервис | Назначение | reference | microVPS |
| -------------------- | --------------------------------------------------------------- | -------------------------------- | ------------------------ |
| **evobgp-api** | REST, CRUD, задачи, `/jobs` | отдельный контейнер | goroutine в `evobgp-all` |
| **evobgp-scheduler** | Интервалы модулей/CDN → события | отдельный контейнер | goroutine в `evobgp-all` |
| **evobgp-ingest** | CDN, DoH, материализация в БД | отдельный контейнер | goroutine в `evobgp-all` |
| **evobgp-render** | Итоговые префиксы, ревизия, текст BIRD | отдельный контейнер | goroutine в `evobgp-all` |
| **evobgp-deploy** | Доставка на мастерский BIRD, публикация бандла | отдельный контейнер | goroutine в `evobgp-all` |
| **evobgp-agent** | Контейнер рядом с **bird2**: запись конфигов, `birdc configure` | то же (общий volume с **bird2**) | то же |
| **evobgp-node** | Реплика: pull бандла, локальный BIRD | отдельный хост | **не на microVPS** |
**Инфраструктура reference:** PostgreSQL, NATS или Redis, опционально Traefik, MinIO, **bird2** + **evobgp-agent** в Docker.
### Зависимости (профиль reference)
```mermaid
flowchart TB
subgraph cp [Control plane]
API[evobgp-api]
SCH[scheduler]
ING[ingest]
REN[render]
DEP[deploy]
end
DB[(PostgreSQL)]
MQ[(Broker)]
API --> DB
API --> MQ
SCH --> DB
SCH --> MQ
ING --> DB
ING --> MQ
REN --> DB
REN --> MQ
DEP --> DB
DEP --> MQ
DEP --> AG[evobgp-agent]
AG --> BIRD2[bird2]
```
### 4.1. Топология Docker Compose
Целевая упаковка: **все компоненты мастера в Compose**, включая **BIRD 2** (`bird2`). Пара **bird2 + evobgp-agent** делает **общий именованный volume** (или bind-mount) для каталога конфигурации и точки управления `birdc` (см. образ/entrypoint в репозитории).
**Сеть для BGP:** на практике для входящих TCP **179** часто нужны `network_mode: host` (Linux), **macvlan/ipvlan** или публикация портов / отдельный L3-интерфейс — выбор фиксируется в профиле Compose и документации оператора; контейнер **bird2** получает **privileged** и набор **capabilities** (`NET_ADMIN`, и при необходимости `NET_RAW`), плюс `sysctls` под forwarding, если не на host-сети.
```mermaid
flowchart TB
subgraph refDocker [profile_reference]
subgraph goRef [Сервисы Go]
API1[evobgp-api]
SCH1[scheduler]
ING1[ingest]
REN1[render]
DEP1[deploy]
end
PG1[(postgres)]
BR1[broker]
subgraph bgpRef [Стек BGP Docker]
AG1[evobgp-agent]
B21[bird2]
end
VOL1[vol_bird_config]
API1 --> PG1
API1 --> BR1
SCH1 --> PG1
SCH1 --> BR1
ING1 --> PG1
ING1 --> BR1
REN1 --> PG1
REN1 --> BR1
DEP1 --> PG1
DEP1 --> BR1
DEP1 --> AG1
AG1 --> VOL1
B21 --> VOL1
AG1 -->|birdc| B21
end
subgraph microDocker [profile_microVPS]
ALL[evobgp-all]
PG2[(postgres)]
subgraph bgpMicro [Стек BGP Docker]
AG2[evobgp-agent]
B22[bird2]
end
VOL2[vol_bird_config]
ALL --> PG2
ALL --> AG2
AG2 --> VOL2
B22 --> VOL2
AG2 -->|birdc| B22
end
```
На **реплике** (`evobgp-node`) — тот же паттерн **bird2 + evobgp-agent** в Docker на отдельном хосте; pull бандла и `birdc configure` без полной БД (см. §10).
---
## 5. База данных, ETL, ER-схема
### 5.1. Группы сущностей
| Область | Назначение |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **module** | Тип `AS_PREFIXES` / `CDN_CIDRS` / `DOMAINS` / `IP_RANGES`, расписание, DoH, приоритет |
| **module_cdn_source** | URL + `source_kind` (формат тела списка); примеры наброска: `text_cidr_lines`, `json_prefix_list`, `custom`; ETag; свой `refresh_interval` опционально |
| **doh_profile** | URL DoH, таймауты, секреты по ссылке |
| **module_domain_entry** / **module_as_entry** / **module_ip_range_entry** | FQDN; или ASN/префикс; или **только CIDR/диапазон** + `community_id` (`IP_RANGES`) |
| **bgp_community** | Справочник community для BIRD |
| **bgp_peer** | Клиентский пир; `speaker_id` NULL = все спикеры в бандле |
| **bgp_speaker** | master / replica, `last_applied_revision_id` |
| **config_revision** / **revision_materialized_prefix** | История, diff, откат |
| **job_audit** | Асинхронные задачи; в reference дополняет брокер |
### 5.2. ETL
```mermaid
flowchart LR
subgraph ex [Extract]
CDN[CDN fetch]
DOH[DoH]
AS[AS из БД]
IPR[IP ranges из БД]
end
subgraph tr [Transform]
NORM[Нормализация CIDR]
DEDUP[Дедуп]
COMM[Community]
end
subgraph ld [Load]
DB[(SQL БД)]
REV[revision]
ART[артефакты BIRD]
end
CDN --> NORM
DOH --> NORM
AS --> NORM
IPR --> NORM
NORM --> DEDUP --> COMM
COMM --> DB
COMM --> REV
REV --> ART
```
### 5.3. ER (упрощённо)
```mermaid
erDiagram
tenant ||--o{ module : owns
tenant ||--o{ bgp_community : owns
tenant ||--o{ bgp_peer : owns
tenant ||--o{ bgp_speaker : owns
doh_profile ||--o{ module : uses
module ||--o{ module_cdn_source : contains
module ||--o{ module_domain_entry : contains
module ||--o{ module_as_entry : contains
module ||--o{ module_ip_range_entry : contains
bgp_community ||--o{ module_domain_entry : tags
bgp_community ||--o{ module_as_entry : tags
bgp_community ||--o{ module_ip_range_entry : tags
bgp_community ||--o{ module_cdn_source : tags
bgp_speaker ||--o{ bgp_peer : scope
module ||--o{ config_revision : produces
config_revision ||--o{ revision_materialized_prefix : snapshot
module ||--o{ job_audit : tasks
```
### 5.4. Пояснения к таблицам
| Таблица | Назначение | Ключевые поля | Кто использует |
| -------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| **tenant** | Multi-tenant | `id`, `name`, `slug` | API, все сервисы |
| **module** | Блок политики | `type`, `enabled`, `priority`, `doh_profile_id`, `refresh_interval_sec`, `cron_expr`, `default_community_id` | API, scheduler, ingest, render |
| **doh_profile** | DoH | `url`, таймауты, ссылка на секрет | DOMAINS, ingest |
| **module_cdn_source** | Строка CDN | `source_kind` (набросок: `text_cidr_lines`, `json_prefix_list`, `custom`), `url`, `etag`, `refresh_interval_sec`, `community_id` | ingest, render |
| **module_domain_entry** | FQDN | `fqdn`, `community_id`, метаданные резолва | ingest, render |
| **module_as_entry** | AS/префикс | `asn`, `prefix`, `community_id` | API, render |
| **module_ip_range_entry** | Статический CIDR | `prefix` (CIDR), `community_id`; без ASN и без внешнего URL — модуль типа `IP_RANGES` | API, render |
| **bgp_community** | Справочник | `kind`, значения, уникальность в tenant | API, render |
| **bgp_peer** | Клиентский пир | neighbor, ASN, политики, `bgp_speaker_id` (NULL = все спикеры) | API, render, бандл |
| **bgp_speaker** | Экземпляр BIRD | `role` master/replica, endpoint, `last_applied_revision_id` | deploy, нода |
| **config_revision** | История | `hash`, артефакт, `parent_revision_id` | render, deploy, rollback |
| **revision_materialized_prefix** | Снимок префиксов | `revision_id`, `prefix`, `community_id`, `source` | preview, diff, откат |
| **job_audit** | Задачи | `kind`, `status`, `idempotency_key` | API, воркеры |
Дополнительно: `**global_settings`** (KV); `**module_cdn_fetch_log`** (опционально, TTL).
В **reference** очередь: брокер + `job_audit`; в **microVPS** — только БД и идемпотентность в `job_audit`.
---
## 6. Модули префиксов (AS / CDN / домены)
### Продуктовая трактовка
В терминах продукта **модуль** — это один из **четырёх видов источника префиксов**: **AS** (`AS_PREFIXES`), **CDN** (`CDN_CIDRS`), **DOMAINS** (`DOMAINS`), **IP-диапазоны** (`IP_RANGES`). Наполнение задаётся либо **записями в таблице** (ASN или префикс + `community_id`; FQDN + `community_id` для доменов; **CIDR + `community_id`** для `IP_RANGES` без ASN и без внешнего URL), либо для CDN — **источником по URL** с полем `source_kind`, определяющим формат скачанного списка и парсер. Строка сущности `**module`** в БД — это экземпляр модуля выбранного типа (расписание, приоритет, DoH для доменов и т.д.); `**module_id` в API** — идентификатор экземпляра, а не «имя типа». При минимальном развёртывании (один bird2, мало пиров) допустим **один экземпляр на каждый нужный тип** или узкий набор экземпляров — это не противоречит модели.
| Тип | Ввод | Поведение |
| ---------------- | ---------------------------- | ------------------------------------------------------------------------- |
| **AS** | ASN / префиксы | Таблица + опционально внешний `PrefixProvider` |
| **CDN** | URL или список | Fetch, ETag, интервал модуля или строки |
| **Домены** | FQDN | В BIRD попадают **только** IP-префиксы после DoH-резолва |
| **IP-диапазоны** | CIDR (IPv4/IPv6) + community | Только таблица `module_ip_range_entry`; без IRR/ASN-семантики и без fetch |
**FQDN:** воркер резолвит через **DoH-профиль** модуля → `/32` / `/128` (или политика) → материализация в БД → генерация static include для BIRD.
---
## 7. REST API
**Статус:** черновик для согласования и реализации; каноничная машиночитаемая форма — **[docs/openapi.yaml](docs/openapi.yaml)** (по мере заполнения). Ниже — **единая проработка** путей и семантики (консолидация с [docs/evobgp-api-sketches.md](docs/evobgp-api-sketches.md)). Базовый префикс: `**/v1`**. Термины: [§3](#3-логическая-модель-общая-для-обоих-профилей), [§5](#5-база-данных-etl-er-схема), [§6](#6-модули-префиксов-as-cdn-домены).
### 7.1. Общие соглашения
| Тема | Решение (набросок) |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Аутентификация** | Заголовок `Authorization: Bearer <api_key>` или mTLS на edge; ключи привязаны к tenant и роли. |
| **Multi-tenant** | Все сущности в скоупе tenant: либо из ключа, либо явный префикс `X-Tenant-Id` (только для супер-ролей). |
| **Идентификаторы** | UUID v7 или ULID в URL; в JSON — строки. |
| **Время** | ISO 8601 UTC (`2026-04-03T12:00:00Z`). |
| **Ошибки** | Тело `application/problem+json` (RFC 9457): `type`, `title`, `status`, `detail`, `instance`, опционально `errors[]` по полям. |
| **Идемпотентность** | Для мутаций, создающих задачи или побочные эффекты: заголовок `Idempotency-Key` (опционально обязателен для `POST` apply/refresh). |
| **Пагинация** | `?cursor=<opaque>&limit=50` (cursor-based); ответ: `items`, `next_cursor`, `has_more`. |
| **Асинхронные операции** | `202 Accepted`, заголовок `Location: /v1/jobs/{job_id}`; тело `{ "job_id", "status": "queued" }`. |
| **Версионирование** | Несовместимые изменения — новый префикс `/v2`. |
### 7.2. Системные и служебные
| Метод | Путь | Назначение |
| ----- | ------------- | ------------------------------------------------------------ |
| `GET` | `/v1/health` | Liveness (процесс жив). |
| `GET` | `/v1/ready` | Readiness (БД, брокер при reference, и т.д.). |
| `GET` | `/v1/version` | Версия сборки API и control-plane (`git_sha`, `build_time`). |
### 7.3. Модули префиксов (`module`)
**Связь с продуктом.** В продуктовой формулировке **ASN / CDN / DOMAINS / IP-диапазоны** — это **четыре вида модулей**. Поле `**type`** в `POST /v1/modules` выбирает вид: `AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS` или `**IP_RANGES`**. Дочерние ресурсы: `**as-entries`**, `**cdn-sources**`, `**domain-entries**`; `**ip-range-entries**` — статические **CIDR + `community_id`** (без ASN и без URL). `**module_id**` в пути — идентификатор **конкретного экземпляра** модуля, а не имя типа (согласовано с [§6](#6-модули-префиксов-as-cdn-домены)).
| Метод | Путь | Описание |
| -------- | ------------------------- | ----------------------------------------------------------------- |
| `GET` | `/v1/modules` | Список модулей tenant (фильтры: `?type=`, `?enabled=`). |
| `POST` | `/v1/modules` | Создать модуль. |
| `GET` | `/v1/modules/{module_id}` | Детали модуля. |
| `PATCH` | `/v1/modules/{module_id}` | Частичное обновление (расписание, DoH, приоритет, `enabled`). |
| `DELETE` | `/v1/modules/{module_id}` | Мягкое удаление или `enabled=false` — зафиксировать в реализации. |
**CDN-источники модуля**
| Метод | Путь | Описание |
| -------- | ------------------------------------------------- | --------------------------------------------------------------------- |
| `GET` | `/v1/modules/{module_id}/cdn-sources` | Список строк CDN. |
| `POST` | `/v1/modules/{module_id}/cdn-sources` | Добавить источник (URL + `source_kind` + опционально `community_id`). |
| `PATCH` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | URL, `source_kind`, `community_id`, свой `refresh_interval_sec`. |
| `DELETE` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | Удалить. |
**Записи AS / домены**
| Метод | Путь | Описание |
| -------- | --------------------------------------------------- | --------------------- |
| `GET` | `/v1/modules/{module_id}/as-entries` | Список ASN/префиксов. |
| `POST` | `/v1/modules/{module_id}/as-entries` | Добавить. |
| `PATCH` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Обновить. |
| `DELETE` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Удалить. |
| `GET` | `/v1/modules/{module_id}/domain-entries` | FQDN + community. |
| `POST` | `/v1/modules/{module_id}/domain-entries` | Добавить. |
| `PATCH` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Обновить. |
| `DELETE` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Удалить. |
**Записи IP-диапазонов** (только для `type: IP_RANGES`)
| Метод | Путь | Описание |
| -------- | ----------------------------------------------------- | ------------------------------------ |
| `GET` | `/v1/modules/{module_id}/ip-range-entries` | Список CIDR + `community_id`. |
| `POST` | `/v1/modules/{module_id}/ip-range-entries` | Добавить (`prefix`, `community_id`). |
| `PATCH` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Обновить. |
| `DELETE` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Удалить. |
**Refresh (ingest)**
| Метод | Путь | Описание |
| ------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/v1/modules/{module_id}/refresh` | Запуск ingest для модуля (CDN / DoH / AS по типу). Для `**IP_RANGES`** обычно **не требуется** (данные только в БД); возможен `**204`** / no-op или `**400`**, если тип не поддерживает refresh — зафиксировать в реализации. |
**Пример тела создания модуля (набросок)**
```json
{
"type": "CDN_CIDRS",
"name": "edge-v4",
"enabled": true,
"priority": 10,
"doh_profile_id": null,
"refresh_interval_sec": 3600,
"cron_expr": null,
"default_community_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
Пример модуля `**IP_RANGES**`: `type: "IP_RANGES"`, `doh_profile_id: null`, далее строки через `ip-range-entries` с полями `prefix` (например `203.0.113.0/24`) и `community_id`.
### 7.4. DoH-профили (`doh_profile`)
| Метод | Путь | Описание |
| -------- | ----------------------- | ------------------------------------------------------------------------------------- |
| `GET` | `/v1/doh-profiles` | Список. |
| `POST` | `/v1/doh-profiles` | Создать (URL, таймауты; секрет — ссылка на vault id или отдельный `POST .../secret`). |
| `GET` | `/v1/doh-profiles/{id}` | Детали (без раскрытия секрета). |
| `PATCH` | `/v1/doh-profiles/{id}` | Обновить. |
| `DELETE` | `/v1/doh-profiles/{id}` | Удалить, если не используется модулями. |
### 7.5. BGP community (bgp_community)
| Метод | Путь | Описание |
| -------- | ---------------------- | ------------------------------ |
| `GET` | `/v1/communities` | Список справочника. |
| `POST` | `/v1/communities` | Создать. |
| `GET` | `/v1/communities/{id}` | Детали. |
| `PATCH` | `/v1/communities/{id}` | Обновить. |
| `DELETE` | `/v1/communities/{id}` | Удалить при отсутствии ссылок. |
### 7.6. Пиры (`bgp_peer`)
| Метод | Путь | Описание |
| -------- | ---------------- | ------------------------------------------------------------------------------ |
| `GET` | `/v1/peers` | Список (`?speaker_id=`, пагинация). |
| `POST` | `/v1/peers` | Создать пира. |
| `GET` | `/v1/peers/{id}` | Детали. |
| `PATCH` | `/v1/peers/{id}` | Политики, neighbor, ASN, привязка к `bgp_speaker_id` или `null` = все спикеры. |
| `DELETE` | `/v1/peers/{id}` | Удалить / отключить. |
### 7.7. Спикеры BIRD (`bgp_speaker`)
| Метод | Путь | Описание |
| ------- | ------------------- | ------------------------------------------ |
| `GET` | `/v1/speakers` | Список (master / replica, endpoint). |
| `POST` | `/v1/speakers` | Зарегистрировать спикер (реплика, canary). |
| `GET` | `/v1/speakers/{id}` | Детали + `last_applied_revision_id`. |
| `PATCH` | `/v1/speakers/{id}` | Метаданные, endpoint. |
### 7.8. Ревизии конфигурации (config_revision)
| Метод | Путь | Описание |
| ------ | -------------------------------------- | --------------------------------------------------------------- |
| `GET` | `/v1/revisions` | История ревизий (`?module_id=`, `?limit=`). |
| `GET` | `/v1/revisions/{revision_id}` | Метаданные: хэш, родитель, время, артефакты. |
| `GET` | `/v1/revisions/{revision_id}/prefixes` | Материализованный снимок префиксов (пагинация). |
| `GET` | `/v1/revisions/{revision_id}/preview` | Превью фрагментов BIRD (read-only, без apply). |
| `POST` | `/v1/revisions/{revision_id}/rollback` | Создать **новую** ревизию с содержимым отката; часто `**202`**. |
**Сравнение ревизий (набросок)**
| Метод | Путь | Описание |
| ----- | ---------------------------- | ----------------------------------------------------------------------------- |
| `GET` | `/v1/revisions/{a}/diff/{b}` | Diff префиксов / метаданных (формат зафиксировать: JSON patch или табличный). |
### 7.9. Применение конфигурации (deploy, BIRD)
| Метод | Путь | Описание |
| ------ | ------------------------- | --------------------------------------------------------------------------------------------- |
| `POST` | `/v1/apply` | Применить текущую целевую ревизию на всех спикерах (или по политике по умолчанию). `**202`**. |
| `POST` | `/v1/speakers/{id}/apply` | Применить на одном спикере (canary). `**202`**. |
| `POST` | `/v1/bird/reload` | Опционально: явный мягкий reload политики (если отделён от apply); иначе часть `apply`. |
**Тело `POST /v1/apply` (набросок, опционально)**
```json
{
"revision_id": "01JQXYZ...",
"strategy": "all_speakers",
"dry_run": false
}
```
Связь с безопасным применением: [§13](#13-снижение-рисков-меры-и-процессы) (двухфазный deploy, LKG).
### 7.10. Задачи (job_audit)
| Метод | Путь | Описание |
| ------ | -------------------------- | --------------------------------------------- |
| `GET` | `/v1/jobs` | Список задач (`?status=`, `?kind=`, cursor). |
| `GET` | `/v1/jobs/{job_id}` | Статус, прогресс, ошибка, связанные сущности. |
| `POST` | `/v1/jobs/{job_id}/cancel` | Запрос отмены (best-effort). |
**Пример ответа `GET /v1/jobs/{id}`**
```json
{
"job_id": "01JQXYZ...",
"kind": "module_refresh",
"status": "running",
"idempotency_key": "client-abc-123",
"created_at": "2026-04-03T10:00:00Z",
"started_at": "2026-04-03T10:00:01Z",
"finished_at": null,
"error": null,
"meta": { "module_id": "01JQM..." }
}
```
### 7.11. Реплики evobgp-node: бандлы
Вызываются **нодой** с отдельным ключом / mTLS (роль `node`). См. также [§10](#10-реплика-evobgp-node).
| Метод | Путь | Описание |
| ------ | ------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `GET` | `/v1/speakers/{speaker_id}/revisions/latest` | Указатель на последнюю опубликованную ревизию для ноды. |
| `GET` | `/v1/speakers/{speaker_id}/bundle/{revision_id}` | Скачивание подписанного бандла (архив + `manifest.json` + подпись). |
| `POST` | `/v1/nodes/enroll` | Регистрация ноды (обмен ключами, привязка к `speaker_id`) — детали протокола отдельно. |
Заголовки для бандла: `Content-Type: application/octet-stream` или multipart; целостность по `manifest` (SHA-256) и подписи (например Ed25519) — обязательно на ноде в production ([§13](#13-снижение-рисков-меры-и-процессы)).
### 7.12. Глобальные настройки (опционально)
| Метод | Путь | Описание |
| ------- | -------------- | ------------------------------------------------------- |
| `GET` | `/v1/settings` | KV вроде `global_settings` (лимиты CDN, feature flags). |
| `PATCH` | `/v1/settings` | Частичное обновление (только роль operator). |
### 7.13. Матрица прав (роли)
| Ресурс | `viewer` | `editor` | `operator` | `node` |
| -------------------------- | -------- | -------- | ---------- | ------ |
| GET модули, ревизии, peers | да | да | да | нет* |
| PATCH модули, peers | нет | да | да | нет |
| apply, rollback | нет | нет | да | нет |
| bundle / enroll | нет | нет | нет | да |
Нода не ходит в общий CRUD; только [§7.11](#711-реплики-evobgp-node-бандлы).
### 7.14. Следующие итерации API
- Полная **OpenAPI 3.1** в `docs/openapi.yaml` по этому разделу.
- Webhooks: `POST` на URL клиента по завершении `job` (опционально).
- SSE/WebSocket для стрима статуса долгих jobs.
- Rate limits по ключу и по tenant в ответах (`RateLimit-*` заголовки).
### 7.15. Связь API с разделами плана
| Тема плана | Подраздел §7 |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| REST, jobs, идемпотентность | [7.1](#71-общие-соглашения), [7.10](#710-задачи-job_audit) |
| refresh, apply, rollback, preview, `IP_RANGES` | [7.3](#73-модули-префиксов-module), [7.8](#78-ревизии-конфигурации-config_revision), [7.9](#79-применение-конфигурации-deploy-bird) |
| peers, speakers, communities, DoH | [7.4](#74-doh-профили-doh_profile)–[7.7](#77-спикеры-bird-bgp_speaker) |
| bundle API, нода | [7.11](#711-реплики-evobgp-node-бандлы) |
| Контрактные тесты HTTP | [§14](#14-тестирование-bird2-и-матрица-сценариев) + `openapi.yaml` |
Черновик в репозитории [docs/evobgp-api-sketches.md](docs/evobgp-api-sketches.md) держать **синхронным** с §7 при согласовании изменений (или пометить файл указателем на план как единый источник).
---
## 8. Генерация BIRD и ревизии
- Фрагменты `bird.d/*.conf`, `include`, фильтры, `peers.conf`, community из справочника.
- Ревизия: хэш набора префиксов, артефакты, откат = новая ревизия со старым содержимым.
---
## 9. Эксплуатация и масштаб
- До **~20+** клиентских пиров — строки `bgp_peer`, не отдельные хосты EvoBGP.
- Rate-limit CDN, per-module cooldown.
- Canary: несколько `bgp_speaker` или подмножество пиров по фильтру.
- Метрики: размер префикс-сета, ошибки DoH/CDN, `birdc show protocols`.
- Секреты BGP — Vault / K8s secrets, не plaintext в БД.
- Last-known-good конфиг на **volume** / bind-mount, общий для **bird2** и **evobgp-agent** (или снимок на хосте при bind-mount).
---
## 10. Реплика evobgp-node
1. Render на мастере упаковывает **бандл** (как для мастерского BIRD) + **manifest** (SHA-256) + **подпись** (например Ed25519).
2. **evobgp-node**: fetch → проверка → распаковка → `birdc configure`.
3. Общие фильтры/префиксы **идентичны**; `local.conf` на ноде (router id, source) — вне бандла.
4. Риски: дубли анонсов, отставание ревизий, компрометация без подписи.
---
## 11. Выбор СУБД
> **Решение по умолчанию:** **PostgreSQL** в **обоих** профилях — один тип миграций, один SQL-диалект в коде (`pgx` / `database/sql`), проще сопровождение и перенос с microVPS на reference без смены БД.
| Движок | Роль в плане |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **PostgreSQL** | **Основной** для `reference` и `microVPS` (различаются только ресурсы и `postgresql.conf`). |
| **SQLite** | **Опция** `microVPS_sqlite`: один контейнер без PG — только при жёстком лимите «ровно один контейнер приложения+БД в одном процессе». |
| **MySQL / MariaDB** | **Опционально** по требованию заказчика/хостинга; тот же слой DAO через второй драйвер — вне дефолтного пути. |
| **rqlite** | HA без отдельного DBA Postgres; **+контейнеры**; на 1 ГиБ тесно. |
### Почему PG «не много» и всё же два контейнера на microVPS
При **узкой** конфигурации (`shared_buffers` **64–128 МиБ**, `max_connections` **15–30**, **один** пул коннектов в `evobgp-all`, без сотен долгоживущих backend’ов) суммарный вклад PG **сопоставим** с практичным использованием SQLite в том же объёме RAM, зато:
- нет отличий DDL/типов между профилями;
- нормальные **advisory locks** / `SKIP LOCKED` для очереди `job_audit`;
- проще подключить **внешний** managed Postgres при росте.
На **microVPS** отдельные контейнеры `**postgres`** и стек **bird2 + evobgp-agent** — **осознанная плата** за единообразие с **reference** (минимум: `evobgp-all` + `postgres` + `bird2` + `evobgp-agent`).
### PostgreSQL: память и CPU (ориентиры)
| Сценарий соединения | RAM на backend |
| ---------------------- | -------------- |
| Чистый idle | ~1.5 МиБ |
| После простых запросов | ~10–11 МиБ |
| После тяжёлых / temp | ~14.5 МиБ |
Плюс `shared_buffers`, relation cache при огромной схеме, autovacuum. **reference:** при многих процессах Go — **PgBouncer** (transaction pooling). **microVPS:** пулер часто **не нужен**, если суммарно **≤10** реальных коннектов к PG. Таймауты: `idle_in_transaction_session_timeout`, `statement_timeout`, параметры `tcp_keepalives`_*; мониторинг `pg_stat_activity`.
### rqlite
Raft, HTTP/`gorqlite`; для HA control-plane; не замена дефолтного PG в плане без отдельного решения.
---
## 12. Профиль microVPS — детализация
> **Железо:** 1 vCPU · ~1024 МиБ RAM · **7–10 ГиБ** SSD под Docker + данные + логи · Ubuntu 24.04. Рекомендуется **swap 512 МиБ–1 ГиБ**, если его нет.
### Контейнеры
| Вариант | Состав |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| **A (целевой)** | **evobgp-all** + **postgres** + **bird2** + **evobgp-agent** (общий volume конфигов BIRD; см. [§4.1](#41-топология-docker-compose)) |
| **B (опция)** | Без отдельного PG — профиль **microVPS_sqlite** + тот же стек **bird2** + **evobgp-agent** |
| **C (legacy)** | BIRD только на хосте ОС — **не целевой путь**, только для отладки или жёстких ограничений Docker |
### Бюджет диска 7–10 ГиБ
| Статья | Ориентир |
| ---------------- | ------------------------------------- |
| Образ evobgp-all | ~50–150 МиБ |
| Образ PostgreSQL | ~80–200 МиБ (слои образа) |
| Данные PG | ~200 МиБ – 2 ГиБ (политика ревизий) |
| Конфиги / бандлы | ~10–200 МиБ |
| Логи Docker | **max-size** / **max-file** в compose |
| Prune | `docker system prune` по расписанию |
| Резерв ОС/пики | ≥1–2 ГиБ |
### CPU и RAM
- Worker pool ingest/render: **1–2**; без агрессивного параллелизма на одном ядре.
- `deploy.resources`: лимиты на **evobgp-all**, **postgres**, **bird2**, **evobgp-agent** (на PG ориентир **256m–384m**, на пару **bird2+agent** заложить **128m–256m** — уточнить по `docker stats`).
- Ориентир суммарно: **~400–750 МиБ** простой, **~550–900 МиБ** пик (**PG + Go + bird2 + система**) — на **1 ГиБ** обычно нужны **swap** и жёсткий тюнинг PG/`bird2`.
### Tuning PostgreSQL (microVPS)
Пример направлений (не копировать слепо — проверить по мониторингу):
- `shared_buffers` = **64–128 МиБ**; `max_connections` = **20–40**; `work_mem` умеренно низкий.
- Отключить или минимизировать `parallel_workers` на 1 vCPU.
- `effective_cache_size` подсказка планировщику без выделения RAM.
### Tuning PostgreSQL (reference)
Обычные практики под размер ВМ; **PgBouncer** при многих сервисах Go; резерв под autovacuum и пики ingest.
---
## 13. Снижение рисков (меры и процессы)
Дополняет [§9](#9-эксплуатация-и-масштаб) и [§10](#10-реплика-evobgp-node) конкретными обязательными практиками.
### Конфигурация BIRD: безопасное применение
- **Двухфазный deploy:** (1) запись новой ревизии во **временный** каталог на общем volume и проверка `**bird -c <path> -p`** (парсинг без запуска демона; см. `bird(8)`); (2) только при нулевом коде выхода — **атомарная** подмена активных файлов (rename) и `**birdc configure`**. Опционально перед фазой (2) — `preview`/diff в control-plane (REST).
- **Last-known-good (LKG):** хранить на volume **предыдущую** применённую ревизию; при неуспехе `configure` или ненулевом exit **автоматически** восстановить файлы LKG, зафиксировать событие в логах/метриках и **не** оставлять BIRD в полусобранном состоянии.
- **Canary в production:** перед полным apply — отдельный `bgp_speaker` или подмножество `bgp_peer` (см. [§9](#9-эксплуатация-и-масштаб)); полный выкат только после проверки сессий/префиксов на канареечном спикере.
### Данные, очередь, microVPS
- **Миграции:** в основной ветке — только **вперёд**; откат схемы — явные down-миграции (если приняты в процессе) или восстановление БД из бэкапа; политика фиксируется в операторской документации.
- **Jobs:** обязательные `**idempotency_key`** и уникальность в `job_audit` там, где это предотвращает двойной apply/reload.
- **microVPS:** пороги мониторинга на рост таблиц ревизий/артефактов, **retention** старых ревизий, лимиты ротации логов Docker (см. [§12](#12-профиль-microvps-детализация)) — с **алертами** при приближении к лимиту диска и OOM.
### Безопасность
- **evobgp-node:** в production **обязательна** проверка **подписи** бандла (отдельно от TLS транспорта); ключ подписи не смешивать с другими ролями.
- Секреты BGP (пароли, ключи) — только secret store / Docker secrets; **не** логировать полные конфиги с секретами.
### Поставка и совместимость
- В **Dockerfile** / Compose зафиксировать **версию образа BIRD 2** (тег minor или digest), совпадающую с образом, в котором выполняется `**bird -p`** в CI ([§15](#15-cicd-gitea-actions)).
- **Статический каркас** (router id, локальные интерфейсы, операторские правки) — в файлах **вне** автогенерируемых фрагментов; сгенерированное — только в согласованных путях `bird.d/` (см. [§8](#8-генерация-bird-и-ревизии), [§10](#10-реплика-evobgp-node)).
### Порядок внедрения (уточнение)
Перед полным набором микросервисов целесообразен параллельный этап: **контракт OpenAPI + каркас `internal/birdfmt` + golden-тесты + проверка `bird -p` в CI** ([§14](#14-тестирование-bird2-и-матрица-сценариев), [§15](#15-cicd-gitea-actions)), затем миграции PG и один вертикальный сценарий (например `IP_RANGES`).
---
## 14. Тестирование BIRD2 и матрица сценариев
Цель — покрыть **поверхность генератора** EvoBGP, а не весь язык BIRD. Комбинации фиксируются **матрицей** и каталогом сценариев в репозитории (`internal/birdfmt/testdata/scenarios/`).
### Матрица покрытия (ориентир)
| Измерение | Варианты для покрытия |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Роль спикера | master (полный набор фрагментов); реплика / **бандл** для `evobgp-node` (manifest + подмножество includes + локальный `local.conf`) |
| Типы модулей (выход render) | `AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS` (материализованные префиксы), `IP_RANGES` — минимум по одному сценарию; комбинация **2+ типов** в одной ревизии |
| Пиры | при поддержке каркасом — только static; **1× IPv4**, **1× IPv6**, несколько пиров, смешанный v4/v6 |
| Community | каждый поддерживаемый `kind` в справочнике + вариант без community (default) |
| Фильтры | экспорт «разрешить анонс»; при генерации — отрицательные кейсы (reject) |
| Граничные данные | пустой префикс-лист; один префикс; большой список (нагрузка на размер файла) |
| Шаблоны include | каждый именуемый фрагмент из `internal/birdfmt` участвует хотя бы в одном интеграционном сценарии |
По мере расширения генератора матрица **дополняется**; регрессия — новыми строками в таблице тестов и при необходимости новыми подкаталогами сценариев.
### Виды тестов
1. **Unit / snapshot (Go):** пакет `internal/birdfmt` — вход из фикстур, выход сравнивается с `*.golden` или `txtar`.
2. **Синтаксис BIRD в CI:** для каждого сценария с `bird.conf` выполняется `**bird -c … -p`** в контейнере с **той же major/minor версией BIRD 2**, что в production ([§13](#13-снижение-рисков-меры-и-процессы)).
3. **Позже:** контрактные тесты HTTP по `docs/openapi.yaml` и сценариям из [§7](#7-rest-api) после реализации `internal/httpapi`.
---
## 15. CI/CD (Gitea Actions)
- Workflows: каталог `**.gitea/workflows/`** в корне репозитория; синтаксис совместим с GitHub Actions ([документация Gitea Actions](https://docs.gitea.com/usage/actions/quickstart/)).
- Нужен зарегистрированный **act runner** с меткой `ubuntu-latest` (или согласованной с инсталляцией) и при job с Docker — доступ **Docker** на runner.
- Рекомендуемый pipeline: **lint OpenAPI** (`npx @redocly/cli lint docs/openapi.yaml`), `**go vet` / `go test` / `go build ./...`**, **проверка всех `testdata/scenarios/*/bird.conf` через `bird -p`** (см. workflow в репозитории).
---
## 16. Риски и этапы внедрения
### Риски
- Домены и устаревшие IP; DoH down; IRR/RIR vs локальные фильтры; community-ошибки; дубли при master+node; **microVPS:** переполнение диска логами/ревизиями, OOM без swap.
### Этапы
1. Monorepo Go, миграции **PostgreSQL** (основной путь), опционально SQLite для `microVPS_sqlite`, Compose `reference` и `microVPS`.
2. Один тип модуля end-to-end; render + BIRD.
3. Расписания CDN/модуля; deploy + agent.
4. Ревизии, rollback, bundle API.
5. **evobgp-node** на отдельной ВМ; observability.
6. Hardening, документация операторская.
7. **Web UI** в отдельном контейнере ([§17](#17-web-ui-svelte-shadcn-svelte)): полный UX по спискам, расписанию и мониторингу.
---
## 17. Web UI (Svelte, shadcn-svelte)
Цель — **единая операторская поверхность** поверх уже описанного REST ([§7](#7-rest-api)) и observability ([§4](#4-сервисы-сравнение-профилей), этапы в [§16](#16-риски-и-этапы-внедрения)), без дублирования бизнес-логики на фронтенде: UI только вызывает API и визуализирует состояние.
### Стек и упаковка
- **Svelte** (актуальная ветка проекта, SvelteKit при необходимости SSR/роутинга) и **shadcn-svelte** — доступные компоненты (формы, таблицы, диалоги, навигация), единый визуальный язык.
- **Отдельный сервис в Docker Compose** (профиль **reference**): образ со статической сборкой или Node-сервером за reverse-proxy; **не** вшивать UI в `evobgp-api`. Контейнер получает только `VITE_*` / публичный **base URL API** и при необходимости URL метрик/health-прокси (см. ниже).
- Сборка и публикация артефакта UI — отдельный job в CI ([§15](#15-cicd-gitea-actions)) по мере появления кода в репозитории (например `web/` или `ui/`).
### Покрытие user experience
1. **Списки и сущности** — модули префиксов, ревизии, пиры, community, DoH-профили, спикеры: просмотр, фильтрация, создание/редактирование там, где это отражено в OpenAPI; связь с jobs и аудитом ([§7.10](#710-задачи-job_audit)).
2. **Расписание** — триггеры и окна обновления модулей/ETL, ручной refresh, очередь задач и статусы без «чёрного ящика» ([§7](#7-rest-api), scheduler в [§1](#1-структура-репозитория-файлы-и-пакеты)).
3. **Мониторинг системы** — дашборд health API, агентов и нод; интеграция с метриками/алертами (Prometheus/Grafana или встроенные виджеты по публичным эндпоинтам); наглядное состояние BGP-сессий и последних deploy/revision там, где данные доступны через API или безопасный read-only прокси.
### Безопасность и эксплуатация
- Аутентификация и роли — по [§7.13](#713-матрица-прав-роли); UI не хранит секреты вне согласованного потока (cookie/session или OIDC — на этапе проектирования конкретной инсталляции).
- Для **microVPS** полноценный отдельный контейнер UI **опционален** (можно тот же образ с `profiles` или отключённый сервис), чтобы не раздувать single-node; приоритет — **reference** как эталон операторской панели.
---
**Соответствие старым именам:** `full` ≈ `reference`, `target_vps` ≈ `microVPS`.