Files
crank/docs/data-model.md
T
2026-04-06 13:23:45 +03:00

480 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Модель данных
## 1. Назначение документа
Этот документ фиксирует целевую формальную модель данных платформы. Его цель - определить такие структуры, которые можно без существенных изменений перенести в:
- Rust domain types;
- HTTP DTO;
- структуру таблиц БД;
- runtime-представление операций и агентов;
- UI-формы и конфигурационные экраны.
## 2. Общие принципы модели
### 2.1. Одна операция - один интеграционный контракт
Каждая `Operation` соответствует одному интеграционному контракту:
- GraphQL -> один конкретный `query` или `mutation`;
- gRPC -> один unary method или один server-streaming method в bounded execution mode;
- REST -> один endpoint-сценарий.
Однако MCP tool публикуется не напрямую из operation, а через `AgentOperationBinding` внутри конкретного `Agent`.
### 2.2. Внутренний транспортный формат - JSON
Независимо от внешнего протокола внутри системы данные представлены в JSON-ориентированном виде.
### 2.3. Mapping всегда явный
Даже если система умеет строить черновой mapping по примерам данных, итоговая конфигурация mapping сохраняется явно.
### 2.4. JSONPath как единый язык адресации
Для input и output mapping используется `JSONPath`.
### 2.5. Workspace - обязательная граница данных
Все продуктовые сущности принадлежат одному `Workspace`.
Минимальный набор workspace-scoped сущностей:
- `Secret`
- `SecretVersion`
- `Operation`
- `OperationVersion`
- `AuthProfile`
- `Agent`
- `PlatformApiKey`
- `InvocationLog`
- `UsageRollup`
### 2.6. YAML как формат обмена конфигурацией
Помимо канонической JSON-модели система поддерживает импорт и экспорт конфигураций в `YAML`.
### 2.7. Streaming operation обязана быть bounded
Если операция использует upstream streaming, она должна работать в одном из execution modes:
- `window`
- `session`
- `async_job`
Бесконечный passthrough stream не является допустимой моделью `Operation`.
### 2.8. Execution model важнее transport-specific особенностей
Каждая операция в системе определяется двумя измерениями:
- `protocol`
- `execution_mode`
Это позволяет описывать:
- REST unary;
- REST SSE window;
- gRPC server-stream session;
- WebSocket event feed;
- SOAP request-response;
в рамках одной общей модели runtime и MCP publishing.
## 3. Корневые сущности
### 3.1. `Workspace`
Поля:
- `id`
- `slug`
- `display_name`
- `status`
- `settings`
- `created_at`
- `updated_at`
Назначение:
- логическая изоляция команд;
- scoping для операций, агентов, ключей и логов;
- основа для multi-tenant MCP endpoints.
### 3.2. `Operation`
Поля:
- `id`
- `workspace_id`
- `name`
- `display_name`
- `category`
- `protocol`
- `status`
- `version`
- `target`
- `input_schema`
- `output_schema`
- `input_mapping`
- `output_mapping`
- `execution_config`
- `tool_description`
- `samples`
- `generated_draft`
- `config_export`
- `streaming_config`
- `created_at`
- `updated_at`
- `published_at`
### 3.3. `Agent`
`Agent` - пользовательская MCP-поверхность, которая собирает ограниченный набор published operations.
Поля:
- `id`
- `workspace_id`
- `slug`
- `display_name`
- `description`
- `status`
- `current_draft_version`
- `latest_published_version`
- `created_at`
- `updated_at`
- `published_at`
### 3.4. `AgentVersion`
Снимок конфигурации агента.
Поля:
- `agent_id`
- `version`
- `status`
- `instructions`
- `tool_selection_policy`
- `bindings`
- `created_at`
### 3.5. `AgentOperationBinding`
Связь published operation с agent version.
Поля:
- `operation_id`
- `operation_version`
- `tool_name`
- `tool_title`
- `tool_description_override`
- `enabled`
### 3.6. `Secret`
Секрет для доступа к внешней системе.
Поля:
- `id`
- `workspace_id`
- `name`
- `kind`
- `status`
- `current_version`
- `created_at`
- `updated_at`
Значение:
- plaintext не возвращается в list/get endpoints;
- текущее значение хранится в зашифрованном виде через `SecretVersion`;
- rotate создает новую версию секрета без потери ссылочной целостности.
### 3.7. `SecretVersion`
Зашифрованное значение секрета.
Поля:
- `secret_id`
- `version`
- `ciphertext`
- `key_version`
- `created_at`
### 3.8. `AuthProfile`
Используется только для доступа к внешним системам.
Поля:
- `id`
- `workspace_id`
- `name`
- `kind`
- `config`
Принцип:
- `AuthProfile` не хранит plaintext;
- config ссылается на `secret_id` или пару `secret_id`, если auth-схема составная;
- runtime применяет profile к запросу только в момент вызова upstream.
### 3.9. `PlatformApiKey`
Отдельная сущность для доступа к самой платформе.
Поля:
- `id`
- `workspace_id`
- `name`
- `prefix`
- `scopes`
- `status`
- `created_at`
- `last_used_at`
Секрет:
- полный secret показывается только один раз при создании;
- в persistent storage сохраняется только `secret_hash`.
### 3.10. `User`
Поля:
- `id`
- `email`
- `display_name`
- `password_hash`
- `status`
- `created_at`
Пароль:
- хранится только как `Argon2id` hash;
- plaintext пароль не сохраняется;
- верификация использует `password_pepper` из env.
### 3.11. `UserSession`
Поля:
- `id`
- `user_id`
- `secret_hash`
- `status`
- `expires_at`
- `last_seen_at`
- `created_at`
Секрет:
- браузеру выдается только opaque session token;
- в persistent storage сохраняется только `secret_hash`;
- подпись и верификация используют `session_secret` из env.
### 3.12. `Membership`
Поля:
- `workspace_id`
- `user_id`
- `role`
- `created_at`
### 3.13. `InvitationToken`
Поля:
- `id`
- `workspace_id`
- `email`
- `role`
- `status`
- `expires_at`
- `created_at`
Токен:
- полный invite token показывается только один раз при создании;
- в persistent storage сохраняется только `token_hash`.
### 3.14. `InvocationLog`
Продуктовая запись о вызове tool.
Поля:
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `request_id`
- `level`
- `status`
- `duration_ms`
- `error_kind`
- `request_preview`
- `response_preview`
- `created_at`
### 3.15. `UsageRollup`
Агрегированная статистика по периоду.
Поля:
- `workspace_id`
- `agent_id`
- `operation_id`
- `period_kind`
- `period_start`
- `calls_total`
- `calls_ok`
- `calls_error`
- `p50_ms`
- `p95_ms`
- `p99_ms`
### 3.16. `StreamSession`
Поля:
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `mode`
- `status`
- `cursor`
- `state`
- `expires_at`
- `last_poll_at`
- `created_at`
- `closed_at`
Назначение:
- хранение bounded session state для streaming tools;
- поддержка `start/poll/stop`;
- cleanup orphaned и expired sessions.
### 3.17. `AsyncJobHandle`
Поля:
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `status`
- `progress`
- `result`
- `error`
- `expires_at`
- `created_at`
- `updated_at`
- `finished_at`
Назначение:
- поддержка `start/status/result/cancel` для long-running upstream actions.
## 4. `Target`
`Target` описывает конкретный внешний вызов. Это discriminated union по протоколу.
### 4.1. `RestTarget`
- `kind`
- `base_url`
- `method`
- `path_template`
- `static_headers`
### 4.2. `GraphqlTarget`
- `kind`
- `endpoint`
- `operation_type`
- `operation_name`
- `query_template`
- `response_path`
### 4.3. `GrpcTarget`
- `kind`
- `server_addr`
- `package`
- `service`
- `method`
- `descriptor_ref`
- `descriptor_set_b64`
### 4.4. `WebSocketTarget`
- `kind`
- `url`
- `subprotocols`
- `subscribe_message_template`
- `unsubscribe_message_template`
- `static_headers`
### 4.5. `SoapTarget`
- `kind`
- `wsdl_ref`
## 5. `ProtocolOptions`
`ProtocolOptions` хранит protocol-specific runtime tuning, который не должен смешиваться с общей `ExecutionConfig`.
### 5.1. `GrpcProtocolOptions`
- `use_tls`
### 5.2. `WebsocketProtocolOptions`
- `heartbeat_interval_ms`
- `reconnect_max_attempts`
- `reconnect_backoff_ms`
- `service_name`
- `port_name`
- `operation_name`
- `endpoint_override`
- `soap_version`
- `soap_action`
- `header_config`
## 5. `Schema`
`Schema` - нормализованное описание входа или выхода.
Поддерживаются:
- скалярные поля;
- вложенные объекты;
- массивы;
- enum;
- nullable-поля;
- `oneof` для protobuf.
## 6. Принцип совместимости
Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
Для upstream credentials это означает:
- placeholder-строки вида `${secrets.API_KEY}` не считаются реальной моделью данных;
- рабочая продуктовая модель строится только через `Secret` + `AuthProfile`.
Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в:
- `docs/operations-workspace-contracts.md`