22 KiB
Admin API
Admin API используется веб-интерфейсом Crank. Его можно использовать и напрямую для автоматизации настройки.
Base path:
/api/admin
Auth path:
/api/auth
Авторизация
Администратор входит по email и паролю. После входа сервер устанавливает
HttpOnly session cookie и возвращает csrf_token для browser mutations.
Первый production-admin создаётся через локальный одноразовый bootstrap token:
crank-migrate admin-auth bootstrap-create --email owner@example.com
После этого оператор открывает /login, вводит token и задаёт первый пароль.
Token одноразовый; replay возвращает generic unauthorized без раскрытия причины.
curl -i https://crank.example.com/api/auth/login \
-H 'Content-Type: application/json' \
--data '{
"email": "owner@example.com",
"password": "change-me"
}'
Дальше используйте cookie из ответа:
curl https://crank.example.com/api/auth/session \
-b 'crank_session=<cookie_value>'
Для небезопасных browser/API mutations с session cookie передавайте текущий
csrf_token в заголовке x-csrf-token. Cross-origin /api/* requests
отклоняются по умолчанию.
Endpoints:
GET /api/auth/bootstrap/statusPOST /api/auth/bootstrap/completePOST /api/auth/loginPOST /api/auth/logoutGET /api/auth/sessionGET /api/auth/profilePATCH /api/auth/profilePOST /api/auth/password
Capabilities
curl https://crank.example.com/api/admin/capabilities \
-b 'crank_session=<cookie_value>'
Community capabilities:
- protocol:
rest; - operation security level:
standard; - machine access: static agent API keys.
Workspaces
Community работает с одним workspace.
GET /api/admin/workspacesGET /api/admin/workspaces/{workspace_id}PATCH /api/admin/workspaces/{workspace_id}
Пример:
curl https://crank.example.com/api/admin/workspaces \
-b 'crank_session=<cookie_value>'
Upstreams
Upstream хранит базовый URL внешнего REST API и необязательные статические заголовки.
GET /api/admin/workspaces/{workspace_id}/upstreamsPOST /api/admin/workspaces/{workspace_id}/upstreamsPATCH /api/admin/workspaces/{workspace_id}/upstreams/{upstream_id}
Пример создания:
curl https://crank.example.com/api/admin/workspaces/ws_default/upstreams \
-b 'crank_session=<cookie_value>' \
-H 'Content-Type: application/json' \
--data '{
"name": "Frankfurter",
"base_url": "https://api.frankfurter.dev",
"static_headers": {},
"auth_profile_id": null
}'
Operations
Операция описывает один REST endpoint как MCP-инструмент.
Published Version неизменяема. Изменение после публикации создаёт следующую Draft revision; существующий Agent продолжает использовать exact bound version до явного rebinding и новой публикации Agent. Архивация запрещает новые изменения/привязки, но не удаляет опубликованные snapshots или историю.
GET /api/admin/workspaces/{workspace_id}/operationsPOST /api/admin/workspaces/{workspace_id}/operationsPOST /api/admin/workspaces/{workspace_id}/operations/analyze-qualityGET /api/admin/workspaces/{workspace_id}/operations/{operation_id}PATCH /api/admin/workspaces/{workspace_id}/operations/{operation_id}DELETE /api/admin/workspaces/{workspace_id}/operations/{operation_id}POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versionsGET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versions/{version}POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/publishPOST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/archivePOST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/test-runsGET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/exportPOST /api/admin/workspaces/{workspace_id}/operations/import
Пример тестового запуска:
curl https://crank.example.com/api/admin/workspaces/ws_default/operations/<operation_id>/test-runs \
-b 'crank_session=<cookie_value>' \
-H 'Content-Type: application/json' \
--data '{
"version": 1,
"input": {
"base": "USD",
"quote": "EUR"
}
}'
Успешные поля Test Run сохранены. Каждый элемент errors дополнительно содержит
stable code, stage, retryability, outcome_certainty и безопасные
Request/Trace IDs ответа. При outcome_unknown автоматический повтор запрещён;
пользователь должен сверить результат во внешней системе.
Пример публикации:
# Сначала прочитайте актуальный strong ETag из GET /operations/<operation_id>.
curl https://crank.example.com/api/admin/workspaces/ws_default/operations/<operation_id>/publish \
-b 'crank_session=<cookie_value>' \
-H 'If-Match: "<opaque-operation-etag>"' \
-H 'Content-Type: application/json' \
--data '{ "version": 1 }'
Mutation contract revision 2 требует If-Match для PATCH, create-version, publish, archive, delete и изменяющего существующую Operation YAML upsert. Отсутствующий token возвращает 428 operation_precondition_required, устаревший или относящийся к другому объекту — 409 operation_stale_version. Token повторно проверяется под тем же PostgreSQL row lock, что и mutation. Summary возвращает can_delete, а detail — aggregate availability отдельно от exact Draft/Published version state. Exact version reads имеют отдельный content-stable ETag.
Portable export использует закрытый format_version: "2" contract из schemas/operation-export-v2.schema.json. Он исключает persistence IDs, lifecycle metadata, samples, wizard state и credentials. Legacy v1 принимается только при импорте и нормализуется в v2; exporter v1 не выдаёт. YAML import ограничен 256 KiB и возвращает только bounded codes operation_yaml_too_large|operation_yaml_invalid|operation_yaml_unsupported без raw parser text.
analyze-quality принимает payload операции и возвращает рекомендации:
{
"blocking": false,
"findings": [
{
"severity": "warning",
"code": "tool_description_too_short",
"message": "Описание инструмента слишком короткое.",
"suggested_action": "Добавьте назначение, условия применения и ожидаемый успешный результат.",
"field_path": "tool_description.description"
}
]
}
Samples
POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/input-jsonPOST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/output-jsonPOST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/drafts/generate
Samples используются для генерации схемы, стартового маппинга и сохранения тестовых примеров wizard-а.
Secrets
GET /api/admin/workspaces/{workspace_id}/secretsPOST /api/admin/workspaces/{workspace_id}/secretsGET /api/admin/workspaces/{workspace_id}/secrets/{secret_id}POST /api/admin/workspaces/{workspace_id}/secrets/{secret_id}/rotateDELETE /api/admin/workspaces/{workspace_id}/secrets/{secret_id}
Пример создания token secret:
curl https://crank.example.com/api/admin/workspaces/ws_default/secrets \
-b 'crank_session=<cookie_value>' \
-H 'Content-Type: application/json' \
--data '{
"name": "production-api-token",
"kind": "token",
"value": "secret-token-value"
}'
После создания или ротации API возвращает только metadata. Значение секрета нельзя прочитать повторно.
Если secret используется Auth Profile, удаление отклоняется 409 secret_referenced_by_auth_profile.
Create, rotate, delete и denied-delete пишут bounded audit event с actor,
credential ref, request id и trace id; plaintext, ciphertext и hash в event не
попадают.
Auth profiles
GET /api/admin/workspaces/{workspace_id}/auth-profilesPOST /api/admin/workspaces/{workspace_id}/auth-profilesGET /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}
Auth profile хранит ссылки на secrets и способ применения секрета к REST-запросу. При выполнении Operation текущая версия Secret читается перед dispatch; rotation Secret меняет credential для следующего execution без переписывания Operation или Auth Profile refs.
Agents
GET /api/admin/workspaces/{workspace_id}/agentsPOST /api/admin/workspaces/{workspace_id}/agentsGET /api/admin/workspaces/{workspace_id}/agents/{agent_id}PATCH /api/admin/workspaces/{workspace_id}/agents/{agent_id}DELETE /api/admin/workspaces/{workspace_id}/agents/{agent_id}POST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/versionsGET /api/admin/workspaces/{workspace_id}/agents/{agent_id}/versions/{version}POST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/publishPOST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/unpublishPOST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/archivePOST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/bindingsPOST /api/admin/workspaces/{workspace_id}/agents/tool-search/previewDELETE /api/admin/workspaces/{workspace_id}/agents/{agent_id}/bindings/{operation_id}
Пример создания агента:
curl https://crank.example.com/api/admin/workspaces/ws_default/agents \
-b 'crank_session=<cookie_value>' \
-H 'Content-Type: application/json' \
--data '{
"slug": "currency-rates",
"display_name": "Курсы валют",
"description": "Агент с инструментами для получения курсов валют.",
"instructions": {},
"tool_selection_policy": {}
}'
Привязки и политика доступа сохраняются одной атомарной операцией:
{
"bindings": [
{
"operation_id": "op_01",
"operation_version": 1,
"tool_name": "create_invoice",
"tool_title": "Создать счёт",
"enabled": true
}
],
"tool_selection_policy": {
"mode": "search",
"groups": [
{
"id": "finance",
"name": "Расчёты",
"description": "Счета, платежи и возвраты",
"tool_names": ["create_invoice"]
}
],
"search": { "max_results": 8 }
}
}
Старый формат тела из одного массива привязок поддерживается и сохраняет текущую политику агента. Предварительная проверка принимает те же bindings и tool_selection_policy, а также query и необязательный group_ids.
Agent catalog lifecycle (agent-catalog-lifecycle-v9) защищает опубликованный каталог от in-place mutation:
GET /agents/{agent_id}возвращает strongETag, связанный с workspace, Agent identity, current Draft version, latest Published version, availability иcatalog_revision;- mutation опубликованного Agent (
PATCH,DELETE,bindings,publish,unpublish,archive) требует актуальныйIf-Match; отсутствующий precondition возвращает428 agent_precondition_required, устаревший —409 agent_stale_revision; - Published Agent Version и его bindings immutable на уровне registry/DB; изменение каталога создаёт новый Draft/current version и отдельный publish;
- binding принимает только exact Published Operation Version из того же workspace; draft, archived, missing или foreign Operation Version отклоняются до publication;
catalog_revisionмонотонно растёт при publish/unpublish/archive и используется MCP search/call для защиты от stale search results.
Agent API keys
GET /api/admin/workspaces/{workspace_id}/agents/{agent_id}/platform-api-keysPOST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/platform-api-keysPOST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/platform-api-keys/{key_id}/revokeDELETE /api/admin/workspaces/{workspace_id}/agents/{agent_id}/platform-api-keys/{key_id}
Пример создания ключа:
curl https://crank.example.com/api/admin/workspaces/ws_default/agents/<agent_id>/platform-api-keys \
-b 'crank_session=<cookie_value>' \
-H 'Content-Type: application/json' \
--data '{
"name": "Demo MCP client",
"scopes": ["read", "write"]
}'
Полное значение ключа доступно только в create response.
Дальше API возвращает только metadata: id, name, bounded prefix, key_kind,
scopes, status и timestamps. Hash и raw key никогда не возвращаются.
key_kind = mcp_client используется только для MCP client доступа.
key_kind = approval используется только для approval side-channel. Для approval
keys можно задать allowed_origins; значения должны быть точными
http:///https:// origins без path/query/userinfo. MCP approval запрос с
чужим Origin отклоняется до исполнения side effect.
revoke немедленно прекращает доступ, включая уже существующие MCP session.
DELETE переводит уже revoked key в terminal metadata state deleted, но не
стирает provenance. Credential mutations пишут bounded audit event с actor,
credential ref, request id и trace id; raw key/hash в audit event не попадают.
Create response для mcp_client key дополнительно содержит ephemeral
connection: canonical MCP endpoint и copy-safe конфигурации для
поддерживаемых representative clients. Это единственная граница, где API
возвращает raw secret. При повторном GET, после обновления страницы, revoke
или delete возвращаются только metadata; потерянное значение не восстанавливается.
После неоднозначной ошибки create UI не должен автоматически повторять запрос:
оператор сначала обновляет metadata, затем осознанно создаёт или ротирует key.
Getting Started onboarding
GET /api/admin/workspaces/{workspace_id}/onboardingPOST /api/admin/workspaces/{workspace_id}/onboarding/events
GET доступен только аутентифицированному участнику workspace и строит
ограниченную server-authoritative проекцию: operation, test,
publish_operation, agent, key, mcp_connection, first_call. Browser
не передаёт completed state и не может завершить domain step. Первый read
идемпотентно фиксирует server-owned eligible cohort, а terminal projection после
реального успеха идемпотентно фиксирует completion. Оба времени выдаются в UTC
RFC3339.
Ответ имеет schema_version: 1, opaque revision, status
(in_progress|complete), eligible_since, упорядоченные steps, точные
Operation/Agent/key references, canonical mcp_endpoint и, только после
успешного public tools/call, first_call. Каждый step содержит stable
id, completed, status (pending|current|complete|regressed),
action_code и reason_code. В first_call есть только безопасные
log_id, Agent/key/Operation/version, tool, timestamp, Request ID и Trace ID;
входной payload, raw key и upstream body в snapshot не попадают.
POST /onboarding/events принимает только presentation events
started|resumed|dismissed|abandoned, bounded idempotency_key и текущий
opaque expected_revision. Unknown fields и попытки отправить eligible,
completed, domain steps или client supplied cohort timestamp отклоняются.
Конфликт revision возвращает 409 onboarding_stale_revision с recovery
reload; запрещённый event — 422 onboarding_event_not_allowed. Повтор того
же idempotency key безопасен и не меняет server-derived progress.
Logs и usage
GET /api/admin/workspaces/{workspace_id}/logsGET /api/admin/workspaces/{workspace_id}/logs/{log_id}GET /api/admin/workspaces/{workspace_id}/logs/export.csvGET /api/admin/workspaces/{workspace_id}/usageGET /api/admin/workspaces/{workspace_id}/usage/export.csvGET /api/admin/workspaces/{workspace_id}/usage/operations/{operation_id}GET /api/admin/workspaces/{workspace_id}/usage/agents/{agent_id}GET /api/admin/workspaces/{workspace_id}/approvalsGET /api/admin/workspaces/{workspace_id}/approvals/{approval_id}
Пример:
curl 'https://crank.example.com/api/admin/workspaces/ws_default/logs?limit=20' \
-b 'crank_session=<cookie_value>'
Logs list принимает bounded filters period, created_after,
created_before, level, status, outcome_group, source, operation_id,
agent_id, search, limit и opaque cursor. Explicit
created_after/created_before задают UTC RFC3339 half-open window [start, end) и должны передаваться парой. Ответ имеет форму { "items": [...], "next_cursor": "..." | null }; cursor привязан к детерминированному порядку
created_at desc, id desc и не раскрывает host path или секреты. Log detail
возвращает безопасные preview-поля, request_id, trace_id, execution
taxonomy, точную Operation Version и связанную Operation/Agent metadata только
в рамках текущего workspace.
logs/export.csv применяет те же auth, scope и filters, что и list endpoint.
CSV ограничен по строкам/размеру, использует уже отредактированные previews и
экранирует spreadsheet-formula значения (=, +, -, @, tab, CR/LF в
начале cell). CSV остаётся локальным Admin response; Crank не отправляет usage
наружу.
Usage endpoints используют UTC half-open interval [start, end). Они
принимают либо bounded period, либо explicit RFC3339
created_after/created_before пару. Overview возвращает workspace summary,
timeline, breakdown по Operation/Agent и outcome группы: success, upstream,
client, schema, crank. Эти группы позволяют отличать ошибки внешнего
upstream или пользовательского input от ошибок самого Crank; request_id и
trace_id не используются как labels или aggregation keys. usage/export.csv
создаётся сервером из того же scoped usage dataset, имеет bounded размер,
экранирует spreadsheet-formula значения и не зависит от текущего client-side
snapshot браузера.
Admin API approvals являются read-only operational view. Approve/deny выполняет
MCP approval side-channel с отдельным approval key, потому что этот ключ можно
ограничить конкретным Agent и allowed_origins. Admin list/get показывает
только bounded safe request summary и terminal response payload. Raw request
payload, approval key, auth headers, confirmation/control tokens и secret-like
значения не возвращаются.
Ошибки
Admin API возвращает JSON-ошибки с человекочитаемым сообщением и контекстом, если он доступен.
Частые HTTP-коды:
400- неверный payload;401- нет сессии;403- действие запрещено;404- сущность не найдена;409- конфликт состояния;429- rate limit;500- внутренняя ошибка.