# Схема БД

## 1. Назначение документа

Этот документ фиксирует целевую структуру хранения workspace-scoped конфигураций, агентов, ключей доступа и observability-данных. Базовая СУБД - `PostgreSQL`.

## 2. Общие принципы хранения

### 2.1. Версионирование обязательно

Конфигурация operation и agent не хранится только в одной "живой" записи. Каждое существенное изменение создает новую версию.

### 2.2. Published и draft разделяются логически

- `draft` может меняться;
- `published` всегда указывает на конкретную version;
- runtime читает только опубликованные представления.

### 2.3. Workspace scoping обязателен

Все продуктовые таблицы должны ссылаться на `workspaces`.

### 2.4. Артефакты и конфигурация не смешиваются

`.proto`, descriptor set, sample JSON и YAML payload не хранятся в тех же строках, что runtime-ready configuration.

### 2.5. Секреты не хранятся в открытом виде

- upstream secrets живут в отдельных таблицах и шифруются;
- agent keys и credentials доверенных клиентов хранятся как hash;
- короткоживущие токены хранятся в форме, пригодной для отзыва и учета использования.

## 3. Основные таблицы

- `workspaces`
- `users`
- `user_sessions`
- `memberships`
- `invitation_tokens`
- `secrets`
- `secret_versions`
- `operations`
- `operation_versions`
- `published_operations`
- `operation_samples`
- `descriptors`
- `auth_profiles`
- `agents`
- `agent_versions`
- `agent_operation_bindings`
- `published_agents`
- `agent_keys`
- `issued_agent_tokens`
- `platform_client_credentials`
- `stream_sessions`
- `async_jobs`
- `invocation_logs`
- `usage_rollups`
- `yaml_import_jobs`

## 4. Operations

### `operations`

- `id`
- `workspace_id`
- `name`
- `display_name`
- `protocol`
- `status`
- `security_level`
- `current_draft_version`
- `latest_published_version`
- `created_at`
- `updated_at`
- `published_at`

Ограничение:

- `unique (workspace_id, name)`

### `operation_versions`

- `operation_id`
- `version`
- `status`
- `target_json`
- `input_schema_json`
- `output_schema_json`
- `input_mapping_json`
- `output_mapping_json`
- `execution_config_json`
- `tool_description_json`
- `samples_json`
- `generated_draft_json`
- `config_export_json`
- `change_note`
- `created_at`
- `created_by`

### `published_operations`

- `operation_id`
- `version`
- `published_at`
- `published_by`

## 5. Operation artifacts

### `operation_samples`

- `id`
- `operation_id`
- `version`
- `sample_kind`
- `storage_ref`
- `content_type`
- `file_name`
- `created_at`

### `descriptors`

- `id`
- `operation_id`
- `version`
- `descriptor_kind`
- `storage_ref`
- `source_name`
- `package_index_json`
- `created_at`

`descriptor_kind` дополнительно поддерживает:

- `wsdl_upload`
- `xsd_upload`

`package_index_json` для `wsdl_upload` хранит нормализованный inspection result:

- `service_name`
- `port_name`
- `binding_name`
- `endpoint`
- `soap_version`
- `operations[]`

### `yaml_import_jobs`

- `id`
- `source_sample_id`
- `status`
- `format_version`
- `mode`
- `result_operation_id`
- `result_version`
- `error_text`
- `created_at`
- `finished_at`

## 6. Upstream secrets and auth

### `secrets`

- `id`
- `workspace_id`
- `name`
- `kind`
- `status`
- `current_version`
- `created_at`
- `updated_at`

Ограничение:

- `unique (workspace_id, name)`

### `secret_versions`

- `secret_id`
- `version`
- `ciphertext`
- `key_version`
- `created_at`

### `auth_profiles`

- `id`
- `workspace_id`
- `name`
- `kind`
- `config_json`
- `created_at`
- `updated_at`

Назначение:

- `config_json` хранит ссылки на `secret_id`, а не plaintext значения;
- допустимы bearer, basic, api-key-header, api-key-query профили.

Ограничение:

- `unique (workspace_id, name)`

### `agent_keys`

- `id`
- `workspace_id`
- `agent_id`
- `name`
- `prefix`
- `secret_hash`
- `scopes_json`
- `status`
- `expires_at`
- `created_at`
- `last_used_at`

Ограничения:

- `unique (agent_id, name)`
- `prefix` уникален глобально

Назначение:

- длинноживущий ключ принадлежит одному агенту;
- ключ используется как исходное основание для выпуска короткоживущего токена;
- прямой вызов MCP по такому ключу допускается только в переходном режиме.

### `issued_agent_tokens`

- `id`
- `workspace_id`
- `agent_id`
- `agent_key_id`
- `token_kind`
- `status`
- `scopes_json`
- `max_uses`
- `used_count`
- `cnf_jkt`
- `expires_at`
- `created_at`
- `used_at`

Индексы:

- `(agent_id, status, expires_at)`
- `(workspace_id, status, expires_at)`
- `(cnf_jkt)` при включенной привязке токена к ключевой паре клиента

Назначение:

- учет выданных короткоживущих токенов;
- поддержка одноразовых токенов;
- поддержка отзыва и проверки повторного использования.

### `stream_sessions`

- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `mode`
- `status`
- `cursor_json`
- `state_json`
- `expires_at`
- `last_poll_at`
- `created_at`
- `closed_at`

Индексы:

- `(workspace_id, status, expires_at)`
- `(operation_id, status)`

### `async_jobs`

- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `status`
- `progress_json`
- `result_json`
- `error_json`
- `expires_at`
- `created_at`
- `updated_at`
- `finished_at`

Индексы:

- `(workspace_id, status, expires_at)`
- `(operation_id, status)`

## 7. Workspaces and access layer

### `workspaces`

- `id`
- `slug`
- `display_name`
- `status`
- `settings_json`
- `created_at`
- `updated_at`

### `users`

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

### `user_sessions`

- `id`
- `user_id`
- `secret_hash`
- `status`
- `expires_at`
- `last_seen_at`
- `created_at`

Ограничения:

- `unique (user_id, id)`

### `memberships`

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

### `invitation_tokens`

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

## 8. Agents

### `agents`

- `id`
- `workspace_id`
- `slug`
- `display_name`
- `description`
- `status`
- `current_draft_version`
- `latest_published_version`
- `created_at`
- `updated_at`
- `published_at`

Ограничение:

- `unique (workspace_id, slug)`

### `agent_versions`

- `agent_id`
- `version`
- `status`
- `instructions_json`
- `tool_selection_policy_json`
- `created_at`

### `agent_operation_bindings`

- `agent_id`
- `agent_version`
- `operation_id`
- `operation_version`
- `tool_name`
- `tool_title`
- `tool_description_override`
- `enabled`

### `published_agents`

- `agent_id`
- `version`
- `published_at`
- `published_by`

## 9. Machine access and observability

### `agent_keys`

- `id`
- `workspace_id`
- `agent_id`
- `name`
- `prefix`
- `secret_hash`
- `scopes_json`
- `status`
- `expires_at`
- `created_at`
- `last_used_at`

### `issued_agent_tokens`

- `id`
- `workspace_id`
- `agent_id`
- `agent_key_id`
- `token_kind`
- `status`
- `scopes_json`
- `max_uses`
- `used_count`
- `cnf_jkt`
- `expires_at`
- `created_at`
- `used_at`

### `invocation_logs`

- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `source`
- `request_id`
- `level`
- `status`
- `tool_name`
- `message`
- `status_code`
- `duration_ms`
- `error_kind`
- `request_preview_json`
- `response_preview_json`
- `created_at`

### `usage_rollups`

- `workspace_id`
- `agent_id`
- `operation_id`
- `period_kind`
- `period_start`
- `calls_total`
- `calls_ok`
- `calls_error`
- `p50_ms`
- `p95_ms`
- `p99_ms`

Замечание:

- в MVP usage read-model может вычисляться напрямую из `invocation_logs`;
- `usage_rollups` сохраняется как совместимая таблица под materialized aggregates и дальнейшую оптимизацию.

## 10. Migration strategy

Переход от текущей схемы к целевой идет так:

1. добавить `workspaces` и заполнить default workspace;
2. добавить `workspace_id` в `operations` и `auth_profiles`;
3. добавить `secrets` и `secret_versions`;
4. перевести `auth_profiles` на secret-backed config;
5. добавить `agents` и `published_agents`;
6. внедрить `agent_keys`, `platform_client_credentials` и `issued_agent_tokens`;
7. добавить `invocation_logs` и `usage_rollups`;
8. перевести MCP runtime на `published_agents`, а не на глобальный список operations.
