API Control Plane
HTTP-интерфейс управляющей подсистемы Control Plane
Состояние контракта
| Атрибут | Значение |
|---|---|
| Базовый путь | /control-plane |
| Путь API | /api/v1 |
| OpenAPI | 3.1.2 |
| JSON Schema | 2020-12 |
| Версия контракта | 1.0-r2 |
| Состояние | r2-target-accepted-by-ADR-0002-ADR-0003-ADR-0004 |
| Аутентификация | OIDC/JWT (Bearer) для всех операций |
| Авторизация | Прямая проверка разрешения в Platform AuthZ (SpiceDB), fail-closed |
| Идемпотентность | Заголовок Idempotency-Key для write-операций |
| Контроль ревизий | If-Match (ETag вида "r<revision>") для PATCH/DELETE |
SHA-256 и путь нормативного источника опубликованы в манифесте артефактов.
OpenAPI содержит относительные ссылки на схемы из соседнего каталога
schemas. Используйте опубликованный адрес OpenAPI; для работы без сети
скачайте также все файлы этого каталога из манифеста, сохранив расположение
api/ и schemas/. Один скачанный JSON-файл не включает зависимости.
В опубликованных схемах адреса $id и $ref приведены к локальным
ссылкам; правила проверки данных сохранены. Манифест содержит SHA-256 опубликованного
файла и отдельный sourceSha256 его нормативного источника.
Control Plane — центральный управляющий компонент Платформы ИИ. Он хранит желаемое состояние и управляет длительными операциями, но не участвует в обработке запросов LLM-инференса и в вызовах агентов (A2A). Каталог агентов и их вызов — отдельные API (Agent Registry и Platform Agent Gateway).
Асинхронные операции изменения состояния возвращают
202 Accepted со ссылкой на Operation — это подтверждение принятия
намерения, а не факта выполнения. Фактическое применение отслеживается через
поле verifiedState ресурса и GET /api/v1/operations/{operationId}.
Права доступа
| Право | Назначение |
|---|---|
control_plane#observe | Чтение безопасного статуса, дрейфа и аудита |
control_plane#administer | Управление уровнями, провайдерами, моделями, ключами и политиками |
control_plane#operate | Операции, согласование и восстановление |
control_plane#author_publication | Публикация low-code агентов |
agent_publication#manage | Управление публикацией конкретного агента |
agent#diagnose | Диагностика и статус публикации |
api_key#view / api_key#manage | Самообслуживание ключами текущего субъекта |
api_key#activity_read | Агрегаты и диагностика LLM-запросов конкретного ключа |
agent#activity_aggregate | Агрегаты обращений к агенту и LLM-потребления его ключей |
agent#activity_diagnose | Дополнительно журнал и карточки LLM-запросов ключей агента |
Проверка выполняется прямо на целевом ресурсе. Недоступность или неопределённый результат SpiceDB приводит к отказу (fail-closed), а не к частичному доступу.
Операции
| Метод и путь | Назначение | Право |
|---|---|---|
GET /api/v1/system/status | Безопасный статус областей Control Plane | control_plane#observe |
GET / PATCH /api/v1/key-issuance-policy | Политика выдачи ключей (синглтон) | control_plane#administer |
GET / PATCH /api/v1/agent-publication-policy | Политика публикации агентов (синглтон) | control_plane#administer |
GET / POST /api/v1/tiers | Список и создание уровней | control_plane#administer |
GET / PATCH / DELETE /api/v1/tiers/{tierId} | Операции с уровнем | control_plane#administer |
GET / POST /api/v1/providers | Список и создание провайдеров | control_plane#administer |
GET / PATCH / DELETE /api/v1/providers/{providerId} | Операции с провайдером | control_plane#administer |
PUT /api/v1/providers/{providerId}/credential | Установить учётные данные провайдера | control_plane#administer |
GET / POST /api/v1/models | Список и создание моделей | control_plane#administer |
GET / PATCH / DELETE /api/v1/models/{modelId} | Операции с моделью | control_plane#administer |
GET / POST /api/v1/keys | Список и выдача ключей (админ) | control_plane#administer |
GET / PATCH /api/v1/keys/{keyId} | Метаданные и политика ключа | control_plane#administer |
POST /api/v1/keys/{keyId}:rotate | Ротация ключа | control_plane#administer |
POST /api/v1/keys/{keyId}:revoke | Отзыв ключа | control_plane#administer |
POST /api/v1/bulk/previews | Предпросмотр пакетных изменений | control_plane#administer |
POST /api/v1/bulk/previews/{previewId}:execute | Выполнить предпросмотр | control_plane#administer |
GET /api/v1/me/key-issuance-policy | Политика выдачи для текущего субъекта | Аутентифицированный человек |
GET / POST /api/v1/me/keys | Ключи субъекта и самообслуживающая выдача | api_key#view |
GET /api/v1/me/keys/{keyId} | Свой ключ | api_key#view |
POST /api/v1/me/keys/{keyId}:rotate | Ротация своего ключа | api_key#manage |
POST /api/v1/me/keys/{keyId}:revoke | Отзыв своего ключа | api_key#manage |
GET /api/v1/me/agent-publication-envelope | Обёртка публикации low-code для субъекта | control_plane#author_publication |
POST /api/v1/low-code/artifacts | Загрузка канонического low-code источника | control_plane#author_publication |
GET / POST /api/v1/agents | Список и создание управляемых агентов | agent_publication#manage / control_plane#author_publication |
GET / PATCH /api/v1/agents/{agentId} | Метаданные и владелец агента | agent_publication#manage |
GET / POST /api/v1/agents/{agentId}/versions | Список и создание версий | agent_publication#manage |
GET /api/v1/agents/{agentId}/versions/{versionId} | Версия агента | agent_publication#manage / agent#diagnose |
POST /api/v1/agents/{agentId}:rollback | Откат к версии | agent_publication#manage |
POST /api/v1/agents/{agentId}:unpublish | Скрытие агента | agent_publication#manage |
POST /api/v1/agents/{agentId}:revalidate | Повторная валидация | agent_publication#manage |
GET /api/v1/agents/{agentId}/status | Статус публикации | agent_publication#manage / agent#diagnose |
GET /api/v1/operations | Список операций | control_plane#operate |
GET /api/v1/operations/{operationId} | Статус операции | control_plane#operate / operation#view |
POST /api/v1/operations/{operationId}:cancel | Отмена операции | control_plane#operate / operation#cancel |
GET /api/v1/system/drift | Отчёт о дрейфе | control_plane#observe |
GET /api/v1/audit | Журнал аудита | control_plane#observe |
POST /api/v1/system:reconcile | Согласование по области | control_plane#operate |
POST /api/v1/recovery/previews | Предпросмотр восстановления | control_plane#operate |
POST /api/v1/recovery/previews/{previewId}:execute | Выполнение восстановления | control_plane#operate |
Параметры постраничной навигации: cursor (непрозрачный, привязан к
идентификации и фильтрам) и limit (1–100, по умолчанию 20).
Ответ страницы содержит items и page.nextCursor.
Активность и потребление
Activity API предоставляет агрегаты LLM-потребления, журнал LLM-запросов и
отдельные агрегаты HTTP-обращений к агентам. Все десять операций — безопасные
GET; они не создают объект Operation и не требуют Idempotency-Key.
Полные схемы параметров и ответов находятся в
каноническом OpenAPI выпуска.
Ниже пути указаны относительно /control-plane.
| Метод и путь | Результат |
|---|---|
GET /api/v1/activity/llm/summary | Выбранные метрики за весь интервал |
GET /api/v1/activity/llm/series | Временной ряд одной метрики, при необходимости по разрезам |
GET /api/v1/activity/llm/breakdown | Таблица одного или двух разрезов, общий итог и «Остальные» |
GET /api/v1/activity/llm/requests | Журнал завершённых логических LLM-запросов |
GET /api/v1/activity/llm/requests/{requestId} | Карточка запроса и наблюдаемые попытки |
GET /api/v1/activity/llm/filter-options | Доступные наблюдаемые значения одного разреза |
GET /api/v1/activity/agents/summary | Итоги HTTP-обращений к агентам |
GET /api/v1/activity/agents/series | Временной ряд обращений к агентам |
GET /api/v1/activity/agents/breakdown | Таблица по агентам и их версиям |
GET /api/v1/activity/agents/filter-options | Доступные агенты или версии для фильтра |
Метрики агентов относятся к HTTP-обращению на шлюзе: это не жизненный цикл асинхронной A2A-задачи. Для агентов нет журнала, карточки обращения, разреза вызывающего пользователя или связи «обращение → внутренние LLM-запросы». Инициатор LLM-потребления общего процесса агента неизвестен. Денежной стоимости и остатка накопительной квоты в этих ответах нет.
Область доступа
control_plane#administer разрешает Activity всей установки.
Владелец обычного ключа получает api_key#activity_read: агрегаты, журнал и
карточки своего ключа. Владелец агента получает agent#activity_aggregate:
агрегаты агента и его LLM-ключей. Для журнала этих ключей нужно отдельное
agent#activity_diagnose либо разрешение на конкретный ключ. Права
control_plane#observe, control_plane#operate, просмотра или вызова агента
сами по себе Activity не открывают.
У LLM-агрегатов и вариантов фильтра есть явный выбор
access=aggregate (по умолчанию) или access=diagnostic.
Второй вариант оставляет записи, для которых разрешена диагностика, поэтому
его итог может быть меньше. Журнал и карточка всегда используют диагностику,
Agent API — только агрегаты. ownership=mine ограничивает LLM-выборку ключами,
непосредственно принадлежащими текущему человеку или сервису по паре
issuer + subject; ключи его агентов и «вызовы, сделанные мной» сюда не входят.
Ответ сообщает meta.allowedDimensions, meta.allowedFilters и
meta.detailAccess (full, limited, none). Ключ и его владелец доступны
как разрезы в диагностике, в ownership=mine или администратору установки.
Точные фильтры requestId/traceId также требуют диагностических прав.
Недопустимый разрез возвращает отказ: сервер не сужает область незаметно.
Права проверяются заново перед каждым чтением, включая следующую страницу и известный идентификатор карточки, до агрегирования и выбора лидеров. Отзыв ключа не удаляет историю за срок хранения, но отзыв права закрывает следующее чтение. Недоступность сервиса авторизации возвращает ошибку, без старых результатов.
Параметры выборки и группировки
Неизвестные параметры и повтор скалярного параметра отклоняются.
Массив задаётся повтором имени: modelId=A&modelId=B, без разделения запятыми.
Значения одного фильтра объединяются через «ИЛИ», разные фильтры — через «И».
Идентификаторы ресурсов — канонические UUIDv7.
| Параметр | Где и как используется |
|---|---|
from, to | Пара RFC3339 со смещением, например окончание Z; интервал по началу запроса [from, to), конец не в будущем. Без пары — последние 24 часа |
observation | Непрозрачный токен из предыдущего ответа для связанного чтения с той же границей поступления данных |
access, ownership | Только LLM; область доступа описана выше. Журнал не принимает access, поскольку он всегда диагностический |
agentId, agentVersionId | Повторяемые точные фильтры обеих групп API, до 50 значений каждого |
keyId, modelId, finalProviderId, attemptProviderId | Повторяемые точные фильтры LLM, до 50 значений каждого |
ownerType, ownerIssuer, ownerSubject, ownerAgentId | Один точный владелец LLM-ключа: principal/service вместе с issuer и subject либо agent вместе с идентификатором агента; неполная комбинация недопустима |
outcome, reasonCode | Повторяемые LLM-фильтры. Исход: success, rejected, error, cancelled, unknown; причина — безопасный код из OpenAPI, без произвольного текста ошибки |
requestId, traceId | Точные LLM-фильтры: соответственно 64 и 32 строчных шестнадцатеричных символа; не разрезы группировки |
unknown | Повторяемые имена разрезов с отсутствующим значением; agentId=A&unknown=agentId выбирает A и записи без известного агента |
metric | Только агрегаты. summary без параметра возвращает совместимые метрики; series требует ровно одну, breakdown — хотя бы одну |
groupBy | Повторяемый упорядоченный список: для series 0–2 разных разреза, для breakdown 1–2 |
granularity | Только series: auto, minute, hour, day. auto выбирает минуты до 24 часов включительно, иначе часы |
top, rankBy | series/breakdown: 1–20 лидеров, по умолчанию 10. Ранжирование по выбранной метрике, либо durationMs.mean/p50/p95, ttftMs.p50/p95 |
sort | breakdown: rank или -rank (по умолчанию). Журнал: startedAt, -startedAt (по умолчанию), durationMs, -durationMs |
limit, cursor | Только журнал и варианты фильтров: размер 1–100, по умолчанию 20, и непрозрачный токен следующей страницы |
dimension, q | Только filter-options: обязательный разрез ресурса и необязательная подстрока идентификатора или безопасного названия, 1–256 символов после удаления крайних пробелов, без учёта регистра |
Карточка /llm/requests/{requestId} принимает только необязательный
observation; период, фильтры и размер страницы ей не передают.
Разрезы LLM: keyId, keyOwner, agentId, agentVersionId, modelId,
finalProviderId, attemptProviderId, outcome, reasonCode, с учётом прав.
У Agent API доступны только agentId и agentVersionId.
filter-options принимает только разрезы ресурсов, исключает собственный
фильтр dimension и сохраняет остальные. Недоступные значения и их количество
не раскрываются; это не каталог всех пользователей или ресурсов платформы.
finalProviderId означает провайдера последней попытки и совместим с числом
запросов, исходами и задержками. attemptProviderId относится к наблюдаемым
попыткам и совместим с токенами. Смешивать оба разреза или несовместимые метрики
нельзя. В журнале фильтр attemptProviderId выбирает запросы с наблюдаемой
попыткой этого провайдера: usage остаётся расходом всего запроса, а
matchedAttemptUsage содержит расход совпавших попыток.
Хранится скользящий интервал 30 дней. Истёкшая часть периода вызывает ошибку,
а не незаметное обрезание. Минутные интервалы допустимы только для периода
до 24 часов; максимум — 2000 временных интервалов. Границы считаются в UTC,
краевые интервалы обрезаются по from/to. Лидеры определяются за весь период,
не заново в каждой точке. «Остальные» и общий итог вычисляются по исходной
разрешённой выборке, включая корректный пересчёт процентилей.
Ответ, полнота и страницы
Успешный ответ содержит schemaVersion, meta, data;
schemaVersion равен agent-platform.control-plane.activity/v1.
Ответы имеют Cache-Control: private, no-store.
| Операция | Данные в data |
|---|---|
summary | metrics — выбранные метрики |
series | granularity, total, series; у ряда есть group, isOther и points с from, to, metrics |
breakdown | total, items с group/metrics, other либо null |
requests | items и page.nextCursor, без количества скрытых строк |
| Карточка | request, attempts, attemptsComplete |
filter-options | items с value/label и page.nextCursor |
LLM-метрики: requests, errorRate, rejectionRate, inputTokens,
outputTokens, totalTokens, cachedInputTokens, reasoningTokens,
durationMs, ttftMs. Agent-метрики: agentRequests, httpErrors,
gatewayRejections, durationMs. Доли имеют значения 0–1; длительности —
миллисекунды. requests.byOutcome показывает исходы. В знаменатель долей ошибок
и отклонений входят все выбранные запросы, включая неизвестный исход;
coverage позволяет увидеть число известных наблюдений. Для LLM задержки
по умолчанию считаются по успешным запросам; явный outcome меняет выборку,
а outcomes в метрике сообщает её. Задержки агентов учитывают все наблюдения.
Значение null означает отсутствие измерения, а не ноль. У обычных метрик
есть coverage.known/total, у отдельных токенных метрик дополнительно
coverage.partial. Для totalTokens покрытие разделено на complete,
partial, unknown, conflict, total: известная сумма не обещает полный
расход, если часть запросов измерена не полностью. Конфликтные компоненты
не превращаются в достоверные числа. В пустой выборке суммы запросов и токенов
равны 0, покрытие — 0/0, доли и задержки — null.
Журнал содержит одну строку на логический запрос. Карточка показывает только наблюдаемые попытки, коды исхода, измерения времени, безопасные идентификаторы и числовые данные о расходе токенов; промежуточные попытки не восстанавливаются догадкой. Текст запроса, ответ модели, заголовки, токены доступа, произвольный текст ошибки и внутренний URL провайдера в Activity API не возвращаются.
Для следующей страницы передают page.nextCursor и те же фильтры/сортировку.
Окончание списка — только nextCursor=null: даже пустая страница может иметь
продолжение. Клиенту следует устранять повторные requestId при объединении
страниц. Отзыв доступа и истечение срока хранения учитываются при каждом чтении.
meta.observation действует один час, связывает пользователя, установку,
группу API и asOf — границу поступления данных. Его можно использовать с
другими фильтрами при переходе от графика к журналу и карточке. Он не фиксирует
права и не гарантирует неизменный снимок БД: начавшаяся раньше вставка может
стать видимой позднее. Для обновления данных выполняют новый запрос без токена
и курсора. meta.retentionFrom сообщает текущую границу хранения.
meta.freshness.state — current, delayed или unknown;
checkedAt относится к проверке доставки, lastReceivedAt — к последнему
доступному факту. current не доказывает доставку всей истории.
meta.completeness.state — unknown или known_gap; gaps содержит периоды
проблем доставки с причиной delivery_loss или source_gap. Эти признаки
отдельны от полноты токенов в конкретной метрике.
Ошибки Activity
| HTTP / код | Значение и действие клиента |
|---|---|
400 invalid_request | Исправить параметры, типы или несовместимую комбинацию |
400 activity_metric_dimension_mismatch | Разделить метрики запросов и токенов при анализе провайдеров |
400 invalid_cursor / observation_expired | Начать новое чтение; не подменять фильтры старого курсора |
401 | Требуется действительная аутентификация |
403 activity_dimension_forbidden | Выбрать разрешённый разрез или явно перейти в доступную диагностическую область |
404 not_found | Карточка отсутствует, истекла или недоступна; причины не различаются |
422 activity_range_unavailable | Выбрать период в пределах текущего срока хранения |
422 activity_query_too_large | Укрупнить временные интервалы или сократить период |
422 activity_scope_too_broad | Сузить запрос точным ключом/агентом; сервер не вернул усечённый итог |
429 management_rate_limited | Повторить с учётом Retry-After |
503 authorization_unavailable | Текущие права не удалось проверить; повторить после восстановления |
503 activity_unavailable | Activity, хранилище или необходимые привязки недоступны; повторить после восстановления |
Ошибка не заменяется успешным пустым ответом. Примеры работы через интерфейс — в руководстве пользователя, команды — в руководстве cpctl.
Управление моделями
Три связанных сущности: Tier (профиль скорости и квот), Provider
(внешний LLM-провайдер) и Model (конкретная модель, привязанная к
провайдеру). Все операции, изменяющие состояние, возвращают 202 Accepted.
Чтение ресурса возвращает заголовок ETag вида "r<revision>",
обязательный в If-Match для PATCH/DELETE.
Tier (Уровень)
Профиль обслуживания ключа: лимит запросов в минуту и квота токенов в минуту. Tier не имеет членов, владельцев или унаследованных прав.
| Поле | Тип | Описание |
|---|---|---|
name | string | Уникальное имя, ^[a-z][a-z0-9._-]{0,127}$; неизменяемо |
displayName | string (1–128) | Отображаемое имя (необязательно) |
requestsPerMinute | integer (≥1) | Лимит запросов в минуту |
tokensPerMinute | integer (≥1) | Квота токенов в минуту |
desiredState | enum | enabled / disabled |
Удаление (DELETE) выполняется только для неиспользуемого уровня;
при наличии зависимостей возвращается 409 Conflict. Список
(GET /tiers) поддерживает точный fingerprint
(sha256:…) в пределах аутентифицированного владельца; expand=models
принимается только вместе с fingerprint.
Provider (Поставщик)
Конфигурация внешнего LLM-провайдера. Создание — небезопасное и отключённое:
POST /providers принимает name, kind
(единственное значение openai_compatible), endpoint
(только https://) и desiredState: "disabled".
| Поле | Тип | Описание |
|---|---|---|
kind | const | openai_compatible |
endpoint | uri | HTTPS-адрес; неизменяемо при создании |
credentialStatus | enum | missing, pending, verified, invalid |
credentialFingerprint | string | sha256:…, если учётные данные установлены |
Учётные данные задаются отдельным ресурсом
PUT /providers/{providerId}/credential. Значение поля
credential (16–8192 символов) — writeOnly и
sensitive: API не возвращает его. Провайдер можно включить
(desiredState: "enabled") только после проверки учётных данных.
Список и чтение провайдера не содержат сырых учётных данных. Удаление
(DELETE) возможно только когда на провайдера не ссылаются модели.
Учётные данные провайдера удалить нельзя: их можно только установить или
заменить через PUT /providers/{providerId}/credential.
Model (Модель)
Конкретная модель с именем для шлюза, привязкой к провайдеру и профилем. Наличие модели не создаёт права доступа — доступ выдаётся только явными привязками моделей к ключам.
| Поле | Тип | Описание |
|---|---|---|
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 |
В PATCH входят только upstreamModel, profile
и desiredState: сменить «внутреннее» имя gatewayName или
провайдера нельзя — можно только сменить модель, на которую указывает
upstreamModel. Удаление (DELETE) возможно только когда
модель не требуется активными ключами или маршрутами, иначе 409 Conflict.
Смена состояния модели в масштабе — через пакетные операции
set_model_state (см. «Пакетные операции»).
Управление ключами
ApiKey (Ключ API)
Виртуальный ключ доступа к LLM: один владелец, один Tier, непустой набор разрешённых моделей. Ключ создаётся через защищённую транзакцию — сначала сервис авторизации подтверждает операцию и владельца, затем ключ проверяется Gateway, и только после этого возвращается секретный материал.
| Поле | Тип | Описание |
|---|---|---|
id | UUIDv7 | Стабильный идентификатор ключа |
label | string (1–128) | Читаемое название (необязательно) |
owner | object | principal / service (issuer, subject) или agent (agentId) |
tierId | UUIDv7 | Идентификатор уровня |
modelIds | UUIDv7[] (1–256) | Разрешённые модели, уникальные |
status | enum | pending, active, revoking, revoked, degraded |
generation | integer (≥1) | Номер поколения, инкрементируется при ротации |
fingerprint | string | sha256:… — контроль без раскрытия ключа |
operationId / operationLink | UUIDv7 / path | Операция, создавшая текущую ревизию |
Поведение выдачи и ротации:
- Операция не возвращает
202. Первичный успешный вызов —201сoneTimeSecret(форматcpk_…) иIdempotency-Replayed: false; точная идемпотентная повторная отправка —200только с метаданными иReplayed: true. - Секрет возвращается только один раз. Сырой ключ в базе не хранится —
только хэш
fingerprint. - Ключи с владельцем-агентом (
owner.type = "agent") нельзя обновлять, ротировать или отзывать административными методами — их жизненный цикл принадлежит публикации агента. Попытка приводит к409 Conflictс кодомagent_key_lifecycle_owned_by_publication. - Отзыв (
revoke) проверяется через Gateway; если Gateway не отвечает за отведённое время, возвращается504 VerificationTimeout.
KeyIssuancePolicy (Политика выдачи ключей)
Настраиваемый синглтон, задающий условия самообслуживающейся выдачи ключей. Изменение политики действует только на будущие выдачи и не переписывает ранее выданные ключи.
| Поле | Тип | Описание |
|---|---|---|
enabled | boolean | Включена ли выдача |
allowedTierIds | UUIDv7[] (0–256) | Разрешённые уровни; одно значение фиксирует, несколько дают выбор |
allowedModelIds | UUIDv7[] (0–256) | Точный набор, материализуемый в каждый новый ключ |
maxActiveKeysPerPrincipal | integer (1–100) | Лимит активных ключей на субъекта |
PATCH сливается с текущим синглтоном, затем проверяется итог;
при enabled: true требуются минимум один Tier и одна Model.
Операции: GET /key-issuance-policy — полная политика;
GET /me/key-issuance-policy — сокращённая обёртка для
аутентифицированного человека (allowedTiers, allowedModels,
remainingActiveKeys и т.д.).
Самообслуживание ключами
Операции /api/v1/me/* работают с ключами, принадлежащими самому
аутентифицированному субъекту (человеку или сервису). Владелец, Tier и набор
моделей определяются по удостоверению и политике выдачи; в теле передаётся
только label и, при много-Tier политике, tierId.
| Метод и путь | Назначение | Право |
|---|---|---|
POST /me/keys | Выдать ключ по политике (выдача один раз) | аутентифицированный человек |
GET /me/keys | Список своих ключей | api_key#view |
GET /me/keys/{keyId} | Метаданные своего ключа | api_key#view |
POST /me/keys/{keyId}:rotate | Ротация своего ключа | api_key#manage |
POST /me/keys/{keyId}:revoke | Отзыв своего ключа | api_key#manage |
Отсутствующий или намеренно скрытый ключ возвращает 404
(hidden_or_absent), не раскрывая его существования.
Пакетные операции
POST /bulk/previews принимает до 1000 типизированных изменений —
set_key_policy, revoke_key, set_model_state —
и возвращает ограниченный семантический предпросмотр без вывода провайдера:
список изменений и сводку воздействия (количество затронутых ключей, моделей,
шардов, разрушительных операций). Предпросмотр неизменяем, имеет
requestDigest и срок действия.
POST /bulk/previews/{previewId}:execute выполняет неистёкший
предпросмотр как одну длительную операцию, сверяя requestDigest.
Истёкший предпросмотр — 410; если хоть одна цель — ключ агента,
отказ (409).
Публикация агентов
Управление low-code агентами: создание, версии, откат, скрытие и повторная
валидация. Каталог (чтение карточек, поиск, вызов) — это отдельный API
Agent Registry, здесь ведётся только желаемое состояние публикации.
Жёсткое удаление агента не предусмотрено — вместо него unpublish.
Low-code источник
POST /low-code/artifacts проверяет и устойчиво сохраняет
канонические байты low-code источника и подтверждает связь владения.
Источник LowCodeSource ссылается на Поток:
| Поле | Значение |
|---|---|
adapter | low-code — единственный поддерживаемый адаптер |
flowId / revisionId | UUID потока и ревизии |
revisionNumber | Номер ревизии (≥1) |
uri | S3 или OCI URI (s3://…, oci://…@sha256:…) |
sourceSha256 | [0-9a-f]{64}; проверяется по содержимому uri |
schemaVersion | platform.low-code.flow/v1 |
Источник не содержит секретов, ключей, путей к Kubernetes, токенов и
внутренних адресов. Прямая загрузка в OCI, Kubernetes или Agent Registry
запрещена. Превышение фиксированного R2-лимита — 413; исчерпание
лимита — 429.
Agent (Агент)
Сущность low-code агента со стабильным идентификатором и изменяемыми метаданными. Владелец — ровно один прямой (человек или сервис) или отсутствует.
| Поле | Тип | Описание |
|---|---|---|
id | UUIDv7 | Стабильный идентификатор |
name | string | ^a-z0-9?$ |
displayName | string (1–256) | Отображаемое имя |
description | string (≤4096) | Описание |
owner | DirectOwner | principal или service (необязательно) |
publicationState | enum | active, degraded, suspended, unpublished |
selectedVersionId | UUIDv7 | Выбранная версия |
publicationRevision / revision | integer | Ревизия публикации / ревизия ресурса (ETag) |
POST /agents создаёт агента и начальную неизменяемую версию,
принимая name, displayName, необязательное
description, необязательного owner и initialVersion.
Опущенный owner равен аутентифицированному вызывающему; явная
подстановка владельца допускается только администратором.
PATCH /agents/{agentId} изменяет name,
displayName, description или owner, но
не затрагивает неизменяемые версии и карточку. Смена владельца требует
активного сервиса: Registry атомарно заменяет кортеж владения, а Control
Plane фиксирует квитанцию согласованности до успеха.
AgentVersion (Версия агента)
Неизменяемая версия с SemVer и исходным low-code артефактом. Любое изменение неизменяемых данных создаёт новую версию. Версия замораживает допустимый набор моделей, уровень и ревизию политики публикации.
| Поле | Тип | Описание |
|---|---|---|
id / agentId | UUIDv7 | Идентификатор версии и родительского агента |
semanticVersion | string | Версия в формате SemVer |
protocol | const | a2a |
source | LowCodeSource | Низкоуровневый источник (адаптер low-code) |
artifactDigest | Digest | SHA-256 артефакта после детерминированной OCI-упаковки |
llmRequired | boolean | Выводится из проверенного неизменяемого потока |
modelIds | UUIDv7[] (0–256) | Выводимые разрешения моделей; пусто, только если llmRequired=false |
tierId | UUIDv7 | Уровень из политики публикации; обязателен, если llmRequired=true |
publicationPolicyRevision / publicationPolicyDigest | integer / Digest | Ревизия и дайджест политики на момент создания |
Важно:
llmRequired,modelIdsиtierId— выводимые поля, а не задаваемые. Их вычисляет сервер поAgentPublicationPolicyи low-code источнику; передача в запросе создания отклоняется как400 InvalidRequest.
POST /agents/{agentId}/versions создаёт новую неизменяемую
версию-кандидат и публикует её. Создание версии проверяется против политики
публикации:
выключенная или недоступная политика блокирует создание новых LLM-версий, но
не влияет на уже активные.
AgentPublicationPolicy (Политика пНастраиваемый синглтон, задающий допустимый уровень (tierId) и
набор моделей (не более 256) для новых версий агентов.сий агентов. Замораживается при создании версии; последующие изменения не действуют задним числом.
| Поле | Тип | Описание |
|---|---|---|
enabled | boolean | Включена ли политика |
tierId | UUIDv7 | Уровень по умолчанию; обязателен, если enabled |
allowedModelIds | UUIDv7[] (0–256) | Разрешённые модели; минимум 1, если enabled |
policyDigest | Digest | Дайджест политики |
GET/PATCH /agent-publication-policy — управление
политикой; GET /me/agent-publication-envelope — безопасная
обёртка для конкретного вызывающего (кэшируется с private, no-store).
Команды жизненного цикла
Все команды возвращают 202 Accepted и создают длительную операцию.
| Команда | Метод | Поведение |
|---|---|---|
| Откат | POST /agents/{agentId}:rollback | Выбор ранее проверенной неизменяемой версии (targetAgentVersionId). Создаёт новую ревизию и привязку; предыдущий Service UID и наблюдения Registry не наследуются |
| Скрытие | POST /agents/{agentId}:unpublish | Снимает доступность, сохраняя идентичность и историю. Причины: user_requested, security_response, source_retired, operational |
| Повторная валидация | POST /agents/{agentId}:revalidate | Проходит все гейты заново. Триггеры: scope_restored, operator_recovery, provider_recovered |
| Статус | GET /agents/{agentId}/status | Безопасный желаемый, применённый, проверенный и проекционный статус |
Статус публикации GET /agents/{agentId}/status содержит желаемое,
применённое, проверенное состояние и проекции Registry. В ответ не включаются
исходные байты, сырой ключ, координаты Secret, bearer-токены, учётные данные
провайдера, сырой манифест, внутренние адреса и стек-трейсы.
Агент становится active только после прохождения всех применимых
гейтов; состояния degraded и suspended возникают при
незавершённых или не пройденных гейтах. Подробности о гейтах и наблюдениях
Registry описаны в руководстве администратора Control Plane.
Операции
Каждая изменяющая команда порождает одну длительную операцию (Operation) — устойчивое намерение в PostgreSQL, которое переживает перезапуск и повторяется до завершения или компенсации. Одна операция — одна конечная Workflow.
| Поле | Описание |
|---|---|
operationId | UUIDv7 устойчивого идентификатора |
state | accepted, running, retry_wait, verifying, compensating, succeeded, failed, cancelled |
operationLink | /control-plane/api/v1/operations/{operationId} |
GET /operations фильтруется по state;
GET /operations/{operationId} — статус одной операции. Операция
не может содержать секретный материал. POST /operations/{operationId}:cancel
запрашивает ограниченную отмену; уже применённые эффекты закрываются
компенсацией, а не откатом.
Идемпотентность: заголовок Idempotency-Key (16–128 символов,
^[A-Za-z0-9._~-]+$) обязателен для write-операций. Устойчивое
намерение и выделенные идентификаторы повторно используются при точном
повторе того же ключа. Различный дайджест при том же ключе — 409 Conflict
(idempotency_conflict).
Статус, дрейф и аудит
GET /system/status возвращает безопасный статус трёх областей
(model_policy, key_policy, publication):
состояние, ревизии (желаемая, применённая, проверенная), временная метка.
Состояния: ready, reconciling, degraded,
blocked.
GET /system/drift — классифицированный отчёт о дрейфе. Каждая
запись содержит ресурс (resource.type, resource.id),
область, классификацию (pending_convergence,
unauthorized_external_change, missing_object,
unsupported_provider_state, stale_observation,
blocked_recovery), желаемую и наблюдаемую ревизию и время
обнаружения. Фильтруется по domain.
GET /audit — неизменяемый журнал управления. Записи содержат
исполнителя (человек или сервис), действие, ресурс, результат
(accepted, succeeded, failed,
denied), requestId и опционально operationId.
POST /system:reconcile запрашивает типизированное согласование
по области (observe или repair). Вызывающий не
выбирает инфраструктурные координаты. Согласование может обнаружить
blocked_recovery, но не разрешает его.
Восстановление
POST /recovery/previews сохраняет неизменяемый observe-only
предпросмотр. Сервер связывает типизированную причину с текущим
полным снимком, инвентарём и областью действия; вызывающий не передаёт
ссылки на инфраструктуру.
POST /recovery/previews/{previewId}:execute выполняет точные
решения из неистёкшего предпросмотра. Каждое решение привязано к записи,
наблюдению и ревизии. Разрушительное подтверждение становится
неизменяемым аудит-фактом, привязанным к исполнителю.
Ошибки
Ошибки возвращают единый объект error (схема
error-v1). Сообщение не раскрывает стек, токен, внутренний
адрес, учётные данные и существование запрещённого объекта.
| HTTP | Класс ошибки |
|---|---|
| 400 | Неверная форма или закрытая структура запроса |
| 401 | Отсутствует или недействительно токен управления |
| 403 | Подтверждённому субъекту запрещено функциональное право |
| 404 | Ресурс отсутствует; у вызывающего — отсутствует или намеренно скрыт |
| 409 | Конфликт идемпотентности, зависимостей или несовместимое состояние |
| 410 | Сохранённый предпросмотр истёк и должен быть создан заново |
| 412 | If-Match не называет текущую точную ревизию |
| 413 | Фреймированный запрос или канонический источник превышают фиксированный R2-лимит |
| 429 | Исчерпан лимит частоты, объектов или байт источника |
| 503 | Недоступна авторизация — запрос завершается отказом (fail-closed) |
| 504 | Кандидат ключа не проверен за синхронный бюджет; секрет не возвращается |
Кодовые идентификаторы конфликтов включают, например,
idempotency_conflict и
agent_key_lifecycle_owned_by_publication.
Совместимость
Потребитель фиксирует SHA-256 OpenAPI и проверяет различия перед обновлением.
Несовместимое изменение поля, пути или права требует новой версии API либо
принятого периода перехода с проверками поставщика и потребителя и условием
удаления прежней формы. Версия контракта 1.0-r2; r2
— релиз продукта, v1 — независимый мажорный номер API.