---
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`.