Files
crank/docs/data-model.md
T
2026-04-06 00:40:25 +03:00

373 lines
8.5 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;
- 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`.
## 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`
- `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`
## 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`
## 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`