# Модель данных ## 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` - `AgentKey` - `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. ### 2.9. Уровень защиты задается операцией Обязательная политика машинного доступа задается на уровне `Operation`, а не на уровне `Agent`. Допустимые значения: - `standard` - `elevated` - `strict` Принцип: - если операция требует более строгий режим доступа, агент не может его ослабить; - один агент может публиковать операции с разной критичностью данных; - `mcp-server` сравнивает тип представленного credential с требуемым `security_level`. ## 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` - `security_level` - `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. `AgentKey` Отдельная сущность для машинного доступа к инструментам одного конкретного AI-агента. Поля: - `id` - `workspace_id` - `agent_id` - `name` - `prefix` - `scopes` - `status` - `created_at` - `last_used_at` - `expires_at` Секрет: - полное значение ключа показывается только один раз при создании; - в persistent storage сохраняется только `secret_hash`; - ключ не должен использоваться как основной рабочий токен вызова в целевой защищенной модели; - ключ используется для выпуска короткоживущего токена доступа. ### 3.10. `IssuedAgentToken` Учет выданных машинных токенов для вызова MCP-инструментов. Поля: - `id` - `workspace_id` - `agent_id` - `agent_key_id` - `platform_client_id` - `token_kind` - `status` - `scopes` - `max_uses` - `used_count` - `cnf_jkt` - `expires_at` - `created_at` - `used_at` Принцип: - токен живет ограниченное время; - токен может быть одноразовым; - токен может быть привязан к ключевой паре клиента; - токен выражает более узкие права, чем длинноживущий ключ агента. ### 3.11. `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` - `read_only` - `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` - `service_name` - `port_name` - `operation_name` - `endpoint_override` - `soap_version` - `soap_action` - `binding_style` - `headers` - `fault_contract` - `metadata` ## 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` ### 5.3. `SoapProtocolOptions` - `use_tls` - `validate_certificate` - `ws_security_profile` ## 6. `ExecutionConfig` `ExecutionConfig` хранит общие runtime-настройки операции. Ключевые поля: - `timeout_ms` - `retry_policy` - `response_cache` - `auth_profile_ref` - `headers` - `protocol_options` - `streaming` ### 6.1. `ResponseCachePolicy` Первый поддержанный вариант response cache policy: - `ttl_ms` Ограничения стартовой реализации: - policy задается явно на операции; - response cache допускается только для read-only вызовов; - текущие поддержанные пути: - `REST GET` - `GraphQL query` - `gRPC unary` только при `GrpcTarget.read_only = true` - все варианты только без `auth_profile_ref` - операции со `streaming` не допускают response cache даже при наличии policy; - cache key должен строиться не глобально, а минимум в контексте: - `workspace` - `agent` - `operation` - `operation version` - `request fingerprint` Это позволяет избежать неявного кэширования ответов, зависящих от upstream credentials. ## 7. XML normalization model Для SOAP/XSD-пайплайна `crank-schema` теперь фиксирует XML-origin metadata отдельными code-level types: - `XmlNodeKind` - `XmlQualifiedName` - `XmlSchemaBinding` Они описывают: - является ли узел element / attribute / text; - локальное имя и namespace; - repeated / nillable semantics. ## 8. `Schema` `Schema` - нормализованное описание входа или выхода. Поддерживаются: - скалярные поля; - вложенные объекты; - массивы; - enum; - nullable-поля; - `oneof` для protobuf. ## 9. Принцип совместимости Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД. Для upstream credentials это означает: - placeholder-строки вида `${secrets.API_KEY}` не считаются реальной моделью данных; - рабочая продуктовая модель строится только через `Secret` + `AuthProfile`. Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в: - `docs/operations-workspace-contracts.md`