openapi: 3.1.0 info: title: EvoBGP Control Plane API version: 1.0.0 description: | REST API управления префиксами, модулями ingest, ревизиями конфигурации BIRD и задачами (async jobs). **Актуальная semver-сборка:** `GET /version` или `GET /v1/version` (поле `version`; совпадает с git-тегом `vX.Y.Z`). **Соглашения:** префикс `/v1`; идентификаторы - UUID v7 или ULID (строки); время - ISO 8601 UTC. Ошибки - `application/problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)). Пагинация списков - `cursor` + `limit`; ответ содержит `items`, `next_cursor`, `has_more`. **Роли** (матрица доступа): `viewer`, `editor`, `operator`, `node`. Нода использует отдельные пути и ключ с ролью `node`. Заголовок `X-Tenant-Id` допускается только для супер-ролей (явный tenant); иначе tenant берётся из API-ключа. license: name: Proprietary identifier: LicenseRef-Proprietary servers: - url: https://api.example.com description: Базовый URL инсталляции (замените на свой) tags: - name: System description: Liveness, readiness и метаданные сборки. Обычно без чувствительных данных; доступ может быть шире. - name: Modules description: Экземпляры модулей префиксов (AS, CDN, домены, статические IP-диапазоны) и вложенные записи. Чтение - viewer+; изменение - editor+. - name: DoH profiles description: Профили DNS-over-HTTPS для модулей типа домены. Секрет в ответах не возвращается. - name: Communities description: Справочник BGP community в скоупе tenant. - name: Peers description: BGP-пиры и политики; привязка к спикеру или ко всем спикерам. - name: Speakers description: Регистрация и метаданные экземпляров BIRD (master/replica/canary). - name: Revisions description: История ревизий конфигурации, снимки префиксов, превью и откат (часто асинхронно). - name: Deploy description: Применение ревизий на спикерах и опциональный reload BIRD. Требуется роль operator. - name: Jobs description: Асинхронные задачи; статус и отмена (best-effort). - name: Node description: "API для evobgp-node (бандлы ревизий и enrollment). Отдельный ключ или mTLS, роль node." - name: Settings description: Глобальные настройки и feature flags; изменение - только operator. - name: API keys description: Управление API-ключами tenant (operator). Секрет возвращается только при создании и ротации. - name: Auth description: Сессия текущего API-ключа (tenant и роль). - name: Monitoring description: Наблюдаемость PostgreSQL и корреляция (instance-level, viewer+). Maintenance — operator. - name: Maintenance description: Политики обслуживания PostgreSQL (instance-scoped). CRUD и запуск — operator. - name: RuntimeLogs description: | Файловые runtime-логи Docker-сервисов (каталог EVOBGP_RUNTIME_LOGS_DIR). Доступно только в процессе evobgp-all с примонтированным volume; иначе 503. Просмотр — viewer+; очистка — operator+ (синхронно, с audit). - name: Firewall description: Linux firewall blocklist clients, policy rules (block/accept), and data-plane sync. security: - bearerAuth: [] externalDocs: description: Черновик и контекст API url: ./evobgp-api-sketches.md components: securitySchemes: bearerAuth: type: http scheme: bearer description: API-ключ в формате `Bearer `. На edge может заменяться или дополняться mTLS. parameters: TenantId: name: X-Tenant-Id in: header required: false schema: $ref: "#/components/schemas/ResourceId" description: > Явный tenant (только супер-роли). Без заголовка tenant определяется по API-ключу. **Реализация v1:** заголовок в Go handlers не обрабатывается; tenant только из Bearer-токена (см. docs/access.md). IdempotencyKey: name: Idempotency-Key in: header required: false schema: type: string maxLength: 256 description: Идемпотентность мутаций. Для `POST` apply и части refresh рекомендуется или обязателен по политике продукта. Cursor: name: cursor in: query schema: type: string description: Непрозрачный курсор из предыдущего ответа. Limit: name: limit in: query schema: type: integer minimum: 1 maximum: 500 default: 50 description: Размер страницы (по умолчанию 50). ModuleId: name: module_id in: path required: true schema: $ref: "#/components/schemas/ResourceId" SpeakerId: name: speaker_id in: path required: true schema: $ref: "#/components/schemas/ResourceId" RevisionId: name: revision_id in: path required: true schema: $ref: "#/components/schemas/ResourceId" JobId: name: job_id in: path required: true schema: $ref: "#/components/schemas/ResourceId" PeerId: name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" DohProfileId: name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" CommunityId: name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" ApiKeyId: name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" SourceId: name: source_id in: path required: true schema: $ref: "#/components/schemas/ResourceId" EntryId: name: entry_id in: path required: true schema: $ref: "#/components/schemas/ResourceId" RevisionA: name: revision_a in: path required: true schema: $ref: "#/components/schemas/ResourceId" RevisionB: name: revision_b in: path required: true schema: $ref: "#/components/schemas/ResourceId" ModuleTypeFilter: name: type in: query schema: $ref: "#/components/schemas/ModuleType" ModuleEnabledFilter: name: enabled in: query schema: type: boolean SpeakerFilter: name: speaker_id in: query schema: $ref: "#/components/schemas/ResourceId" RevisionModuleFilter: name: module_id in: query schema: $ref: "#/components/schemas/ResourceId" JobStatusFilter: name: status in: query schema: $ref: "#/components/schemas/JobStatus" JobKindFilter: name: kind in: query schema: type: string description: > Фильтр по виду задачи; точный перечень расширяем. Основные значения: `module_refresh`, `peer_reconcile`, `deploy_apply`, `revision_rollback`, `bird_reload`. responses: Unauthorized: description: Нет или невалидный Bearer-токен. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" Forbidden: description: Недостаточно прав для операции. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" NotFound: description: Ресурс не найден в скоупе tenant. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" Conflict: description: Конфликт состояния (например, удаление DoH-профиля, который ещё используется). content: application/problem+json: schema: $ref: "#/components/schemas/Problem" UnprocessableEntity: description: Семантическая ошибка запроса или валидации полей. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" DefaultProblem: description: Ошибка (см. тело Problem). content: application/problem+json: schema: $ref: "#/components/schemas/Problem" schemas: ResourceId: type: string description: UUID v7 или ULID. pattern: "^[0-9A-Za-z_-]{20,36}$" examples: - 01JQXYZABCDEFGHIJKLMNOPQRS Problem: type: object description: Problem Details (RFC 9457). required: - title - status properties: type: type: string format: uri-reference description: URI типа проблемы. title: type: string status: type: integer detail: type: string instance: type: string format: uri-reference errors: type: array items: $ref: "#/components/schemas/FieldError" additionalProperties: true FieldError: type: object properties: field: type: string description: Имя поля (или JSON Pointer). pointer: type: string description: JSON Pointer к значению. message: type: string additionalProperties: true AsyncJobAccepted: type: object required: - job_id - status properties: job_id: $ref: "#/components/schemas/ResourceId" status: type: string enum: [queued] description: Начальный статус задачи в очереди. ModuleType: type: string enum: - AS_PREFIXES - CDN_CIDRS - DOMAINS - IP_RANGES DohResolverPolicy: type: string description: | Политика резолва доменов через DoH при нескольких профилях. `primary_only` — только первый профиль; `failover` — по порядку до первого успешного; `union` — объединение A/AAAA со всех профилей. enum: - primary_only - failover - union default: primary_only JobStatus: type: string description: Статус задачи; перечень может расширяться. enum: - queued - running - succeeded - failed - cancelled ApplyStrategy: type: string description: Стратегия выбора спикеров для apply. enum: - all_speakers Module: type: object required: - id - type - name - enabled - priority properties: id: $ref: "#/components/schemas/ResourceId" type: $ref: "#/components/schemas/ModuleType" name: type: string enabled: type: boolean priority: type: integer doh_profile_id: type: ["string", "null"] description: Первый DoH-профиль (legacy); предпочтительно `doh_profile_ids`. doh_profile_ids: type: array items: $ref: "#/components/schemas/ResourceId" description: Упорядоченный список DoH-профилей для модулей `DOMAINS`. doh_resolver_policy: $ref: "#/components/schemas/DohResolverPolicy" refresh_interval_sec: type: ["integer", "null"] minimum: 0 cron_expr: type: ["string", "null"] default_community_id: type: ["string", "null"] last_refreshed_at: type: ["string", "null"] format: date-time description: Время последнего успешного обновления данных модуля. additionalProperties: true ModuleCreate: type: object required: - type - name properties: type: $ref: "#/components/schemas/ModuleType" name: type: string enabled: type: boolean default: true priority: type: integer default: 0 doh_profile_id: type: ["string", "null"] description: Первый DoH-профиль (legacy); предпочтительно `doh_profile_ids`. doh_profile_ids: type: array items: $ref: "#/components/schemas/ResourceId" description: Упорядоченный список DoH-профилей для модулей `DOMAINS`. doh_resolver_policy: $ref: "#/components/schemas/DohResolverPolicy" refresh_interval_sec: type: ["integer", "null"] last_refreshed_at: type: ["string", "null"] format: date-time description: Время последнего успешного обновления этого CDN-источника. cron_expr: type: ["string", "null"] default_community_id: type: ["string", "null"] ModulePatch: type: object properties: name: type: string enabled: type: boolean priority: type: integer doh_profile_id: type: ["string", "null"] description: Первый DoH-профиль (legacy); предпочтительно `doh_profile_ids`. doh_profile_ids: type: array items: $ref: "#/components/schemas/ResourceId" description: Упорядоченный список DoH-профилей для модулей `DOMAINS`. doh_resolver_policy: $ref: "#/components/schemas/DohResolverPolicy" refresh_interval_sec: type: ["integer", "null"] cron_expr: type: ["string", "null"] default_community_id: type: ["string", "null"] CdnSource: type: object required: - id - url - source_kind properties: id: $ref: "#/components/schemas/ResourceId" url: type: string format: uri source_kind: type: string description: Формат скачанного списка / парсер. prefix_path: type: string description: Путь до поля с префиксами для source_kind=json (например data.items[].cidr). community_id: type: ["string", "null"] refresh_interval_sec: type: ["integer", "null"] CdnSourceCreate: type: object required: - url - source_kind properties: url: type: string format: uri source_kind: type: string prefix_path: type: string community_id: type: ["string", "null"] CdnSourcePatch: type: object properties: url: type: string format: uri source_kind: type: string prefix_path: type: string community_id: type: ["string", "null"] refresh_interval_sec: type: ["integer", "null"] CdnPreviewRequest: type: object required: [url, source_kind] properties: url: type: string format: uri source_kind: type: string prefix_path: type: string CdnPreviewResponse: type: object required: [items, total, truncated, source_url] properties: items: type: array items: type: string total: type: integer truncated: type: boolean source_url: type: string AsEntry: type: object required: - id - asn properties: id: $ref: "#/components/schemas/ResourceId" asn: type: integer minimum: 1 maximum: 4294967295 community_id: type: ["string", "null"] additionalProperties: true AsEntryCreate: type: object required: - asn properties: asn: type: integer minimum: 1 maximum: 4294967295 community_id: type: ["string", "null"] additionalProperties: true AsEntryPatch: type: object properties: asn: type: integer minimum: 1 maximum: 4294967295 community_id: type: ["string", "null"] additionalProperties: true DomainEntry: type: object required: - id - fqdn properties: id: $ref: "#/components/schemas/ResourceId" fqdn: type: string community_id: type: ["string", "null"] DomainEntryCreate: type: object required: - fqdn properties: fqdn: type: string community_id: type: ["string", "null"] IpRangeEntry: type: object required: - id - prefix - community_id properties: id: $ref: "#/components/schemas/ResourceId" prefix: type: string description: IPv4/IPv6 CIDR. example: 203.0.113.0/24 community_id: $ref: "#/components/schemas/ResourceId" IpRangeEntryCreate: type: object required: - prefix - community_id properties: prefix: type: string community_id: $ref: "#/components/schemas/ResourceId" DohProfile: type: object required: - id properties: id: $ref: "#/components/schemas/ResourceId" name: type: string description: Отображаемое имя профиля (опционально). url: type: string format: uri timeout_ms: type: integer vault_secret_ref: type: ["string", "null"] description: Ссылка на секрет в vault; сырое значение не отдаётся в GET. additionalProperties: true DohProfileCreate: type: object required: - url properties: name: type: string description: Отображаемое имя профиля (опционально). url: type: string format: uri timeout_ms: type: integer vault_secret_ref: type: ["string", "null"] AuthSession: type: object required: [tenant_id, role] properties: tenant_id: $ref: "#/components/schemas/ResourceId" role: type: string enum: [viewer, editor, operator, node] ApiKey: type: object required: [id, name, role, prefix, created_at, updated_at] properties: id: $ref: "#/components/schemas/ResourceId" name: type: string role: type: string enum: [viewer, editor, operator, node] prefix: type: string description: Первые 8 символов токена для идентификации в UI. created_at: type: string format: date-time updated_at: type: string format: date-time expires_at: type: ["string", "null"] format: date-time revoked_at: type: ["string", "null"] format: date-time last_used_at: type: ["string", "null"] format: date-time additionalProperties: true ApiKeyCreate: type: object required: [name, role] properties: name: type: string role: type: string enum: [viewer, editor, operator, node] expires_at: type: ["string", "null"] format: date-time ApiKeyPatch: type: object properties: name: type: string role: type: string enum: [viewer, editor, operator, node] expires_at: type: ["string", "null"] format: date-time ApiKeyCreated: allOf: - $ref: "#/components/schemas/ApiKey" - type: object required: [token] properties: token: type: string description: Полный Bearer-токен; показывается один раз. BgpCommunity: type: object required: - id properties: id: $ref: "#/components/schemas/ResourceId" community: type: string description: Техническое значение BGP community (строка для BIRD, например 65001:120 или large JSON в value_json). title: type: string description: Человекочитаемое название для UI и фильтров. additionalProperties: true BgpPeer: type: object required: - id properties: id: $ref: "#/components/schemas/ResourceId" neighbor: type: string description: IP соседа. remote_asn: type: integer bgp_speaker_id: type: ["string", "null"] description: "`null` - политика для всех спикеров." connected_speaker_id: type: ["string", "null"] description: > Live (GET /v1/peers?live=1): спикер, на котором сессия Established; опрос CP birdc + agent /v1/agent/bird/protocols. connected_speaker_label: type: string description: Человекочитаемая метка ноды из live-опроса. session_on_speakers: type: array description: Состояние протокола пира на каждой опрошенной ноде. items: type: object properties: speaker_id: type: string label: type: string state: type: string established_on_speakers: type: array description: Ноды, где сессия в состоянии Established (один пир может быть на нескольких). items: type: object properties: speaker_id: type: string label: type: string state: type: string session_mismatch: type: boolean description: > true если bgp_speaker_id задан, но на этой ноде нет Established (сессия может быть на других нодах — это не ошибка для tenant-wide пиров). policies_json: type: string description: > JSON-объект (строка). Поля `local_ipv4`, `local_ipv6`, `local_asn` переопределяют глобальные `bird_local_ipv4` / `bird_local_ipv6` / `bird_local_asn` тенанта. Если эффективный локальный адрес или ASN пира отличается от дефолтов тенанта (`bird_local_*`), в блок `protocol bgp … from bgp_template` добавляется строка `local … as …`. additionalProperties: true BgpSpeaker: type: object required: - id properties: id: $ref: "#/components/schemas/ResourceId" role: type: string description: master, replica, canary и т.д. endpoint: type: string last_applied_revision_id: type: ["string", "null"] published_revision_id: type: ["string", "null"] description: Последняя опубликованная на CP ревизия для этого спикера. published_at: type: ["string", "null"] format: date-time agent_domain: type: string description: FQDN agent API за Traefik (Address в UI, Remnawave-style). node_ipv4: type: string description: IPv4 VPS; default для bird_bgp_source_ipv4. bird_bgp_source_ipv4: type: string description: Per-speaker override router id / BGP local (см. pipeline overlay). dispatch_status: type: string description: ok, error, skipped — последний Panel→Node wake-up. sync_status: type: string description: synced, error — состояние sync на реплике. last_dispatch_at: type: string format: date-time last_dispatch_error: type: string meta_json: type: object description: > Расширяемый объект. Ключи agent_domain, agent_secret (только при создании), agent_port, node_ipv4, bird_bgp_source_ipv4, bird_bgp_source_ipv6. live: $ref: "#/components/schemas/SpeakerLiveStatus" description: > При GET /v1/speakers?live=1 — runtime-статус agent и BGP-опроса на ноде. additionalProperties: true SpeakerLiveStatus: type: object description: Live runtime snapshot for one speaker (GET /v1/speakers?live=1). properties: label: type: string description: Человекочитаемая метка ноды (agent domain или CP master). agent_ok: type: boolean description: true если agent /v1/agent/health успешен (master — local birdc poll). agent_error: type: string agent_last_sync_at: type: string format: date-time agent_last_applied_revision_id: type: string bgp_poll_ok: type: boolean description: true если birdc (CP) или GET /v1/agent/bird/protocols (replica) успешен. bgp_poll_error: type: string bgp_sessions_total: type: integer bgp_established: type: integer sessions: type: array items: $ref: "#/components/schemas/BgpSessionLive" additionalProperties: true BgpSessionLive: type: object properties: name: type: string neighbor: type: string state: type: string additionalProperties: true LiveSpeakerPoll: type: object description: Метаданные опроса одной ноды в GET /v1/peers?live=1. properties: speaker_id: type: string label: type: string ok: type: boolean session_count: type: integer poll_error: type: string additionalProperties: true PostgresOverview: type: object description: Instance-level PostgreSQL snapshot (GET /v1/monitoring/postgres/overview). additionalProperties: true PostgresQueriesResponse: type: object properties: collected_at: type: string format: date-time source: type: string enum: [live, snapshot] items: type: array items: type: object additionalProperties: true PostgresRecommendations: type: object properties: collected_at: type: string format: date-time items: type: array items: type: object properties: severity: type: string code: type: string title: type: string detail: type: string refs: type: array items: type: string PostgresMaintenanceBody: type: object properties: table: type: string dry_run: type: boolean default: false policy: type: string description: Deprecated; use maintenance policies API. limit: type: integer MaintenancePolicy: type: object required: [name, table_name, schedule, vacuum_strategy] properties: id: $ref: "#/components/schemas/ResourceId" name: type: string table_name: type: string condition: type: string default: "true" retention_period_sec: type: integer minimum: 1 max_rows: type: integer minimum: 1 maximum: 100000 vacuum_strategy: type: string enum: [none, vacuum, analyze, vacuum_analyze, reindex] schedule: type: string description: Cron expression (5-field, UTC). enabled: type: boolean default: true dry_run_enabled: type: boolean default: false last_run_at: type: string format: date-time last_status: type: string last_error: type: string created_at: type: string format: date-time updated_at: type: string format: date-time MaintenancePolicyPatch: type: object properties: name: type: string table_name: type: string condition: type: string retention_period_sec: type: integer max_rows: type: integer vacuum_strategy: type: string enum: [none, vacuum, analyze, vacuum_analyze, reindex] schedule: type: string enabled: type: boolean dry_run_enabled: type: boolean MaintenanceRunBody: type: object required: [policy_id] properties: policy_id: $ref: "#/components/schemas/ResourceId" MaintenancePolicyList: type: object properties: items: type: array items: $ref: "#/components/schemas/MaintenancePolicy" next_cursor: type: string has_more: type: boolean RuntimeLogCleanupMode: type: string enum: [truncate, delete] description: | truncate — обнулить файл (по умолчанию); delete — удалить файл с диска. RuntimeLogFile: type: object required: [name, size_bytes, modified_at] properties: name: type: string description: Basename файла (*.log) в каталоге runtime-логов. pattern: '^[a-z0-9][a-z0-9_.-]*\.log$' size_bytes: type: integer format: int64 minimum: 0 modified_at: type: string format: date-time RuntimeLogFileList: type: object required: [items] properties: items: type: array items: $ref: "#/components/schemas/RuntimeLogFile" RuntimeLogTail: type: object required: [filename, content, truncated, lines_returned] properties: filename: type: string content: type: string description: UTF-8 текст хвоста файла. truncated: type: boolean description: true если применён лимит bytes/lines. lines_returned: type: integer minimum: 0 RuntimeLogCleanupResult: type: object required: [audit_id, filename, action, size_before] properties: audit_id: $ref: "#/components/schemas/ResourceId" filename: type: string action: $ref: "#/components/schemas/RuntimeLogCleanupMode" size_before: type: integer format: int64 size_after: type: ["integer", "null"] format: int64 RuntimeLogCleanupAudit: type: object required: [id, tenant_id, actor_prefix, filename, action, size_before, created_at] properties: id: $ref: "#/components/schemas/ResourceId" tenant_id: $ref: "#/components/schemas/ResourceId" actor_prefix: type: string filename: type: string action: $ref: "#/components/schemas/RuntimeLogCleanupMode" size_before: type: integer format: int64 size_after: type: ["integer", "null"] format: int64 detail: type: object additionalProperties: true created_at: type: string format: date-time RuntimeLogCleanupAuditList: type: object properties: items: type: array items: $ref: "#/components/schemas/RuntimeLogCleanupAudit" next_cursor: type: string has_more: type: boolean RuntimeLogAutoPolicy: type: object properties: enabled: type: boolean max_file_bytes: type: integer format: int64 schedule: type: string description: UTC cron (minute hour dom month dow). mode: $ref: "#/components/schemas/RuntimeLogCleanupMode" RuntimeLogAutoEstimateItem: type: object required: [filename, size_bytes, would_cleanup] properties: filename: type: string size_bytes: type: integer format: int64 would_cleanup: type: boolean skip_reason: type: string description: under_threshold, too_large, или текст ошибки. RuntimeLogAutoEstimate: type: object properties: policy: $ref: "#/components/schemas/RuntimeLogAutoPolicy" items: type: array items: $ref: "#/components/schemas/RuntimeLogAutoEstimateItem" would_count: type: integer minimum: 0 RuntimeLogAutoRunResult: type: object additionalProperties: true description: | dry_run, trigger, policy, cleaned[], skipped[], cleaned_count, skipped_count. BirdLocalStatus: type: object description: Статус локального BIRD на хосте API (GET /v1/bird/status). properties: birdc_configured: type: boolean message: type: string error: type: string protocols_excerpt: type: string bgp_sessions_total: type: integer bgp_established: type: integer healthy: type: ["boolean", "null"] additionalProperties: true BundleSigningPublicKey: type: object required: [public_key_base64] properties: public_key_base64: type: string description: Ed25519 public key (base64) для verify-bundle на реплике. ConfigRevision: type: object required: - id properties: id: $ref: "#/components/schemas/ResourceId" parent_revision_id: type: ["string", "null"] content_hash: type: string created_at: type: string format: date-time additionalProperties: true RevisionPruneEstimate: type: object required: - retention_minutes - cutoff_at - revision_count - prefix_row_count - orphan_snapshot_count - bytes_estimate properties: retention_minutes: type: integer minimum: 15 maximum: 43200 cutoff_at: type: string format: date-time revision_count: type: integer minimum: 0 prefix_row_count: type: integer minimum: 0 description: Строки prefix_snapshot_row в освобождаемых снимках. orphan_snapshot_count: type: integer minimum: 0 bytes_estimate: type: integer format: int64 minimum: 0 description: Ориентировочный логический объём данных (байты). RevisionPruneResult: type: object required: - deleted_revisions - deleted_prefix_snapshots - deleted_prefix_rows - bytes_estimate properties: deleted_revisions: type: integer minimum: 0 deleted_prefix_snapshots: type: integer minimum: 0 deleted_prefix_rows: type: integer minimum: 0 bytes_estimate: type: integer format: int64 minimum: 0 RevisionPruneRequest: type: object properties: retention_minutes: type: integer minimum: 15 maximum: 43200 description: TTL в минутах; если не задан — из revision_retention_minutes tenant settings. PrefixSnapshotItem: type: object description: > Элемент материализованного снимка. Поле prefix обычно содержит CIDR; для модулей AS_PREFIXES допускается ключ вида as:<номер_asn> (не CIDR). additionalProperties: true Job: type: object required: - job_id - kind - status - created_at properties: job_id: $ref: "#/components/schemas/ResourceId" kind: type: string status: $ref: "#/components/schemas/JobStatus" idempotency_key: type: ["string", "null"] created_at: type: string format: date-time started_at: type: ["string", "null"] description: ISO 8601 UTC, если задача уже стартовала. finished_at: type: ["string", "null"] description: ISO 8601 UTC по завершении. error: type: ["string", "null"] meta: type: object additionalProperties: true ApplyRequest: type: object properties: revision_id: $ref: "#/components/schemas/ResourceId" strategy: $ref: "#/components/schemas/ApplyStrategy" dry_run: type: boolean default: false VersionInfo: type: object properties: version: type: string description: Semver сборки (git tag без префикса v). example: "1.2.3" api_version: type: string deprecated: true description: Alias поля `version` (сохранён для обратной совместимости). git_sha: type: string build_time: type: string format: date-time additionalProperties: true HealthStatus: type: object properties: status: type: string example: ok additionalProperties: true ReadyStatus: type: object properties: status: type: string example: ready checks: type: object additionalProperties: true additionalProperties: true Settings: type: object description: | KV настройки tenant/глобальные лимиты и feature flags. Параметры BIRD (строки в `global_settings`, JSON-значения — обычно строка или число): `bird_router_id`, `bird_local_ipv4`, `bird_local_ipv6`, `bird_local_asn`; если задан `bird_bgp_source_ipv4`, он подставляется как BIRD `router id` (перекрывает `bird_router_id`). Ключи `bird_bgp_source_ipv4` / `bird_bgp_source_ipv6` в сгенерированном BGP для пиров не используются (оставлены для совместимости API). Шаблон BGP в конфиге: `local as ;` без локального IP. properties: bird_router_id: type: string description: BIRD `router id` (IPv4 dotted quad), если не задан `bird_bgp_source_ipv4`. bird_local_ipv4: type: string bird_local_ipv6: type: string bird_local_asn: type: integer bird_bgp_source_ipv4: type: string description: | Если задан — используется как BIRD `router id` (IPv4). В блоках `protocol bgp … from bgp_template` строка `source address` не генерируется. bird_bgp_source_ipv6: type: string description: Зарезервировано; в текущей генерации BGP не используется. revision_retention_minutes: type: integer minimum: 15 maximum: 43200 runtime_logs_auto_enabled: type: boolean description: Автоочистка *.log на evobgp-all по расписанию (только при FS volume). runtime_logs_max_file_mb: type: integer minimum: 1 maximum: 512 description: Truncate/delete файлов строго больше порога (MiB). runtime_logs_auto_schedule: type: string description: UTC cron для автоочистки (по умолчанию `0 */6 * * *`). runtime_logs_auto_mode: $ref: "#/components/schemas/RuntimeLogCleanupMode" additionalProperties: true RevisionDiff: type: object description: | Сравнение двух ревизий. Конкретный формат (JSON Patch, табличный diff и т.д.) задаётся реализацией - контракт может уточняться. additionalProperties: true NodeEnrollRequest: type: object description: Тело регистрации ноды; протокол обмена ключами уточняется отдельно. properties: public_key: type: string speaker_id: $ref: "#/components/schemas/ResourceId" additionalProperties: true NodeEnrollResponse: type: object description: Подтверждение записи enrollment (метаданные спикера обновлены). properties: status: type: string example: enrolled speaker_id: $ref: "#/components/schemas/ResourceId" tenant_id: $ref: "#/components/schemas/ResourceId" additionalProperties: true DohProfilePatch: type: object properties: name: type: string url: type: string format: uri timeout_ms: type: integer vault_secret_ref: type: ["string", "null"] BgpCommunityCreate: type: object required: [community] properties: community: type: string title: type: string additionalProperties: true BgpCommunityPatch: type: object properties: community: type: string title: type: string additionalProperties: true BgpPeerCreate: type: object required: [neighbor, remote_asn] properties: neighbor: type: string remote_asn: type: integer bgp_speaker_id: type: ["string", "null"] additionalProperties: true BgpPeerPatch: type: object properties: neighbor: type: string remote_asn: type: integer bgp_speaker_id: type: ["string", "null"] additionalProperties: true BgpSpeakerCreate: type: object required: [endpoint] properties: role: type: string default: replica endpoint: type: string description: URL agent или https://AGENT_DOMAIN meta_json: type: string description: > JSON-объект. Ключи node_ipv4, bird_bgp_source_ipv4 (default = node_ipv4), agent_domain, agent_secret (генерируется при создании если пуст). additionalProperties: true BgpSpeakerPatch: type: object properties: role: type: string endpoint: type: string meta_json: type: string description: JSON-объект с ключами agent_domain, node_ipv4, bird_bgp_source_ipv4 и др. additionalProperties: true LatestRevisionPointer: type: object required: - revision_id properties: revision_id: $ref: "#/components/schemas/ResourceId" published_at: type: string format: date-time FirewallClient: type: object properties: id: $ref: "#/components/schemas/ResourceId" name: type: string hostname: type: string token_prefix: type: string status: type: string enum: [pending, approved, revoked] last_seen_at: type: string format: date-time last_apply_at: type: string format: date-time last_apply_status: type: string last_apply_prefix_count: type: integer last_apply_packets_dropped: type: integer format: int64 description: Cumulative packets dropped by blocklist rule (from client kernel counter). last_apply_packets_accepted: type: integer format: int64 description: Cumulative packets accepted past blocklist chain (nft counter accept rule). client_version: type: string FirewallInstallContext: type: object description: Контекст для one-liner установки firewall-клиента (только operator). properties: bundle_seed: type: string description: Значение EVOBGP_BUNDLE_SEED_HEX на control plane. bundle_seed_configured: type: boolean suggested_cp_url: type: string format: uri install_sh_url: type: string format: uri FirewallRule: type: object properties: id: $ref: "#/components/schemas/ResourceId" client_id: $ref: "#/components/schemas/ResourceId" nullable: true priority: type: integer action: type: string enum: [block, accept] community_id: $ref: "#/components/schemas/ResourceId" nullable: true comment: type: string FirewallBlocklist: type: object properties: client_id: $ref: "#/components/schemas/ResourceId" revision_id: $ref: "#/components/schemas/ResourceId" prefixes: type: array items: type: string total: type: integer hash: type: string paths: /v1/health: get: tags: [System] summary: Liveness description: Проверка, что процесс API жив. Не обязана проверять БД или брокер. operationId: getHealth responses: "200": description: Процесс отвечает. content: application/json: schema: $ref: "#/components/schemas/HealthStatus" default: $ref: "#/components/responses/DefaultProblem" /v1/ready: get: tags: [System] summary: Readiness description: Готовность к трафику (БД, брокер сообщений при reference-архитектуре и т.д.). operationId: getReady responses: "200": description: Сервис готов принимать запросы. content: application/json: schema: $ref: "#/components/schemas/ReadyStatus" "503": description: Не готов (зависимости недоступны). content: application/problem+json: schema: $ref: "#/components/schemas/Problem" default: $ref: "#/components/responses/DefaultProblem" /version: get: tags: [System] summary: Версия сборки (корневой путь) description: | Аналог `GET /v1/version`. Публичный маршрут без аутентификации. Semver в поле `version` задаётся при сборке Docker-образов из git-тега. operationId: getVersionRoot responses: "200": description: Метаданные сборки. content: application/json: schema: $ref: "#/components/schemas/VersionInfo" default: $ref: "#/components/responses/DefaultProblem" /v1/version: get: tags: [System] summary: Версия сборки description: | Semver control-plane и метаданные сборки (`git_sha`, `build_time`). Дублирует `GET /version`. operationId: getVersion responses: "200": description: Метаданные сборки. content: application/json: schema: $ref: "#/components/schemas/VersionInfo" default: $ref: "#/components/responses/DefaultProblem" /v1/modules: get: tags: [Modules] summary: Список модулей description: Модули tenant с опциональными фильтрами по типу и флагу `enabled` (фильтры применяются сервером). operationId: listModules parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/ModuleTypeFilter" - $ref: "#/components/parameters/ModuleEnabledFilter" responses: "200": description: Страница модулей. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/Module" next_cursor: type: ["string", "null"] has_more: type: boolean "401": $ref: "#/components/responses/Unauthorized" default: $ref: "#/components/responses/DefaultProblem" /v1/router-lists/catalog: get: tags: [Modules] summary: Агрегированный каталог для router-lists-ui description: | Возвращает в одном ответе: - модули типов `DOMAINS`, `IP_RANGES`, `AS_PREFIXES`; - entries по каждому модулю; - справочник community (`id`, `community`, `title`). operationId: getRouterListsCatalog parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Агрегированные данные для списков UI. content: application/json: schema: type: object required: [modules, domains, asns, ip_ranges, communities] properties: modules: type: object required: [items] properties: items: type: array items: $ref: "#/components/schemas/Module" domains: type: object required: [items] properties: items: type: array items: type: object required: [module_id, entry] properties: module_id: $ref: "#/components/schemas/ResourceId" entry: $ref: "#/components/schemas/DomainEntry" asns: type: object required: [items] properties: items: type: array items: type: object required: [module_id, entry] properties: module_id: $ref: "#/components/schemas/ResourceId" entry: $ref: "#/components/schemas/AsEntry" ip_ranges: type: object required: [items] properties: items: type: array items: type: object required: [module_id, entry] properties: module_id: $ref: "#/components/schemas/ResourceId" entry: $ref: "#/components/schemas/IpRangeEntry" communities: type: object required: [items] properties: items: type: array items: $ref: "#/components/schemas/BgpCommunity" "401": $ref: "#/components/responses/Unauthorized" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Modules] summary: Создать модуль description: | Создаёт экземпляр модуля. Поле `type` задаёт вид (`AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS`, `IP_RANGES`). `module_id` в других путях - идентификатор экземпляра, не имя типа. operationId: createModule parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ModuleCreate" responses: "201": description: Модуль создан. content: application/json: schema: $ref: "#/components/schemas/Module" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" get: tags: [Modules] summary: Получить модуль operationId: getModule responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/Module" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [Modules] summary: Обновить модуль description: Частичное обновление (расписание, DoH, приоритет, `enabled` и т.д.). operationId: patchModule parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ModulePatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/Module" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Modules] summary: Удалить модуль description: Мягкое удаление или перевод в `enabled=false` - конкретное поведение задаётся реализацией. operationId: deleteModule parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено или деактивировано. "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/cdn-sources: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" get: tags: [Modules] summary: Список CDN-источников operationId: listCdnSources parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/CdnSource" next_cursor: type: ["string", "null"] has_more: type: boolean "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Modules] summary: Добавить CDN-источник description: URL, `source_kind`, опционально `community_id`. operationId: createCdnSource parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CdnSourceCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/CdnSource" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/cdn-sources/{source_id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" - $ref: "#/components/parameters/SourceId" patch: tags: [Modules] summary: Обновить CDN-источник operationId: patchCdnSource parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CdnSourcePatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/CdnSource" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Modules] summary: Удалить CDN-источник operationId: deleteCdnSource parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено. "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/as-entries: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" get: tags: [Modules] summary: Список AS-записей operationId: listAsEntries parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/AsEntry" next_cursor: type: ["string", "null"] has_more: type: boolean "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Modules] summary: Добавить AS-запись operationId: createAsEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/AsEntryCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/AsEntry" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/as-entries/{entry_id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" - $ref: "#/components/parameters/EntryId" patch: tags: [Modules] summary: Обновить AS-запись operationId: patchAsEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/AsEntryPatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/AsEntry" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Modules] summary: Удалить AS-запись operationId: deleteAsEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено. "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/domain-entries: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" get: tags: [Modules] summary: Список доменных записей description: FQDN и привязка к community. operationId: listDomainEntries parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/DomainEntry" next_cursor: type: ["string", "null"] has_more: type: boolean "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Modules] summary: Добавить доменную запись operationId: createDomainEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DomainEntryCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/DomainEntry" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/domain-entries/{entry_id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" - $ref: "#/components/parameters/EntryId" patch: tags: [Modules] summary: Обновить доменную запись operationId: patchDomainEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DomainEntryCreate" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/DomainEntry" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Modules] summary: Удалить доменную запись operationId: deleteDomainEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено. "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/ip-range-entries: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" get: tags: [Modules] summary: Список IP-диапазонов description: "Только для модулей с типом IP_RANGES (статические CIDR и community_id)." operationId: listIpRangeEntries parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/IpRangeEntry" next_cursor: type: ["string", "null"] has_more: type: boolean "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Modules] summary: Добавить IP-диапазон operationId: createIpRangeEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/IpRangeEntryCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/IpRangeEntry" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/ip-range-entries/{entry_id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" - $ref: "#/components/parameters/EntryId" patch: tags: [Modules] summary: Обновить IP-диапазон operationId: patchIpRangeEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/IpRangeEntryCreate" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/IpRangeEntry" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Modules] summary: Удалить IP-диапазон operationId: deleteIpRangeEntry parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено. "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/refresh: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" post: tags: [Modules] summary: Запустить ingest description: | Запуск обновления данных модуля (CDN / DoH / AS в зависимости от типа). Итоговая ревизия и фрагменты BIRD строятся **по всем включённым модулям тенанта**: обновляемый модуль пересчитывается заново, остальные — по текущему состоянию в БД/источниках, так что префиксы ASN, CDN, IP и т.д. суммируются в одном конфиге. Для `IP_RANGES` данные обычно только в БД: сервер может вернуть **204** (no-op) или **400**, если refresh не поддерживается - поведение фиксируется в реализации. Рекомендуется передавать `Idempotency-Key`. operationId: postModuleRefresh parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "202": description: Задача поставлена в очередь. headers: Location: description: URL задачи `/v1/jobs/{job_id}` schema: type: string content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" "204": description: Нет операции (например модуль `IP_RANGES` и no-op). "400": description: Тип модуля не поддерживает refresh. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/entries.csv: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" get: tags: [Modules] summary: Экспорт записей модуля в CSV description: | Доступно для типов модулей `AS_PREFIXES`, `DOMAINS`, `IP_RANGES`. Возвращает CSV с колонками: - AS: `asn,community` - Домены: `domain,community` - IP ranges: `ipRange,community` operationId: exportModuleEntriesCsv responses: "200": description: CSV-файл записей модуля. content: text/csv: schema: type: string "404": $ref: "#/components/responses/NotFound" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Modules] summary: Импорт записей модуля из CSV description: | Импортирует CSV в модуль типов `AS_PREFIXES`, `DOMAINS`, `IP_RANGES`. Поддерживаемые заголовки: - `asn,community` - `domain,community` - `ipRange,community` В поле `community` можно передавать либо ID community, либо её значение. operationId: importModuleEntriesCsv parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: text/csv: schema: type: string responses: "200": description: Импорт завершён. content: application/json: schema: type: object required: [imported, module_type] properties: imported: type: integer minimum: 0 module_type: type: string "404": $ref: "#/components/responses/NotFound" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/doh-profiles: get: tags: [DoH profiles] summary: Список DoH-профилей operationId: listDohProfiles parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/DohProfile" next_cursor: type: ["string", "null"] has_more: type: boolean default: $ref: "#/components/responses/DefaultProblem" post: tags: [DoH profiles] summary: Создать DoH-профиль description: URL и таймауты; секрет - через vault id или отдельный вызов установки секрета. operationId: createDohProfile parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DohProfileCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/DohProfile" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/modules/{module_id}/cdn-sources/preview: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ModuleId" post: tags: [Modules] summary: Предпросмотр префиксов из CDN-источника description: > Синхронный запрос: conditional GET к URL (до 8 MiB тела ответа), парсинг plaintext или JSON, возврат до 100 префиксов в `items` (полный счётчик в `total`). Выполняется в HTTP worker; при таймауте клиента используйте короткий URL или меньший payload. operationId: previewCdnSource requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CdnPreviewRequest" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/CdnPreviewResponse" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/doh-profiles/{id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/DohProfileId" get: tags: [DoH profiles] summary: Получить DoH-профиль description: Секрет в ответе не возвращается. operationId: getDohProfile responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/DohProfile" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [DoH profiles] summary: Обновить DoH-профиль operationId: patchDohProfile parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DohProfilePatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/DohProfile" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [DoH profiles] summary: Удалить DoH-профиль description: Допустимо только если профиль не используется модулями. operationId: deleteDohProfile parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено. "409": $ref: "#/components/responses/Conflict" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/communities: get: tags: [Communities] summary: Список BGP community operationId: listCommunities parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/BgpCommunity" next_cursor: type: ["string", "null"] has_more: type: boolean default: $ref: "#/components/responses/DefaultProblem" post: tags: [Communities] summary: Создать community operationId: createCommunity parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BgpCommunityCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/BgpCommunity" default: $ref: "#/components/responses/DefaultProblem" /v1/communities/{id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/CommunityId" get: tags: [Communities] summary: Получить community operationId: getCommunity responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/BgpCommunity" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [Communities] summary: Обновить community operationId: patchCommunity parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BgpCommunityPatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/BgpCommunity" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Communities] summary: Удалить community description: Только при отсутствии ссылок из других сущностей. operationId: deleteCommunity parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено. "409": $ref: "#/components/responses/Conflict" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/peers: get: tags: [Peers] summary: Список пиров operationId: listPeers parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/SpeakerFilter" - name: live in: query schema: type: string enum: ["1"] description: > Опрос birdc на CP и GET /v1/agent/bird/protocols на репликах; обогащает session_state и connected_speaker_*. responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/BgpPeer" next_cursor: type: ["string", "null"] has_more: type: boolean live_speaker_poll: type: array description: > При live=1 — результат опроса каждой ноды (CP birdc + agent protocols). items: $ref: "#/components/schemas/LiveSpeakerPoll" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Peers] summary: Создать пира description: > Создаёт BGP-пира и инициирует быстрый reconcile пиров (job `peer_reconcile`) без module ingest. После reconcile автоматически запускается apply на спикеры. operationId: createPeer parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BgpPeerCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/BgpPeer" default: $ref: "#/components/responses/DefaultProblem" /v1/peers/{id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/PeerId" get: tags: [Peers] summary: Получить пира operationId: getPeer responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/BgpPeer" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [Peers] summary: Обновить пира description: > Политики (`policies_json`: `local_ipv4`, `local_ipv6`, `local_asn`), neighbor, ASN, привязка к `bgp_speaker_id` или `null` для всех спикеров. Изменение инициирует быстрый reconcile пиров (job `peer_reconcile`) без module ingest и затем авто-apply на спикеры. operationId: patchPeer parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BgpPeerPatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/BgpPeer" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Peers] summary: Удалить или отключить пира description: > Удаление/отключение инициирует быстрый reconcile пиров (job `peer_reconcile`) без module ingest и затем авто-apply на спикеры. operationId: deletePeer parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено / отключено. "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/bundle/signing-public-key: get: tags: [Bundles] summary: Публичный ключ подписи бандлов description: > Ed25519 public key (base64) для `evobgp-node verify-bundle` / agent sync на реплике. Роль viewer и выше. operationId: getBundleSigningPublicKey responses: "200": description: Ключ для env EVOBGP_BUNDLE_PUBKEY_BASE64 на реплике. content: application/json: schema: $ref: "#/components/schemas/BundleSigningPublicKey" default: $ref: "#/components/responses/DefaultProblem" /v1/speakers: get: tags: [Speakers] summary: Список спикеров description: Master / replica, endpoint и связанные поля. operationId: listSpeakers parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" - name: live in: query schema: type: string enum: ["1"] description: > Live-опрос agent /v1/agent/health и BGP protocols на репликах; CP — local birdc. Обогащает каждый item полем `live`. responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/BgpSpeaker" next_cursor: type: ["string", "null"] has_more: type: boolean default: $ref: "#/components/responses/DefaultProblem" post: tags: [Speakers] summary: Зарегистрировать спикер description: Реплика, canary и т.д. operationId: createSpeaker parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BgpSpeakerCreate" responses: "201": description: Ресурс создан. content: application/json: schema: $ref: "#/components/schemas/BgpSpeaker" default: $ref: "#/components/responses/DefaultProblem" /v1/speakers/{id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/PeerId" get: tags: [Speakers] summary: Получить спикер description: В т.ч. `last_applied_revision_id`. operationId: getSpeaker responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/BgpSpeaker" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [Speakers] summary: Обновить метаданные спикера operationId: patchSpeaker parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/BgpSpeakerPatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/BgpSpeaker" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Speakers] summary: Удалить спикер description: > Удаляет BGP-спикер. Пиры с `bgp_speaker_id` этого спикера остаются, привязка сбрасывается (ON DELETE SET NULL). operationId: deleteSpeaker parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Удалено. "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/revisions: get: tags: [Revisions] summary: История ревизий operationId: listRevisions parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/RevisionModuleFilter" responses: "200": description: Список ревизий с cursor-пагинацией; опциональный фильтр `module_id`. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/ConfigRevision" next_cursor: type: ["string", "null"] has_more: type: boolean default: $ref: "#/components/responses/DefaultProblem" /v1/revisions/prune-estimate: get: tags: [Revisions] summary: Оценка очистки ревизий по retention description: > Считает ревизии и ориентировочный объём данных, которые будут удалены при prune (те же правила, что applyRevisionRetention: последняя ревизия tenant и раскатанные на спикерах сохраняются). operationId: getRevisionPruneEstimate parameters: - $ref: "#/components/parameters/TenantId" - name: retention_minutes in: query required: false schema: type: integer minimum: 15 maximum: 43200 description: TTL в минутах; если не задан — из tenant settings (default 30d). responses: "200": description: Оценка. content: application/json: schema: $ref: "#/components/schemas/RevisionPruneEstimate" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/revisions/prune: post: tags: [Revisions] summary: Очистить старые ревизии (синхронно) description: > Удаляет ревизии старше cutoff по retention и GC неиспользуемых prefix_snapshot. Operator-only. operationId: pruneRevisions parameters: - $ref: "#/components/parameters/TenantId" requestBody: required: false content: application/json: schema: $ref: "#/components/schemas/RevisionPruneRequest" responses: "200": description: Результат очистки. content: application/json: schema: $ref: "#/components/schemas/RevisionPruneResult" "403": $ref: "#/components/responses/Forbidden" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/revisions/{revision_id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/RevisionId" get: tags: [Revisions] summary: Метаданные ревизии description: Хэш, родитель, время, артефакты. operationId: getRevision responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/ConfigRevision" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/revisions/{revision_id}/prefixes: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/RevisionId" get: tags: [Revisions] summary: Снимок префиксов ревизии operationId: getRevisionPrefixes parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/PrefixSnapshotItem" next_cursor: type: ["string", "null"] has_more: type: boolean "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/revisions/{revision_id}/preview: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/RevisionId" get: tags: [Revisions] summary: Превью фрагментов BIRD description: Только чтение, без apply. operationId: getRevisionPreview responses: "200": description: Текст или структурированное представление - формат задаётся реализацией. content: application/json: schema: type: object additionalProperties: true text/plain: schema: type: string "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/revisions/{revision_id}/rollback: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/RevisionId" post: tags: [Revisions] summary: Откат через новую ревизию description: | Создаёт **новую** ревизию с содержимым отката от указанной. Часто возвращает **202** с `Location` на задачу. Рекомендуется `Idempotency-Key`. operationId: postRevisionRollback parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "202": description: Откат поставлен в очередь. headers: Location: schema: type: string content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/revisions/{revision_a}/diff/{revision_b}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/RevisionA" - $ref: "#/components/parameters/RevisionB" get: tags: [Revisions] summary: Сравнить две ревизии description: Формат diff (JSON Patch, табличный и т.д.) определяется реализацией. operationId: getRevisionDiff responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/RevisionDiff" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/apply: post: tags: [Deploy] summary: Применить конфигурацию description: | Применить целевую ревизию на спикерах (политика по умолчанию или из тела). Ответ **202** с `Location` на `/v1/jobs/{job_id}`. **Рекомендуется или обязателен** `Idempotency-Key` по политике продукта. operationId: postApply parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: false content: application/json: schema: $ref: "#/components/schemas/ApplyRequest" responses: "202": description: Запрос принят, задача поставлена в очередь. headers: Location: schema: type: string content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" default: $ref: "#/components/responses/DefaultProblem" /v1/speakers/{id}/apply: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/PeerId" post: tags: [Deploy] summary: Применить на одном спикере description: Canary / точечный deploy. **202** + `Location` на job. operationId: postSpeakerApply parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: false content: application/json: schema: $ref: "#/components/schemas/ApplyRequest" responses: "202": description: Запрос принят, задача поставлена в очередь. headers: Location: schema: type: string content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/bird/status: get: tags: [Deploy] summary: Статус локального BIRD на хосте API description: > Опрос birdc через EVOBGP_BIRDC_SOCKET на процессе API (обычно CP master). На репликах без birdc на CP — birdc_configured=false. operationId: getBirdStatus parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/BirdLocalStatus" default: $ref: "#/components/responses/DefaultProblem" /v1/bird/reload: post: tags: [Deploy] summary: Мягкий reload BIRD description: Опциональный явный reload политики, если отделён от apply; иначе может отсутствовать или быть частью apply. operationId: postBirdReload parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" responses: "202": description: Задача reload поставлена (если реализовано как async). headers: Location: schema: type: string content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" "204": description: Reload выполнен синхронно (если так реализовано). "501": description: Эндпоинт не используется в данной сборке. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" default: $ref: "#/components/responses/DefaultProblem" /v1/jobs: get: tags: [Jobs] summary: Список задач operationId: listJobs parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/JobStatusFilter" - $ref: "#/components/parameters/JobKindFilter" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/Job" next_cursor: type: ["string", "null"] has_more: type: boolean default: $ref: "#/components/responses/DefaultProblem" /v1/jobs/{job_id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/JobId" get: tags: [Jobs] summary: Статус задачи description: Прогресс, ошибка, связанные сущности в `meta`. operationId: getJob responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/Job" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/jobs/{job_id}/cancel: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/JobId" post: tags: [Jobs] summary: Запросить отмену задачи description: Best-effort; гарантии зависят от вида задачи. operationId: postJobCancel parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "202": description: Запрос отмены принят. content: application/json: schema: $ref: "#/components/schemas/Job" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/speakers/{speaker_id}/revisions/latest: parameters: - $ref: "#/components/parameters/SpeakerId" get: tags: [Node] summary: Последняя опубликованная ревизия для ноды description: Вызывается **evobgp-node** с ключом роли `node`. operationId: getSpeakerLatestRevision security: - bearerAuth: [] responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/LatestRevisionPointer" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/speakers/{speaker_id}/bundle/{revision_id}: parameters: - $ref: "#/components/parameters/SpeakerId" - $ref: "#/components/parameters/RevisionId" get: tags: [Node] summary: Скачать подписанный бандл description: | Архив с `manifest.json` и подписью (например Ed25519). Целостность по SHA-256 в манифесте. `Content-Type` - `application/octet-stream` или multipart; детали - в реализации. operationId: getSpeakerBundle security: - bearerAuth: [] responses: "200": description: Бинарное тело бандла. headers: Content-Disposition: schema: type: string content: application/octet-stream: schema: type: string format: binary "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/nodes/enroll: post: tags: [Node] summary: Регистрация ноды description: Обмен ключами и привязка к `speaker_id`; детали протокола уточняются отдельно. operationId: postNodeEnroll security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/NodeEnrollRequest" responses: "200": description: Enrollment записан (node_public_key и время в meta спикера при наличии ключа). content: application/json: schema: $ref: "#/components/schemas/NodeEnrollResponse" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/auth/session: get: tags: [Auth] summary: Текущая сессия API-ключа operationId: getAuthSession parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/AuthSession" "401": $ref: "#/components/responses/Unauthorized" default: $ref: "#/components/responses/DefaultProblem" /v1/api-keys: get: tags: [API keys] summary: Список API-ключей tenant description: Только роль **operator**. Секреты не возвращаются. operationId: listApiKeys parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object required: [items, has_more] properties: items: type: array items: $ref: "#/components/schemas/ApiKey" next_cursor: type: ["string", "null"] has_more: type: boolean "403": $ref: "#/components/responses/Forbidden" default: $ref: "#/components/responses/DefaultProblem" post: tags: [API keys] summary: Создать API-ключ operationId: createApiKey parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ApiKeyCreate" responses: "201": description: Ключ создан; token в ответе один раз. content: application/json: schema: $ref: "#/components/schemas/ApiKeyCreated" "403": $ref: "#/components/responses/Forbidden" "422": $ref: "#/components/responses/UnprocessableEntity" default: $ref: "#/components/responses/DefaultProblem" /v1/api-keys/{id}: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ApiKeyId" get: tags: [API keys] summary: Получить метаданные API-ключа operationId: getApiKey responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/ApiKey" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [API keys] summary: Обновить API-ключ operationId: patchApiKey parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ApiKeyPatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/ApiKey" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [API keys] summary: Отозвать API-ключ operationId: revokeApiKey parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "204": description: Отозван. "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/api-keys/{id}/rotate: parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/ApiKeyId" post: tags: [API keys] summary: Ротировать секрет API-ключа description: Выдаёт новый token; старый перестаёт работать сразу. operationId: rotateApiKey parameters: - $ref: "#/components/parameters/IdempotencyKey" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/ApiKeyCreated" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" default: $ref: "#/components/responses/DefaultProblem" /v1/monitoring/postgres/overview: get: tags: [Monitoring] summary: PostgreSQL overview (instance-level) operationId: getPostgresOverview parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/PostgresOverview" "503": description: PostgreSQL backend не подключён. default: $ref: "#/components/responses/DefaultProblem" /v1/monitoring/postgres/queries: get: tags: [Monitoring] summary: Top queries (pg_stat_statements or snapshot) operationId: getPostgresQueries parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/PostgresQueriesResponse" default: $ref: "#/components/responses/DefaultProblem" /v1/monitoring/postgres/locks: get: tags: [Monitoring] summary: Active locks operationId: getPostgresLocks parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: type: object properties: items: type: array items: type: object additionalProperties: true default: $ref: "#/components/responses/DefaultProblem" /v1/monitoring/postgres/tables: get: tags: [Monitoring] summary: Table sizes and scan stats operationId: getPostgresTables parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object properties: items: type: array items: type: object additionalProperties: true default: $ref: "#/components/responses/DefaultProblem" /v1/monitoring/postgres/recommendations: get: tags: [Monitoring] summary: Heuristic optimization recommendations operationId: getPostgresRecommendations parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/PostgresRecommendations" default: $ref: "#/components/responses/DefaultProblem" /v1/monitoring/correlation: get: tags: [Monitoring] summary: Timeline correlation (jobs vs cache hit) operationId: getMonitoringCorrelation parameters: - $ref: "#/components/parameters/TenantId" - name: window in: query schema: type: integer default: 60 description: Window in minutes (max 1440). responses: "200": description: Успешно. content: application/json: schema: type: object additionalProperties: true default: $ref: "#/components/responses/DefaultProblem" /v1/postgres/vacuum: post: tags: [Monitoring] summary: VACUUM (async job, operator) operationId: postPostgresVacuum parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: content: application/json: schema: $ref: "#/components/schemas/PostgresMaintenanceBody" responses: "202": description: Задача поставлена. content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" "403": $ref: "#/components/responses/Forbidden" default: $ref: "#/components/responses/DefaultProblem" /v1/postgres/vacuum-analyze: post: tags: [Monitoring] summary: VACUUM ANALYZE (async job, operator) operationId: postPostgresVacuumAnalyze parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: content: application/json: schema: $ref: "#/components/schemas/PostgresMaintenanceBody" responses: "202": description: Задача поставлена. content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" default: $ref: "#/components/responses/DefaultProblem" /v1/postgres/analyze: post: tags: [Monitoring] summary: ANALYZE (async job, operator) operationId: postPostgresAnalyze parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: content: application/json: schema: $ref: "#/components/schemas/PostgresMaintenanceBody" responses: "202": description: Задача поставлена. content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" default: $ref: "#/components/responses/DefaultProblem" /v1/postgres/reindex: post: tags: [Monitoring] summary: REINDEX TABLE (async job, operator) operationId: postPostgresReindex parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: content: application/json: schema: $ref: "#/components/schemas/PostgresMaintenanceBody" responses: "202": description: Задача поставлена. content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" default: $ref: "#/components/responses/DefaultProblem" /v1/postgres/cleanup: post: tags: [Monitoring] summary: Retention cleanup (async job, operator) operationId: postPostgresCleanup parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/PostgresMaintenanceBody" responses: "202": description: Задача поставлена. content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" default: $ref: "#/components/responses/DefaultProblem" /v1/postgres/maintenance/logs: get: tags: [Monitoring] summary: Maintenance audit log operationId: listPostgresMaintenanceLogs parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object properties: items: type: array items: type: object additionalProperties: true next_cursor: type: string has_more: type: boolean default: $ref: "#/components/responses/DefaultProblem" /v1/maintenance/policies: get: tags: [Maintenance] summary: List maintenance policies operationId: listMaintenancePolicies parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/MaintenancePolicyList" default: $ref: "#/components/responses/DefaultProblem" post: tags: [Maintenance] summary: Create maintenance policy operationId: createMaintenancePolicy parameters: - $ref: "#/components/parameters/TenantId" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MaintenancePolicy" responses: "201": description: Создано. content: application/json: schema: $ref: "#/components/schemas/MaintenancePolicy" default: $ref: "#/components/responses/DefaultProblem" /v1/maintenance/policies/{id}: get: tags: [Maintenance] summary: Get maintenance policy operationId: getMaintenancePolicy parameters: - $ref: "#/components/parameters/TenantId" - name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/MaintenancePolicy" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [Maintenance] summary: Update maintenance policy operationId: patchMaintenancePolicy parameters: - $ref: "#/components/parameters/TenantId" - name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MaintenancePolicyPatch" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/MaintenancePolicy" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [Maintenance] summary: Delete maintenance policy operationId: deleteMaintenancePolicy parameters: - $ref: "#/components/parameters/TenantId" - name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" responses: "204": description: Удалено. default: $ref: "#/components/responses/DefaultProblem" /v1/maintenance/policies/{id}/hints: get: tags: [Maintenance] summary: PostgreSQL hints for policy table operationId: getMaintenancePolicyHints parameters: - $ref: "#/components/parameters/TenantId" - name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" responses: "200": description: Успешно. content: application/json: schema: type: object additionalProperties: true default: $ref: "#/components/responses/DefaultProblem" /v1/maintenance/config-audit: get: tags: [Maintenance] summary: Maintenance policy configuration audit log operationId: listMaintenanceConfigAudit parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: type: object properties: items: type: array items: type: object additionalProperties: true next_cursor: type: string has_more: type: boolean default: $ref: "#/components/responses/DefaultProblem" /v1/maintenance/run: post: tags: [Maintenance] summary: Run maintenance policy (async job) operationId: postMaintenanceRun parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MaintenanceRunBody" responses: "202": description: Задача поставлена. content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" default: $ref: "#/components/responses/DefaultProblem" /v1/maintenance/dry-run: post: tags: [Maintenance] summary: Dry-run maintenance policy (async job) operationId: postMaintenanceDryRun parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MaintenanceRunBody" responses: "202": description: Задача поставлена. content: application/json: schema: $ref: "#/components/schemas/AsyncJobAccepted" default: $ref: "#/components/responses/DefaultProblem" /v1/runtime-logs/files: get: tags: [RuntimeLogs] summary: Список runtime log-файлов description: | Список *.log в EVOBGP_RUNTIME_LOGS_DIR (размер и mtime). Требуется evobgp-all с примонтированным volume. operationId: listRuntimeLogFiles parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/RuntimeLogFileList" "503": description: Runtime logs недоступны (не evobgp-all или каталог не настроен). content: application/problem+json: schema: $ref: "#/components/schemas/Problem" default: $ref: "#/components/responses/DefaultProblem" /v1/runtime-logs/files/{filename}: get: tags: [RuntimeLogs] summary: Хвост runtime log-файла operationId: getRuntimeLogTail parameters: - $ref: "#/components/parameters/TenantId" - name: filename in: path required: true schema: type: string pattern: '^[a-z0-9][a-z0-9_.-]*\.log$' description: Basename файла (без пути). - name: lines in: query schema: type: integer minimum: 1 maximum: 2000 default: 200 - name: bytes in: query schema: type: integer minimum: 1 maximum: 262144 description: Альтернатива lines; при указании обоих — более строгий лимит. - name: grep in: query schema: type: string maxLength: 128 description: Опциональный подстрочный фильтр (после чтения хвоста). responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/RuntimeLogTail" "404": $ref: "#/components/responses/NotFound" "503": description: Runtime logs недоступны. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" default: $ref: "#/components/responses/DefaultProblem" delete: tags: [RuntimeLogs] summary: Очистить runtime log-файл description: | Синхронная очистка (truncate по умолчанию или delete). Запись в cleanup audit. Максимальный размер файла для очистки — 512 MiB. Только operator+. operationId: deleteRuntimeLogFile parameters: - $ref: "#/components/parameters/TenantId" - name: filename in: path required: true schema: type: string pattern: '^[a-z0-9][a-z0-9_.-]*\.log$' - name: mode in: query schema: $ref: "#/components/schemas/RuntimeLogCleanupMode" description: По умолчанию truncate. responses: "200": description: Файл очищен. content: application/json: schema: $ref: "#/components/schemas/RuntimeLogCleanupResult" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "413": description: Файл превышает лимит 512 MiB. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" "503": description: Runtime logs недоступны. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" default: $ref: "#/components/responses/DefaultProblem" /v1/runtime-logs/auto-estimate: get: tags: [RuntimeLogs] summary: Оценка автоочистки runtime log-файлов description: | Список файлов, которые будут затронуты текущей политикой tenant settings. Требует evobgp-all с примонтированным каталогом runtime-logs. operationId: estimateRuntimeLogAutoCleanup parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/RuntimeLogAutoEstimate" "503": description: FS API недоступен (не evobgp-all или нет volume). content: application/problem+json: schema: $ref: "#/components/schemas/Problem" default: $ref: "#/components/responses/DefaultProblem" /v1/runtime-logs/auto-run: post: tags: [RuntimeLogs] summary: Запустить автоочистку runtime log-файлов description: | Немедленный прогон политики из tenant settings. `dry_run=true` — только оценка без FS-изменений. Записи audit с `actor_prefix=auto:scheduler`. operationId: runRuntimeLogAutoCleanup parameters: - $ref: "#/components/parameters/TenantId" - name: dry_run in: query schema: type: boolean default: false responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/RuntimeLogAutoRunResult" "503": description: FS API недоступен. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" default: $ref: "#/components/responses/DefaultProblem" /v1/runtime-logs/cleanup-audit: get: tags: [RuntimeLogs] summary: Audit очистки runtime log-файлов operationId: listRuntimeLogCleanupAudit parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/Limit" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/RuntimeLogCleanupAuditList" default: $ref: "#/components/responses/DefaultProblem" /v1/settings: get: tags: [Settings] summary: Получить настройки description: KV (лимиты CDN, feature flags и т.д.). operationId: getSettings parameters: - $ref: "#/components/parameters/TenantId" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/Settings" default: $ref: "#/components/responses/DefaultProblem" patch: tags: [Settings] summary: Частично обновить настройки description: Только роль **operator**. operationId: patchSettings parameters: - $ref: "#/components/parameters/TenantId" - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/Settings" responses: "200": description: Успешно. content: application/json: schema: $ref: "#/components/schemas/Settings" "403": $ref: "#/components/responses/Forbidden" default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/install-context: get: tags: [Firewall] summary: Install context for firewall one-liner (operator) operationId: getFirewallInstallContext responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/FirewallInstallContext" "403": $ref: "#/components/responses/Forbidden" default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/enroll: post: tags: [Firewall] summary: Enroll firewall client (public, X-EvoBGP-Seed) security: [] operationId: firewallEnroll parameters: - name: X-EvoBGP-Seed in: header required: true schema: type: string requestBody: required: true content: application/json: schema: type: object required: [name, client_token] properties: name: type: string hostname: type: string client_token: type: string client_version: type: string responses: "201": description: Client created (pending). default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/clients: get: tags: [Firewall] summary: List firewall clients operationId: listFirewallClients responses: "200": description: OK content: application/json: schema: type: object properties: items: type: array items: $ref: "#/components/schemas/FirewallClient" default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/clients/{id}/approve: post: tags: [Firewall] summary: Approve pending client operationId: approveFirewallClient parameters: - name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" responses: "200": description: Approved content: application/json: schema: $ref: "#/components/schemas/FirewallClient" default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/clients/{id}/revoke: post: tags: [Firewall] summary: Reject pending or revoke approved client operationId: revokeFirewallClient parameters: - name: id in: path required: true schema: $ref: "#/components/schemas/ResourceId" responses: "200": description: Revoked content: application/json: schema: type: object properties: status: type: string enum: [revoked] default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/rules: get: tags: [Firewall] summary: List firewall rules operationId: listFirewallRules parameters: - name: scope in: query schema: type: string enum: [tenant, client] - name: client_id in: query schema: $ref: "#/components/schemas/ResourceId" responses: "200": description: OK default: $ref: "#/components/responses/DefaultProblem" post: tags: [Firewall] summary: Create firewall rule operationId: createFirewallRule responses: "201": description: Created default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/blocklist: get: tags: [Firewall] summary: Get evaluated blocklist (firewall client token) operationId: getFirewallBlocklist responses: "200": description: Blocklist content: application/json: schema: $ref: "#/components/schemas/FirewallBlocklist" default: $ref: "#/components/responses/DefaultProblem" /v1/firewall/apply-report: post: tags: [Firewall] summary: Report last apply status operationId: firewallApplyReport requestBody: required: true content: application/json: schema: type: object properties: status: type: string error: type: string prefix_count: type: integer ip_count: type: integer packets_dropped: type: integer format: int64 packets_accepted: type: integer format: int64 kernel_method: type: string source: type: string responses: "200": description: OK default: $ref: "#/components/responses/DefaultProblem"