Control Plane
Руководство администратора компонента Control Plane
Назначение и область применения
Настоящее руководство определяет порядок настройки, сопровождения и проверки компонента Control Plane Платформы ИИ в контуре заказчика. Документ предназначен для администратора платформы и предполагает владение основами администрирования Linux, Kubernetes, Helm и HTTP API.
Control Plane — центральный управляющий компонент Платформы ИИ. Он предоставляет versioned management API для управления политиками моделей, виртуальными ключами LLM и публикацией low-code агентов. Компонент владеет desired state (желаемым состоянием) и длительными операциями (durable operations), но не участвует в путях инференса LLM и вызова агентов (A2A invocation).
Platform API Gateway (api-gw) монтирует Control Plane по пути /control-plane: публичное API доступно как /control-plane/api/v1, документация — как /control-plane/docs. Agent Registry смонтирован независимо — под /agent-registry.
Функция «Активность и потребление» добавляет аналитику LLM-запросов и
HTTP-запросов к агентам. Доставка событий, отдельные права, постоянная очередь,
сроки хранения и безопасный откат описаны в
руководстве по эксплуатации активности и потребления.
Её итоговая нагрузочная квалификация AU-I05 ещё не завершена.
Важно: Все изменяющие состояние операции асинхронные. Запрос возвращает 202 Accepted с ссылкой на Operation — это подтверждение принятия намерения, а не факта выполнения. Фактическое применение отслеживается через
verifiedStateресурса иGET /api/v1/operations/{operationId}.
Термины и сокращения
| Термин | Определение |
|---|---|
| Desired state | Желаемое состояние ресурса, зафиксированное в базе данных Control Plane |
| Durable operation (Operation) | Длительная асинхронная операция: фиксируется в БД, переживает перезапуск и повторяется до завершения или компенсации |
| Tier (Уровень) | Профиль качества обслуживания API ключа: лимит запросов в минуту и квота токенов в минуту |
| Provider (Поставщик) | Конфигурация внешнего LLM-провайдера: адаптер, конечная точка, зашифрованные учётные данные |
| Model (Модель) | Конкретная модель с именем для шлюза (gatewayName), привязкой к провайдеру и профилем internal/external |
| ApiKey (Ключ API) | Виртуальный ключ доступа к LLM: один владелец, один Tier, непустой набор моделей |
Verified state (verifiedState) | Проверенное состояние ресурса после применения: pending, enabled, disabled, degraded |
| Publication (Публикация) | Привязка агента к конкретной версии в окружении; владеет состоянием active / degraded / suspended / unpublished |
| Gate (Гейт) | Независимое наблюдаемое условие валидации публикации: pending, passed, failed, not_applicable |
| Registry observation (Наблюдение Registry) | Внешний факт от независимого Agent Registry: candidate_validated, withheld, активная проекция |
ETag / If-Match | Механизм оптимистичного контроля версий: "r<revision>" |
| Idempotency-Key | Заголовок идемпотентности write-операций |
| Reconciler | Фоновый воркер, детерминированно применяющий желаемое состояние во внешних системах |
| Fail-closed | При недоступности сервиса авторизации — отказ, а не частичный доступ |
1. Архитектура
1.1. Общая схема
Control Plane состоит из прикладного API, фоновых воркеров оркестрации и транзакционной базы данных. Управление внешними ресурсами (Kubernetes, LLM Gateway, Agent Registry) выполняется асинхронно через детерминированных reconciler'ов.

1.2. Состав компонентов
| Компонент | Назначение | Примечание |
|---|---|---|
| Control Plane API | Аутентификация, маппинг субъекта, прямая авторизация, валидация, идемпотентность, optimistic concurrency, durable intent | Stateless, масштабируется горизонтально. Не владеет credential'ами Kubernetes и не выполняет проксирование инференса |
| Model reconciler | Детерминированный render/apply Tier/Provider/Model, материализация зашифрованного материала провайдера в immutable Kubernetes Secret | Работает в Temporal namespace control-plane. Владеет AgentgatewayModel.secretRef |
| Key reconciler | Выделение hash-only шардов, render/apply, верификация selected-revision и Gateway probe, жизненный цикл Agent secret | Не создаёт маршруты агентов. Сырой ключ не хранится |
| Publisher worker | Валидация low-code источника, создание Agent/Version/Publication, workload/policy/route, ожидание фидбэка от Registry | Проходит фазы: Accept → Validate source → Identity → Freeze → Candidate credential → Render/apply → Provider gates → Registry gate → Atomic selection → Active projection → Retire previous → Complete |
| Recovery/operator worker | Ограниченный инвентаризационный охват, preview, observe-only классификация и approved continuation | Не угадывает владельца, не выполняет автоматическое принятие |
| PostgreSQL | Desired state, идентичности, назначения, операции, неизменяемый аудит, application-encrypted значения Provider/Agent | Источник истины. Схема control_plane |
| Temporal | Порядок Workflow, таймеры, retry/cancellation, история выполнения | Восстановление после сбоя без потери операций. Одна конечная Workflow = одна асинхронная Operation |
| Platform AuthZ (SpiceDB) | Ресурсно-ориентированная авторизация: control_plane#publish, control_plane#administer, agent#view/#manage/#diagnose | Fail-closed при недоступности |
Kubernetes, LLM Gateway и Agent Registry — наблюдаемые внешние системы. Control Plane не является их синхронной зависимостью для LLM inference или A2A invocation.
1.3. Границы доверия
| Граница | Правило |
|---|---|
| Права управления | Прямые функциональные отношения для канонических субъектов principal/service. Заявления Workspace/Team/Group/Tenant, метки и пространства имён не создают владения, разрешений или квот |
| Результат авторизации | Недействительный или неизвестный результат — fail closed. Частичный результат или кэшированный allow не допускается |
| Конечные точки провайдера | Принимаются только настроенные схемы и разрешённые адреса. Private/link-local/metadata цели и обход через redirect/rebinding отклоняются |
| Публикация агентов | Только ClusterIP-сервисы, default-deny NetworkPolicy, маршрут — только из Publisher. A2A Gateway принимает платформенный OIDC с TTL токена не более 300 секунд |
| Секреты Provider/Agent | AES-256-GCM в PostgreSQL, доставка через immutable Kubernetes Secret. Выбранные вызывающим координаты Secret, ссылки между workload, inline/env-доставка, PVC и постоянные накопители запрещены |
2. Бизнес-сущности
Сущности сгруппированы по логическим областям: управление моделями, управление ключами, публикация агентов. Все объекты создаются Администратором Платформы через management API, за исключением ресурсов самообслуживания (/api/v1/me/*).
2.1. Управление моделями
Tier (Уровень)
Профиль качества обслуживания для каждого API ключа. Определяет лимит скорости (запросы в минуту) и квоту (токены в минуту). Tier не имеет членов, владельцев или унаследованных прав.
| Атрибут | Тип | Описание |
|---|---|---|
id | UUIDv7 | Стабильный идентификатор уровня, генерируемый платформой |
name | string | Уникальное имя. Формат: ^[a-z][a-z0-9._-]{0,127}$. Неизменяем — не входит в схему обновления |
displayName | string (1–128 символов) | Отображаемое имя (необязательный) |
requestsPerMinute | integer (≥1) | Лимит скорости: количество запросов в минуту |
tokensPerMinute | integer (≥1) | Квота: количество токенов в минуту |
desiredState | enum | enabled — уровень активен, disabled — отключён |
verifiedState | enum | pending — ожидает проверки, enabled — проверен и активен, disabled — отключён, degraded — работает с ограничениями |
revision | integer (≥1) | Монотонно возрастающая ревизия. ETag ресурса: "r<revision>" |
createdAt | timestamp | Временная метка создания |
updatedAt | timestamp | Временная метка последнего обновления |
Важные особенности:
- Все мутации (
POST/PATCH/DELETE) асинхронные: возвращают 202 Accepted — это не подтверждённый успех; фактическое применение отслеживается черезverifiedStateи/api/v1/operations. PATCH/DELETEтребуют точныйIf-Match(формат"r<revision>"), иначе 412 PreconditionFailed.- Удаление возможно только для неиспользуемого уровня (проверка зависимостей: ключи, политики), иначе 409 Conflict.
Provider (Поставщик)
Конфигурация внешнего LLM-провайдера (например, OpenAI, Anthropic). Включает адаптер, конечную точку, привязку учётных данных и состояние. Сырые учётные данные хранятся только как зашифрованная строка в базе данных Control Plane; компонент не разворачивает вычислительные ресурсы провайдера.
Замечание: креды провайдера нельзя удалить. Провайдер может быть создан без связанных кредов; добавление кредов —
v1/providers/{providerId}/credential.
Model (Модель)
Конкретная модель с глобально уникальным именем, привязкой к провайдеру и профилем (внутренний или внешний). Наличие модели не подразумевает автоматического права доступа; права выдаются только через явные привязки к API ключам.
| Атрибут | Тип | Описание |
|---|---|---|
id | UUIDv7 | Стабильный идентификатор модели, генерируемый платформой |
gatewayName | string | Имя модели для Gateway. Формат: ^[a-z][a-z0-9._-]{0,127}$. Используется для маршрутизации. Неизменяем |
providerId | UUIDv7 | Идентификатор провайдера, к которому привязана модель |
upstreamModel | string (1–256 символов) | Имя модели у внешнего провайдера (например, gpt-4o) |
profile | enum | internal — внутренняя модель платформы, external — модель внешнего провайдера |
desiredState | enum | enabled — модель активна, disabled — отключена |
verifiedState | enum | pending, enabled, disabled, degraded |
revision | integer (≥1) | Монотонно возрастающая ревизия. ETag: "r<revision>" |
createdAt | timestamp | Временная метка создания |
updatedAt | timestamp | Временная метка последнего обновления |
Важные особенности:
gatewayNameиproviderIdнеизменяемы: в схему обновления входят толькоupstreamModel,profileиdesiredState. Сменить «внутреннее» имя модели нельзя — можно сменить модель, на которую ссылается запись (upstreamModel).- Удаление возможно только когда модель не требуется активными ключами или маршрутами, иначе 409 Conflict.
- Массовое изменение состояния модели поддерживается через bulk-операции (
set_model_stateв/api/v1/bulk/previews→:execute).
Замечание: параметр
displayNameTier может быть пустым, тогда как во многих местах интерфейса отображается не кодовое имя, а человекочитаемое. Рекомендуется всегда заполнятьdisplayNameпри создании уровня.
2.2. Управление ключами
KeyIssuancePolicy (Политика выдачи ключей)
Настройка, определяющая условия самообслуживающейся выдачи ключей для людей и сервисов. Singleton — единичный объект на всю установку. Изменение политики влияет только на будущие выдачи и не изменяет ранее выданные ключи.
| Атрибут | Описание |
|---|---|
enabled | Включена/отключена выдача |
allowedTierIds | Список разрешённых уровней обслуживания |
allowedModelIds | Список разрешённых моделей |
maxActiveKeysPerPrincipal | Ограничение на количество активных ключей на один субъект |
Имеет состояния verifiedState: pending, enabled, disabled, degraded.
Методы:
| Метод | Описание |
|---|---|
GET /api/v1/key-issuance-policy | Получение полной установки политики |
PATCH /api/v1/key-issuance-policy | Обновление политики для будущих выдач. PATCH объединяется с текущим синглтоном; при enabled=true требуются ≥1 Tier и ≥1 Model |
GET /api/v1/me/key-issuance-policy | Сокращённая версия для самообслуживающегося пользователя: enabled, allowedTiers, allowedModels, maxActiveKeys, remainingActiveKeys, policyRevision |
ApiKey (Ключ API)
Виртуальный ключ доступа к LLM. Имеет одного владельца (человек, сервис или агент), привязку к уровню и непустой набор разрешённых моделей. Ключ создаётся через защищённую транзакцию: идентификатор операции подтверждается сервисом авторизации (SpiceDB), а сам ключ верифицируется Gateway перед тем, как платформа вернёт секретный материал (возвращается один раз).
| Атрибут | Тип | Описание |
|---|---|---|
id | UUIDv7 | Стабильный идентификатор ключа |
label | string (1–128 символов) | Читаемое название (необязательный) |
owner | object | Владелец: principal (человек), service (сервис) или agent (агент). Для человека/сервиса — поля type, issuer, subject; для агента — agentId |
tierId | UUIDv7 | Идентификатор уровня |
modelIds | array of UUIDv7 (1–256) | Набор разрешённых моделей. Все уникальны |
status | enum | pending — ожидает верификации Gateway, active — активен, revoking — в процессе отзыва, revoked — отозван, degraded — работает с ограничениями |
generation | integer (≥1) | Номер генерации. Инкрементируется при каждой ротации |
fingerprint | string (SHA-256) | Хэш ключа формата sha256:<64 hex chars>. Используется для проверки без раскрытия самого ключа |
revision | integer | Монотонно возрастающая ревизия |
operationId | UUIDv7 | Идентификатор операции, создавшей или обновившей ревизию |
operationLink | string | Ссылка на ресурс операции: /control-plane/api/v1/operations/{operationId} |
createdAt | timestamp | Временная метка создания |
updatedAt | timestamp | Временная метка последнего обновления |
revokedAt | timestamp (необязательный) | Заполняется только при переходе в статус revoked |
Важные особенности:
- Секретный материал ключа (
oneTimeSecret, форматcpk_<base64url>) возвращается только один раз при создании или ротации (статусы 201/200 с заголовкомIdempotency-Replayed: false). При идемпотентной повторной отправке того жеIdempotency-Keyвозвращается только метаданные. - Ключи, принадлежащие агенту (
owner.type = "agent"), нельзя обновлять, ротировать или отзывать через административные методы — их жизненный цикл управляется публикацией агента (publish/unpublish/rollback/revalidate). Попытка приводит к 409 Conflict с кодомagent_key_lifecycle_owned_by_publication. - При отзыве (
revoke) ключ верифицируется через Gateway — если Gateway не отвечает в течение таймаута, возвращается 504 VerificationTimeout. - Каждая операция создания/ротации/отзыва создаёт
Operation, на которую ссылаетсяoperationLink.
2.3. Публикация агентов
Agent (Агент)
Сущность, представляющая low-code агента. Имеет стабильный идентификатор, выдаваемый издателем, и изменяемые метаданные (имя, отображаемое имя, описание). Может иметь ровно одного прямого владельца (человек или сервис) или не иметь владельца вовсе. Жёсткое удаление агентов не предусмотрено — удаление заменяется unpublish.
| Атрибут | Тип | Описание |
|---|---|---|
id | UUIDv7 | Стабильный идентификатор агента |
name | string | Имя агента. Pattern: ^[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?$ |
displayName | string (1–256 символов) | Отображаемое имя |
description | string (до 4096 символов) | Описание |
owner | DirectOwner | Владелец: PrincipalOwner или ServiceOwner (необязательный) |
publicationState | enum | active, degraded, suspended, unpublished |
selectedVersionId | UUIDv7 | Идентификатор выбранной версии |
publicationRevision | integer (≥1) | Текущая ревизия публикации |
revision | integer (≥1) | Ревизия ресурса для optimistic locking |
createdAt / updatedAt | timestamp | Временные метки создания и обновления |
AgentVersion (Версия агента)
Неизменяемая версия агента с SemVer, исходным артефактом и хэшами политик. Любое изменение неизменяемых данных создаёт новую версию. Версия замораживает допустимый набор моделей, уровень и ревизию политики публикации.
| Атрибут | Тип | Описание |
|---|---|---|
id | UUIDv7 | Уникальный идентификатор версии |
agentId | UUIDv7 | Ссылка на родительский Agent |
semanticVersion | string | Версия в формате SemVer |
protocol | const | Значение: "a2a" |
source | LowCodeSource | Низкоуровневый источник (адаптер low-code) |
artifactDigest | Digest | SHA-256 хеш артефакта: sha256:[0-9a-f]{64} |
llmRequired | boolean | Требуется ли LLM. Выводится из валидированного потока |
modelIds | UUIDv7[] | Модели, используемые версией. 0–256, уникальные. Обязательны, если llmRequired = true |
tierId | UUIDv7 | Тариф, выведенный из AgentPublicationPolicy. Обязателен, если llmRequired = true |
publicationPolicyRevision | integer | Ревизия политики на момент создания |
publicationPolicyDigest | Digest | Дайджест политики на момент создания |
createdAt | timestamp | Время создания |
Важно:
llmRequired,modelIdsиtierId— выводимые, а не задаваемые поля. Их значение определяется сервером на основеAgentPublicationPolicyи low-code источника; передача их в запросе создания отклоняется с ошибкой 400 InvalidRequest.
LowCodeSource — ссылка на низкоуровневый источник агента:
| Поле | Тип | Описание |
|---|---|---|
adapter | const | Значение: "low-code" — единственный поддерживаемый адаптер |
flowId | UUID | Идентификатор потока (flow) |
revisionId | UUID | Идентификатор ревизии потока |
revisionNumber | integer (≥1) | Номер ревизии |
uri | string | S3 или OCI URI: s3://... или oci://...@sha256:... |
sourceSha256 | string | Хеш источника: [0-9a-f]{64}. Проверяется на соответствие содержимому по uri |
schemaVersion | const | Значение: "platform.low-code.flow/v1" |
Источники Source не содержат секретов, ключей, путей к Kubernetes, токенов или внутренних адресов. Прямая загрузка в OCI, Kubernetes или Agent Registry запрещена.
AgentPublicationPolicy (Политика публикации агентов)
Единичный установочный синглтон, задающий допустимый уровень (Tier) и набор моделей (не более 256) для новых версий агентов. Замораживается при создании версии; последующие изменения не действуют задним числом.
| Атрибут | Тип | Описание |
|---|---|---|
schemaVersion | const | "agent-platform.control-plane.agent-publication-policy/v1" |
policyId | UUIDv7 | Идентификатор политики |
enabled | boolean | Включена ли политика |
tierId | UUIDv7 | Тариф по умолчанию для публикаций. Обязателен, если enabled |
allowedModelIds | UUIDv7[] | Разрешённые модели. 0–256, уникальные. Мин. 1, если enabled |
policyDigest | Digest | Дайджест политики |
verifiedState | enum | pending, enabled, disabled, degraded |
revision | integer (≥1) | Ревизия |
Выключенная или недоступная политика блокирует создание новых LLM-версий, но не влияет на уже активные версии.
Publication (Публикация)
Привязка агента к конкретной версии в рамках окружения. Окружение берётся из доверенной конфигурации установки, а не из команды управления.
| Атрибут | Тип | Описание |
|---|---|---|
environment_id | UUID | Идентификатор окружения из доверенной конфигурации |
agent_id | UUIDv7 | Стабильный идентификатор агента |
publication_revision | integer | Монотонно возрастающее число. Инкрементируется при publish, rollback, unpublish или revalidate |
selected_version_id | UUIDv7 | Идентификатор выбранной версии |
desired_state | enum | publish, rollback, unpublish, revalidate |
actual_state | enum | active, degraded, suspended, unpublished |
operation_id | UUIDv7 | Идентификатор связанной задачи публикации |
gate_observations | object | Наблюдения фаз проверки |
timestamps | object | acceptedAt, phaseStartedAt, phaseCompletedAt, terminalAt |
candidate_version_id | UUIDv7 | Версия кандидата при rollback или обновлении |
PublishJob (Задача публикации)
Длительный рабочий процесс жизненного цикла публикации: фазы, наблюдения, повторные попытки, компенсации и терминальный результат. Каждая команда управления создаёт ровно одну задачу с уникальным operation_id.
Фазы publish: accepted → validating_source → allocating_identity → freezing_version → provisioning_candidate_credential → rendering_candidate → applying_candidate → verifying_workload → verifying_route → selecting_candidate → completed
Фазы unpublish: withdrawing_route → awaiting_registry_withheld → revoking_credential → deleting_workload
Состояния публикации:
| Состояние | Описание |
|---|---|
active | Агент активен, выбранная версия валидирована, маршрут настроен |
degraded | Агент работает с ограничениями (не все гейты прошли) |
suspended | Агент приостановлен, недоступен для вызова; требует revalidate |
unpublished | Агент скрыт, маршрут удалён, учётные данные отозваны |
Состояния задачи (Job.state): queued (в очереди), running (выполняется), retry_wait (ожидание повтора), compensating (выполняется компенсация), succeeded, failed, cancelled.
3. Типовые сценарии администратора
3.1. Настройка провайдера и моделей
Роль: Администратор Платформы
| Шаг | Действие | Примечание |
|---|---|---|
| 1 | Создать Provider: указать адаптер (например, openai), конечную точку, привязать зашифрованные учётные данные | Control Plane хранит ключ провайдера зашифрованным; вычислительные ресурсы провайдера не разворачиваются |
| 2 | Создать Tier: задать имя, лимиты requestsPerMinute/tokensPerMinute, установить состояние enabled | Например, standard (100 запросов/мин, 50K токенов/мин) и premium (500 запросов/мин, 200K токенов/мин) |
| 3 | Создать Model: указать уникальное gatewayName, привязать к Provider, задать профиль | Например, gpt-4o → Provider openai, профиль external |
| 4 | Повторить шаги 2–3 для дополнительных уровней и моделей | Каждая модель привязывается к одному провайдеру |
Результат: Платформа готова к работе с подключёнными провайдерами.
3.2. Настройка политики выдачи ключей
Роль: Администратор Платформы
| Шаг | Действие | Примечание |
|---|---|---|
| 1 | PATCH /api/v1/key-issuance-policy | Singleton — редактируется единый объект |
| 2 | Установить enabled: true, выбрать allowedTierIds и allowedModelIds, задать maxActiveKeysPerPrincipal | При enabled=true требуются ≥1 Tier и ≥1 Model |
| 3 | Сохранить | Система проверяет условное ограничение, обновляет ревизию; состояние переходит в verifiedState: enabled |
Результат: Пользователи и сервисы самостоятельно выпускают ключи в рамках заданных ограничений через POST /api/v1/me/keys. Выдача не затрагивает ранее выданные ключи.
3.3. Выдача ключа администратором
Роль: Администратор Платформы
| Шаг | Действие | Примечание |
|---|---|---|
| 1 | Принять запрос субъекта: требуемый Tier, модели, обоснование | Запрос — через тикет-систему или аналогичный канал |
| 2 | POST /api/v1/keys: указать владельца (человек/сервис), Tier и разрешённые модели | Ключ создаётся администратором от имени субъекта |
| 3 | Передать ключ субъекту через безопасный канал | Сырой ключ не хранится в открытом виде и возвращается только один раз при создании |
Результат: Субъект получает ключ; полный аудит-трейл выдачи доступен администратору.
3.4. Управление ключами
Роль: Администратор Платформы
| Действие | Метод | Описание |
|---|---|---|
| Просмотр списка | GET /api/v1/keys | Фильтрация по владельцу (ownerType, ownerIssuer, ownerSubject). Администратор видит все ключи |
| Получение ключа | GET /api/v1/keys/{keyId} | Метаданные без секрета |
| Обновление политики | PATCH /api/v1/keys/{keyId} | Изменение tierId, modelIds, label |
| Ротация ключа | POST /api/v1/keys/{keyId}:rotate | Новая генерация; новый секрет возвращается один раз |
| Отзыв ключа | POST /api/v1/keys/{keyId}:revoke | Перевод в revoked с верификацией через Gateway |
Ключи с владельцем-агентом не управляются через административные методы — их жизненный цикл принадлежит публикации агента.
3.5. Создание и публикация агента
Роль: Пользователь (человек или сервис) с правом control_plane#publish
Публикация инициируется из Low-code IDE: провайдер agent-platform экспортирует проверенную версию потока и вызывает Control Plane через api-gw от имени пользователя. Прямые вызовы Control Plane пользователем не предусмотрены.
| Шаг | Метод | Результат |
|---|---|---|
| 1. Создание агента с начальной версией | POST /api/v1/agents | 202 Accepted → Operation |
| 2. Создание новой версии | POST /api/v1/agents/{agentId}/versions | 202 Accepted → Operation |
| 3. Проверка статуса публикации | GET /api/v1/agents/{agentId}/status | 200 OK → состояние, гейты, наблюдения Registry |
| 4. Откат / скрытие / повторная валидация | POST /api/v1/agents/{agentId}:rollback / :unpublish / :revalidate | 202 Accepted → Operation |
| 5. Обновление метаданных | PATCH /api/v1/agents/{agentId} | 202 Accepted → Operation |
Признаки успешной публикации:
202 Accepted— операция принята, а не завершена;- финальная готовность —
publicationState = activeв статусе публикации; - статус операции в
GET /api/v1/operations/{operationId}—succeeded.
3.6. Настройка политики публикации
Роль: Администратор Платформы
| Шаг | Действие | Примечание |
|---|---|---|
| 1 | PATCH /api/v1/agent-publication-policy: задать tierId и allowedModelIds | Singleton; при enabled=true требуются оба поля |
| 2 | Создание версий агентов проверяется против политики | Версия замораживает modelIds, tierId, ревизию и дайджест политики |
| 3 | Изменения политики не действуют задним числом | Для смены ограничений публикуется новая версия с новым SemVer |
4. Жизненный цикл публикации
4.1. Команды
| Команда | Метод | Поведение |
|---|---|---|
| Publish | POST /api/v1/agents/{agentId}/versions + публикация версии | Новая задача публикации; прохождение всех нормативных фаз; инкремент publication_revision; предыдущая версия атомарно отключается |
| Rollback | POST /api/v1/agents/{agentId}:rollback | Откат к ранее сохранённой неизменяемой версии. Создаёт новую ревизию, новое привязывание учётных данных (если версия требует LLM) и новые неиспользуемые имена объектов провайдера. Предыдущий Service UID, readiness и факты реестра не наследуются |
| Unpublish | POST /api/v1/agents/{agentId}:unpublish | Атомарно фиксирует desired=unpublished и новую ревизию. Строго упорядоченные побочные эффекты: запись намерения отзыва, блокировка маршрута, ожидание Registry withheld, отзыв учётных данных, удаление управляемых ресурсов. После этого автоматический reconcile не вернёт маршрут без новой команды publish |
| Revalidate | POST /api/v1/agents/{agentId}:revalidate | Повторная валидация приостановленного агента: все фазы повторяются с нуля (provenance, новый UID, readiness, credential, периметр Gateway, гейты Registry). Предыдущее наблюдение active не наследуется |
| Статус | GET /api/v1/agents/{agentId}/status | Состояние публикации, ревизия, гейты, наблюдения, временные метки. В ответ не включаются: исходные байты, сырой ключ, координаты Secret, bearer-токены, учётные данные провайдера, сырой манифест, внутренние адреса, стек-трейсы |
Причина unpublish (reasonCode): user_requested, security_response, source_retired, operational.
Триггер revalidate: scope_restored, operator_recovery, provider_recovered.
4.2. Гейты валидации
Статус публикации содержит 15 строго определённых гейтов. Гейт passed означает, что внешняя система (Registry, Gateway, Admission Controller) подтвердила факт, а не просто запись в базе данных. Статусы гейтов: pending, passed, failed, not_applicable. Агент становится active только после прохождения всех применимых гейтов.
| № | Гейт | Назначение |
|---|---|---|
| 1 | sourceValidation | Корректность структуры и схемы LowCodeSource |
| 2 | artifactIntegrity | Артефакт по uri совпадает с sourceSha256 |
| 3 | identityProvenance | Происхождение и аутентичность identity |
| 4 | candidateCredential | Временный ключ кандидатской версии создан и валиден |
| 5 | workloadAdmission | Workload соответствует политикам безопасности и ресурсам |
| 6 | workloadReadiness | Workload готов к обслуживанию |
| 7 | gatewayRoute | Корректность маршрутизации в Gateway |
| 8 | gatewayPerimeter | Периметр доступа (CORS, IP-фильтрация, отсечение токенов) |
| 9 | registryCandidateValidation | Registry зафиксировал кандидата как валидный (candidate_validated) |
| 10 | registryActiveProjection | Registry обновил публичную проекцию активной версии (agent.active.v2) |
| 11 | registryMetadataOwnerProjection | Registry зафиксировал обновление метаданных владельца (metadata-owner-projection.confirmed.v2) |
| 12 | routeWithdrawal | Отключение маршрута (только unpublish) |
| 13 | registryWithheld | Registry скрыл агента из публичного каталога (только unpublish) |
| 14 | credentialRevocation | Отзыв старого ключа (rollback, unpublish, revalidate) |
| 15 | workloadDeletion | Удаление устаревшего workload (только unpublish) |
Состояния degraded и suspended возникают, если один или несколько гейтов failed или pending.
4.3. Наблюдения от Agent Registry
Agent Registry — независимая система; его состояние является источником истины для валидации гейтов публикации.
| Наблюдение | Смысл |
|---|---|
registryObservation.candidate_validated: true | Registry подтвердил валидность кандидатской версии (при publish) |
registryObservation.withheld: true | Registry скрыл текущую версию из каталога (при unpublish) |
activeProjectionObservation | Registry обновил публичную проекцию agent.active.v2: содержит agentId, versionId, publicationRevision, confirmedAt |
metadataOwnerProjectionObservation | Registry атомарно применил обновление метаданных; все поля, включая canonicalDigest, должны точно совпадать с зафиксированным контекстом |
withdrawalIntent | Невозвратный контекст намерения unpublish: reasonCode и targetAgentVersionId |
При отсутствии наблюдения операция ещё не завершена — ожидается ответ Registry.
5. Безопасность
5.1. Хранение секретов
| Секрет | Где хранится | Где используется |
|---|---|---|
| Ключи провайдеров | AES-256-GCM в PostgreSQL | Model reconciler → immutable Kubernetes Secret |
| Учётные данные агентов | AES-256-GCM в PostgreSQL | Publisher worker → Kubernetes Secret |
| Ключ шифрования установки | Отдельный защищённый Kubernetes Secret | Монтируется только в воркерах с правом на учётные данные; недоступен обычным читателям API |
| Виртуальные ключи (человек/сервис) | Генерируются; в БД — только хэш, сырой ключ — один раз вызывающему | Верификация Gateway, одноразовая доставка |
Сырые учётные данные провайдеров никогда не покидают зашифрованное хранилище Control Plane.
Важно: Не помещайте значение токена, сырой ключ, учётные данные провайдера или внутренний адрес в запрос, лог или сообщение об ошибке.
5.2. Авторизация
Модель «ресурс — право» Control Plane:
| Право | Ресурс | Описание |
|---|---|---|
control_plane#publish | control_plane:control-plane | Публикация агентов |
control_plane#administer | control_plane:control-plane | Администрирование (политики, уровни, провайдеры, модели, ключи) |
control_plane#observe | control_plane:control-plane | Наблюдение за статусом |
agent#view | agent:{agentId} | Просмотр агента |
agent#manage | agent:{agentId} | Управление агентом (метаданные, владелец) |
agent#diagnose | agent:{agentId} | Диагностика и статус публикации |
Защитные гейты авторизации:
- Operation requester gate — новая Operation скрыта и не может стартовать до подтверждения
operation:{operationId}#requesterчерез SpiceDB; - Owner transfer gate — при смене владельца Registry атомарно заменяет старый кортеж
agent#owner; - Agent-owned key lifecycle — ключи агента не изменяются через административное API;
- любой недействительный или неизвестный результат авторизации обрабатывается как отказ (fail-closed).
5.3. Идемпотентность и контроль версий
- Заголовок
Idempotency-Key(16–128 символов,^[A-Za-z0-9._~-]+$) обязателен для всех write-операций. - Заголовок
If-Match(ETag вида"r<revision>") обязателен дляPATCHиDELETE. - Повтор с тем же ключом и тем же каноническим дайджестом возвращает оригинальный результат; другой дайджест — 409 Conflict (
idempotency_conflict).
6. Мониторинг и диагностика
6.1. Проверка состояния
# Поды, развёртывания и сервисы
kubectl -n "$CP_NAMESPACE" get deploy,pod,svc
# Журналы API и воркеров
kubectl -n "$CP_NAMESPACE" logs deploy/ap-control-plane-api --all-pods --tail=200
kubectl -n "$CP_NAMESPACE" logs deploy/ap-control-plane-model-reconciler --all-pods --tail=200
kubectl -n "$CP_NAMESPACE" logs deploy/ap-control-plane-key-reconciler --all-pods --tail=200
kubectl -n "$CP_NAMESPACE" logs deploy/ap-control-plane-publisher --all-pods --tail=200
# События
kubectl -n "$CP_NAMESPACE" get events --sort-by=.lastTimestampДиагностику операции: GET /api/v1/operations/{operationId}; диагностику публикации: GET /api/v1/agents/{agentId}/status.
6.2. Типовые нарушения
| Признак | Источник | Вероятная причина и действие |
|---|---|---|
| 503 AuthorizationUnavailable | API | SpiceDB недоступен — fail-closed. Проверить состояние SpiceDB |
| 504 VerificationTimeout | API | Gateway не отвечает при верификации ключа. Проверить доступность LLM Gateway |
| 412 PreconditionFailed | API | If-Match не совпадает. Получить актуальный ETag через GET и повторить |
| 409 Conflict | API | Ресурс занят зависимостями или конфликт версий. Освободить зависимости либо повторить с новым ETag |
Operation зависла в running | Temporal | Воркер упал. Temporal автоматически перезапустит из истории. Проверить логи воркера |
Operation зависла в queued | Temporal | Requester gate не подтверждён через SpiceDB. Проверить разрешения |
publicationState = degraded | Публикация | Не все гейты прошли. Просмотреть gates в статусе публикации |
publicationState = suspended | Публикация | Критический гейт упал. Устранить причину и выполнить revalidate |
| Воркер не создаёт объекты Kubernetes | Воркер | Не хватает прав RBAC, ключ шифрования недоступен или адрес провайдера недоступен. Проверить логи и события |
7. Восстановление
7.1. Цели восстановления
| Компонент | RTO | RPO |
|---|---|---|
| Control Plane API | 30 минут | 5 минут |
| PostgreSQL | 30 минут | 5 минут |
| Temporal namespace | 15 минут | 0 (воспроизведение из истории) |
Сопоставимые цели Agent Registry — RTO 60 минут, RPO 5 минут.
7.2. Восстановление после отказа
- Развернуть ту же версию компонента с прежними секретами и конфигурацией.
- Восстановить PostgreSQL из резервной копии (схема
control_plane). - Проверить доступность Temporal namespace
control-plane. - Запустить API и воркеры; проверить статус операций.
- Проверить публичное API или список ресурсов.
7.3. Крах воркера
Temporal автоматически перезапускает Workflow из истории. Ручное вмешательство не требуется:
- Проверить, что Temporal namespace доступен.
- Проверить логи воркера на повторяющиеся ошибки.
- Если Workflow завис — проверить права RBAC и доступ к внешним системам (Kubernetes, Gateway, Registry).
- Для согласованного ручного продолжения использовать preview → execute Recovery API.
7.4. Recovery API
Для согласованного ручного продолжения после дрейфа или отказа внешнего ресурса:
- Preview — получить ограниченный инвентарь и observe-only классификацию.
- Execute — выполнить согласованное продолжение с подтверждением области воздействия.
Recovery API не угадывает владельца, не выполняет автоматическое принятие и не допускает широкой очистки.
7.5. Миграции базы данных
Задание миграции и проверка при запуске используют транзакционную блокировку PostgreSQL. При ошибке:
- Остановить развёртывание версии.
- Сохранить номер миграции (Alembic) и ошибку базы.
- Вернуть совместимый образ приложения.
- Понижение версии схемы выполнять только после проверки сохранности данных.
- Не запускать разрушительный SQL из интерактивного контейнера.
8. Сводные таблицы API
Базовый путь: /control-plane/api/v1. Аутентификация — Bearer-токен.
Управление моделями и ключами
| Метод | Путь | Право | Описание |
|---|---|---|---|
GET | /tiers | control_plane#administer | Список уровней |
POST | /tiers | control_plane#administer | Создать уровень |
GET | /tiers/{tierId} | control_plane#administer | Получить уровень |
PATCH | /tiers/{tierId} | control_plane#administer | Обновить уровень |
DELETE | /tiers/{tierId} | control_plane#administer | Удалить уровень |
GET | /models | control_plane#administer | Список моделей |
POST | /models | control_plane#administer | Создать модель |
GET | /models/{modelId} | control_plane#administer | Получить модель |
PATCH | /models/{modelId} | control_plane#administer | Обновить модель |
DELETE | /models/{modelId} | control_plane#administer | Удалить модель |
GET | /keys | control_plane#administer | Список всех ключей |
POST | /keys | control_plane#administer | Выдать ключ (администратор) |
GET | /keys/{keyId} | control_plane#administer | Получить ключ |
PATCH | /keys/{keyId} | control_plane#administer | Обновить политику ключа |
POST | /keys/{keyId}:rotate | control_plane#administer | Ротация ключа |
POST | /keys/{keyId}:revoke | control_plane#administer | Отзыв ключа |
GET | /key-issuance-policy | control_plane#administer | Получить политику выдачи |
PATCH | /key-issuance-policy | control_plane#administer | Обновить политику выдачи |
Самообслуживание (me)
| Метод | Путь | Описание |
|---|---|---|
GET | /me/key-issuance-policy | Сокращённая политика выдачи |
GET | /me/keys | Список ключей текущего субъекта |
POST | /me/keys | Выдать ключ (самообслуживание) |
GET | /me/keys/{keyId} | Получить свой ключ |
POST | /me/keys/{keyId}:rotate | Ротация своего ключа |
POST | /me/keys/{keyId}:revoke | Отзыв своего ключа |
Публикация агентов
| Метод | Путь | Право | Описание |
|---|---|---|---|
GET | /agents | control_plane#publish | Список агентов |
POST | /agents | control_plane#publish | Создать агента с начальной версией |
GET | /agents/{agentId} | control_plane#publish / agent#view | Получить агента |
PATCH | /agents/{agentId} | agent#manage | Обновить метаданные/владельца |
GET | /agents/{agentId}/versions | control_plane#publish / agent#diagnose | Список версий |
POST | /agents/{agentId}/versions | control_plane#publish | Создать версию |
GET | /agents/{agentId}/versions/{versionId} | control_plane#publish / agent#diagnose | Получить версию |
POST | /agents/{agentId}:rollback | control_plane#publish | Откат к версии |
POST | /agents/{agentId}:unpublish | control_plane#publish | Скрыть агента |
POST | /agents/{agentId}:revalidate | control_plane#publish | Повторная валидация |
GET | /agents/{agentId}/status | control_plane#publish / agent#diagnose | Статус публикации |
GET | /agent-publication-policy | control_plane#administer | Получить политику публикации |
PATCH | /agent-publication-policy | control_plane#administer | Обновить политику публикации |
Операции
| Метод | Путь | Описание |
|---|---|---|
GET | /operations/{operationId} | Статус операции |
Коды ответов
| Код | Описание |
|---|---|
| 200 | Успешное чтение / идемпотентное воспроизведение |
| 201 | Ключ создан и верифицирован |
| 202 | Асинхронная операция принята |
| 400 | Неверный запрос |
| 401 | Неаутентифицирован |
| 403 | Недостаточно прав |
| 404 | Ресурс не найден |
| 409 | Конфликт: идемпотентность, зависимости или состояние |
| 410 | Предпросмотр истёк |
| 412 | If-Match не совпадает |
| 503 | Сервис авторизации недоступен |
| 504 | Тайм-аут верификации ключа |
Известные ограничения
- Учётные данные провайдера нельзя удалить (провайдер может быть создан без них).
- «Внутреннее» имя модели (
gatewayName) неизменяемо — можно сменить толькоupstreamModel. - Пустое
displayNameуровня отображается пустым в интерфейсе — заполняйте его при создании. - Жёсткое удаление агентов не предусмотрено — используйте
unpublish. - Изменяемые
Flow.dataне публикуются; неизвестный или изменённый компонент отклоняется с ошибкой. - Прямая загрузка в OCI, Kubernetes или Agent Registry запрещена.