# Модель данных

## 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 сущностей:

- `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. `AuthProfile`

Используется только для доступа к внешним системам.

Поля:

- `id`
- `workspace_id`
- `name`
- `kind`
- `config`

### 3.7. `PlatformApiKey`

Отдельная сущность для доступа к самой платформе.

Поля:

- `id`
- `workspace_id`
- `name`
- `prefix`
- `scopes`
- `status`
- `created_at`
- `last_used_at`

Секрет:

- полный secret показывается только один раз при создании;
- в persistent storage сохраняется только `secret_hash`.

### 3.8. `User`

Поля:

- `id`
- `email`
- `display_name`
- `password_hash`
- `status`
- `created_at`

Пароль:

- хранится только как `Argon2id` hash;
- plaintext пароль не сохраняется;
- верификация использует `password_pepper` из env.

### 3.9. `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.10. `Membership`

Поля:

- `workspace_id`
- `user_id`
- `role`
- `created_at`

### 3.11. `InvitationToken`

Поля:

- `id`
- `workspace_id`
- `email`
- `role`
- `status`
- `expires_at`
- `created_at`

Токен:

- полный invite token показывается только один раз при создании;
- в persistent storage сохраняется только `token_hash`.

### 3.12. `InvocationLog`

Продуктовая запись о вызове tool.

Поля:

- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `request_id`
- `level`
- `status`
- `duration_ms`
- `error_kind`
- `request_preview`
- `response_preview`
- `created_at`

### 3.13. `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, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.

Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в:

- `docs/operations-workspace-contracts.md`
