docs: define secret store and auth profile plan
This commit is contained in:
@@ -44,6 +44,7 @@ Crank - платформа для публикации внешних API в в
|
|||||||
- `docs/deployment.md` - деплой, reverse proxy и CI/CD.
|
- `docs/deployment.md` - деплой, reverse proxy и CI/CD.
|
||||||
- `docs/demo-runbook.md` - демонстрационный сценарий.
|
- `docs/demo-runbook.md` - демонстрационный сценарий.
|
||||||
- `docs/public-smoke-targets.md` - готовые публичные upstream-сервисы и payload-ы для smoke-проверки MCP.
|
- `docs/public-smoke-targets.md` - готовые публичные upstream-сервисы и payload-ы для smoke-проверки MCP.
|
||||||
|
- `docs/secrets-auth-plan.md` - целевая модель upstream secrets, auth profiles и пошаговый план реализации.
|
||||||
- `docs/rust-design.md` - правила распределения поведения в Rust.
|
- `docs/rust-design.md` - правила распределения поведения в Rust.
|
||||||
- `docs/development-rules.md` - правила разработки и workflow.
|
- `docs/development-rules.md` - правила разработки и workflow.
|
||||||
- `docs/rust-code-rules.md` - Rust-specific coding rules.
|
- `docs/rust-code-rules.md` - Rust-specific coding rules.
|
||||||
|
|||||||
@@ -2,19 +2,24 @@
|
|||||||
|
|
||||||
## Current
|
## Current
|
||||||
|
|
||||||
### `feat/public-smoke-configs`
|
### `feat/secrets-auth-plan`
|
||||||
|
|
||||||
Status: completed
|
Status: completed
|
||||||
|
|
||||||
DoD:
|
DoD:
|
||||||
- Public REST, GraphQL, and gRPC smoke targets are documented
|
- Docs describe the target secret store and auth profile model
|
||||||
- Repository contains ready-to-use operation payloads for MCP verification
|
- Backend, runtime, and UI gaps are captured as vertical slices
|
||||||
- gRPC smoke config includes a runtime-ready descriptor set
|
- TASKS and implementation plan reflect the new sequence
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
- `feat/manual-regression-pass`
|
- `feat/secret-store-foundation`
|
||||||
|
|
||||||
## Backlog
|
## Backlog
|
||||||
|
|
||||||
|
- `feat/secret-store-foundation`
|
||||||
|
- `feat/auth-profile-secret-resolution`
|
||||||
|
- `feat/runtime-upstream-auth`
|
||||||
|
- `feat/secrets-ui`
|
||||||
|
- `feat/wizard-auth-selector`
|
||||||
- `feat/manual-regression-pass`
|
- `feat/manual-regression-pass`
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
- `memberships`
|
- `memberships`
|
||||||
- `invitations`
|
- `invitations`
|
||||||
- `operations`
|
- `operations`
|
||||||
|
- `secrets`
|
||||||
- `auth-profiles`
|
- `auth-profiles`
|
||||||
- `agents`
|
- `agents`
|
||||||
- `platform-api-keys`
|
- `platform-api-keys`
|
||||||
@@ -116,12 +117,26 @@
|
|||||||
|
|
||||||
### 5.5. Upstream auth profiles
|
### 5.5. Upstream auth profiles
|
||||||
|
|
||||||
|
- `GET /api/admin/workspaces/{workspace_id}/secrets`
|
||||||
|
- `POST /api/admin/workspaces/{workspace_id}/secrets`
|
||||||
|
- `GET /api/admin/workspaces/{workspace_id}/secrets/{secret_id}`
|
||||||
|
- `POST /api/admin/workspaces/{workspace_id}/secrets/{secret_id}/rotate`
|
||||||
|
- `DELETE /api/admin/workspaces/{workspace_id}/secrets/{secret_id}`
|
||||||
|
|
||||||
- `GET /api/admin/workspaces/{workspace_id}/auth-profiles`
|
- `GET /api/admin/workspaces/{workspace_id}/auth-profiles`
|
||||||
- `POST /api/admin/workspaces/{workspace_id}/auth-profiles`
|
- `POST /api/admin/workspaces/{workspace_id}/auth-profiles`
|
||||||
- `GET /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
- `GET /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
||||||
- `PATCH /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
- `PATCH /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
||||||
- `DELETE /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
- `DELETE /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
||||||
|
|
||||||
|
Контракт:
|
||||||
|
|
||||||
|
- `POST /secrets` принимает metadata и plaintext value, но plaintext возвращается только в create/rotate request path и не выдается повторно;
|
||||||
|
- `GET /secrets` и `GET /secrets/{secret_id}` возвращают только metadata, `kind`, `status`, `current_version`, `created_at`, `updated_at`, `last_used_at` при наличии;
|
||||||
|
- `POST /secrets/{secret_id}/rotate` создает новую secret version;
|
||||||
|
- `DELETE /secrets/{secret_id}` запрещен, если secret используется опубликованными auth profiles или operations;
|
||||||
|
- `AuthProfile.config` хранит ссылки на `secret_id`, а не placeholder-строки `${secrets.*}`.
|
||||||
|
|
||||||
### 5.6. Agents
|
### 5.6. Agents
|
||||||
|
|
||||||
- `GET /api/admin/workspaces/{workspace_id}/agents`
|
- `GET /api/admin/workspaces/{workspace_id}/agents`
|
||||||
@@ -188,6 +203,8 @@
|
|||||||
- samples;
|
- samples;
|
||||||
- draft generation;
|
- draft generation;
|
||||||
- gRPC descriptor upload и discovery.
|
- gRPC descriptor upload и discovery.
|
||||||
|
- upstream auth selector;
|
||||||
|
- quick-create secret / auth profile modal.
|
||||||
|
|
||||||
Детальные DTO и response shapes для экранов `Operations` и `Wizard` зафиксированы отдельно в:
|
Детальные DTO и response shapes для экранов `Operations` и `Wizard` зафиксированы отдельно в:
|
||||||
|
|
||||||
@@ -209,6 +226,15 @@
|
|||||||
- list/create/revoke/delete platform API keys;
|
- list/create/revoke/delete platform API keys;
|
||||||
- one-time reveal значения ключа при создании.
|
- one-time reveal значения ключа при создании.
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
|
Нужны:
|
||||||
|
|
||||||
|
- list/create/rotate/delete upstream secrets;
|
||||||
|
- metadata-only retrieval после создания;
|
||||||
|
- связь с auth profiles;
|
||||||
|
- usage references, чтобы оператор видел, где секрет используется.
|
||||||
|
|
||||||
### Logs
|
### Logs
|
||||||
|
|
||||||
Нужны:
|
Нужны:
|
||||||
|
|||||||
+23
-3
@@ -41,6 +41,7 @@ Crank - платформа для публикации внешних API в в
|
|||||||
Изолирует:
|
Изолирует:
|
||||||
|
|
||||||
- операции;
|
- операции;
|
||||||
|
- secrets;
|
||||||
- auth profiles;
|
- auth profiles;
|
||||||
- agents;
|
- agents;
|
||||||
- platform API keys;
|
- platform API keys;
|
||||||
@@ -84,6 +85,21 @@ Crank - платформа для публикации внешних API в в
|
|||||||
- `Invitation`
|
- `Invitation`
|
||||||
- `PlatformApiKey`
|
- `PlatformApiKey`
|
||||||
|
|
||||||
|
### `Upstream secrets`
|
||||||
|
|
||||||
|
Отдельный слой для доступа к внешним системам:
|
||||||
|
|
||||||
|
- `Secret`
|
||||||
|
- `SecretVersion`
|
||||||
|
- `AuthProfile`
|
||||||
|
|
||||||
|
Принцип:
|
||||||
|
|
||||||
|
- секреты принадлежат workspace;
|
||||||
|
- plaintext не хранится в открытом виде;
|
||||||
|
- `AuthProfile` описывает способ применения секрета к upstream request;
|
||||||
|
- runtime резолвит `auth_profile_ref` в реальный header/query/basic auth только в момент вызова.
|
||||||
|
|
||||||
### `Observability`
|
### `Observability`
|
||||||
|
|
||||||
Отдельный продуктовый слой:
|
Отдельный продуктовый слой:
|
||||||
@@ -117,6 +133,7 @@ Crank - платформа для публикации внешних API в в
|
|||||||
- `Agent` и привязка операций к агенту.
|
- `Agent` и привязка операций к агенту.
|
||||||
- Agent-scoped MCP endpoints.
|
- Agent-scoped MCP endpoints.
|
||||||
- Platform API keys.
|
- Platform API keys.
|
||||||
|
- Workspace-scoped encrypted secrets для upstream access.
|
||||||
- Workspace-scoped auth profiles для upstream access.
|
- Workspace-scoped auth profiles для upstream access.
|
||||||
- Product logs и usage aggregates.
|
- Product logs и usage aggregates.
|
||||||
- Импорт и экспорт operation-конфигураций в `YAML`.
|
- Импорт и экспорт operation-конфигураций в `YAML`.
|
||||||
@@ -137,9 +154,10 @@ Crank - платформа для публикации внешних API в в
|
|||||||
|
|
||||||
1. Выбирает workspace.
|
1. Выбирает workspace.
|
||||||
2. Создает или редактирует operation.
|
2. Создает или редактирует operation.
|
||||||
3. Выполняет test run.
|
3. При необходимости выбирает или создает upstream secret / auth profile.
|
||||||
4. Публикует operation version.
|
4. Выполняет test run.
|
||||||
5. Привязывает operation к одному или нескольким agents.
|
5. Публикует operation version.
|
||||||
|
6. Привязывает operation к одному или нескольким agents.
|
||||||
|
|
||||||
### Оператор агентов
|
### Оператор агентов
|
||||||
|
|
||||||
@@ -209,6 +227,8 @@ GraphQL в MCP публикуется как фиксированная опер
|
|||||||
Базовые сущности:
|
Базовые сущности:
|
||||||
|
|
||||||
- `Workspace`
|
- `Workspace`
|
||||||
|
- `Secret`
|
||||||
|
- `SecretVersion`
|
||||||
- `Operation`
|
- `Operation`
|
||||||
- `OperationVersion`
|
- `OperationVersion`
|
||||||
- `Agent`
|
- `Agent`
|
||||||
|
|||||||
+54
-8
@@ -40,6 +40,8 @@
|
|||||||
|
|
||||||
Минимальный набор workspace-scoped сущностей:
|
Минимальный набор workspace-scoped сущностей:
|
||||||
|
|
||||||
|
- `Secret`
|
||||||
|
- `SecretVersion`
|
||||||
- `Operation`
|
- `Operation`
|
||||||
- `OperationVersion`
|
- `OperationVersion`
|
||||||
- `AuthProfile`
|
- `AuthProfile`
|
||||||
@@ -143,7 +145,40 @@
|
|||||||
- `tool_description_override`
|
- `tool_description_override`
|
||||||
- `enabled`
|
- `enabled`
|
||||||
|
|
||||||
### 3.6. `AuthProfile`
|
### 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`
|
||||||
|
|
||||||
Используется только для доступа к внешним системам.
|
Используется только для доступа к внешним системам.
|
||||||
|
|
||||||
@@ -155,7 +190,13 @@
|
|||||||
- `kind`
|
- `kind`
|
||||||
- `config`
|
- `config`
|
||||||
|
|
||||||
### 3.7. `PlatformApiKey`
|
Принцип:
|
||||||
|
|
||||||
|
- `AuthProfile` не хранит plaintext;
|
||||||
|
- config ссылается на `secret_id` или пару `secret_id`, если auth-схема составная;
|
||||||
|
- runtime применяет profile к запросу только в момент вызова upstream.
|
||||||
|
|
||||||
|
### 3.9. `PlatformApiKey`
|
||||||
|
|
||||||
Отдельная сущность для доступа к самой платформе.
|
Отдельная сущность для доступа к самой платформе.
|
||||||
|
|
||||||
@@ -175,7 +216,7 @@
|
|||||||
- полный secret показывается только один раз при создании;
|
- полный secret показывается только один раз при создании;
|
||||||
- в persistent storage сохраняется только `secret_hash`.
|
- в persistent storage сохраняется только `secret_hash`.
|
||||||
|
|
||||||
### 3.8. `User`
|
### 3.10. `User`
|
||||||
|
|
||||||
Поля:
|
Поля:
|
||||||
|
|
||||||
@@ -192,7 +233,7 @@
|
|||||||
- plaintext пароль не сохраняется;
|
- plaintext пароль не сохраняется;
|
||||||
- верификация использует `password_pepper` из env.
|
- верификация использует `password_pepper` из env.
|
||||||
|
|
||||||
### 3.9. `UserSession`
|
### 3.11. `UserSession`
|
||||||
|
|
||||||
Поля:
|
Поля:
|
||||||
|
|
||||||
@@ -210,7 +251,7 @@
|
|||||||
- в persistent storage сохраняется только `secret_hash`;
|
- в persistent storage сохраняется только `secret_hash`;
|
||||||
- подпись и верификация используют `session_secret` из env.
|
- подпись и верификация используют `session_secret` из env.
|
||||||
|
|
||||||
### 3.10. `Membership`
|
### 3.12. `Membership`
|
||||||
|
|
||||||
Поля:
|
Поля:
|
||||||
|
|
||||||
@@ -219,7 +260,7 @@
|
|||||||
- `role`
|
- `role`
|
||||||
- `created_at`
|
- `created_at`
|
||||||
|
|
||||||
### 3.11. `InvitationToken`
|
### 3.13. `InvitationToken`
|
||||||
|
|
||||||
Поля:
|
Поля:
|
||||||
|
|
||||||
@@ -236,7 +277,7 @@
|
|||||||
- полный invite token показывается только один раз при создании;
|
- полный invite token показывается только один раз при создании;
|
||||||
- в persistent storage сохраняется только `token_hash`.
|
- в persistent storage сохраняется только `token_hash`.
|
||||||
|
|
||||||
### 3.12. `InvocationLog`
|
### 3.14. `InvocationLog`
|
||||||
|
|
||||||
Продуктовая запись о вызове tool.
|
Продуктовая запись о вызове tool.
|
||||||
|
|
||||||
@@ -255,7 +296,7 @@
|
|||||||
- `response_preview`
|
- `response_preview`
|
||||||
- `created_at`
|
- `created_at`
|
||||||
|
|
||||||
### 3.13. `UsageRollup`
|
### 3.15. `UsageRollup`
|
||||||
|
|
||||||
Агрегированная статистика по периоду.
|
Агрегированная статистика по периоду.
|
||||||
|
|
||||||
@@ -321,6 +362,11 @@
|
|||||||
|
|
||||||
Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
|
Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
|
||||||
|
|
||||||
|
Для upstream credentials это означает:
|
||||||
|
|
||||||
|
- placeholder-строки вида `${secrets.API_KEY}` не считаются реальной моделью данных;
|
||||||
|
- рабочая продуктовая модель строится только через `Secret` + `AuthProfile`.
|
||||||
|
|
||||||
Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в:
|
Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в:
|
||||||
|
|
||||||
- `docs/operations-workspace-contracts.md`
|
- `docs/operations-workspace-contracts.md`
|
||||||
|
|||||||
+38
-6
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
### 2.5. Секреты не хранятся в открытом виде
|
### 2.5. Секреты не хранятся в открытом виде
|
||||||
|
|
||||||
- upstream secrets живут за `secret_ref`;
|
- upstream secrets живут в отдельных таблицах и шифруются;
|
||||||
- platform API keys хранятся как hash.
|
- platform API keys хранятся как hash.
|
||||||
|
|
||||||
## 3. Основные таблицы
|
## 3. Основные таблицы
|
||||||
@@ -36,6 +36,8 @@
|
|||||||
- `user_sessions`
|
- `user_sessions`
|
||||||
- `memberships`
|
- `memberships`
|
||||||
- `invitation_tokens`
|
- `invitation_tokens`
|
||||||
|
- `secrets`
|
||||||
|
- `secret_versions`
|
||||||
- `operations`
|
- `operations`
|
||||||
- `operation_versions`
|
- `operation_versions`
|
||||||
- `published_operations`
|
- `published_operations`
|
||||||
@@ -134,7 +136,30 @@
|
|||||||
- `created_at`
|
- `created_at`
|
||||||
- `finished_at`
|
- `finished_at`
|
||||||
|
|
||||||
## 6. Upstream auth
|
## 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`
|
### `auth_profiles`
|
||||||
|
|
||||||
@@ -146,6 +171,11 @@
|
|||||||
- `created_at`
|
- `created_at`
|
||||||
- `updated_at`
|
- `updated_at`
|
||||||
|
|
||||||
|
Назначение:
|
||||||
|
|
||||||
|
- `config_json` хранит ссылки на `secret_id`, а не plaintext значения;
|
||||||
|
- допустимы bearer, basic, api-key-header, api-key-query профили.
|
||||||
|
|
||||||
Ограничение:
|
Ограничение:
|
||||||
|
|
||||||
- `unique (workspace_id, name)`
|
- `unique (workspace_id, name)`
|
||||||
@@ -308,7 +338,9 @@
|
|||||||
|
|
||||||
1. добавить `workspaces` и заполнить default workspace;
|
1. добавить `workspaces` и заполнить default workspace;
|
||||||
2. добавить `workspace_id` в `operations` и `auth_profiles`;
|
2. добавить `workspace_id` в `operations` и `auth_profiles`;
|
||||||
3. добавить `agents` и `published_agents`;
|
3. добавить `secrets` и `secret_versions`;
|
||||||
4. внедрить `platform_api_keys`;
|
4. перевести `auth_profiles` на secret-backed config;
|
||||||
5. добавить `invocation_logs` и `usage_rollups`;
|
5. добавить `agents` и `published_agents`;
|
||||||
6. перевести MCP runtime на `published_agents`, а не на глобальный список operations.
|
6. внедрить `platform_api_keys`;
|
||||||
|
7. добавить `invocation_logs` и `usage_rollups`;
|
||||||
|
8. перевести MCP runtime на `published_agents`, а не на глобальный список operations.
|
||||||
|
|||||||
@@ -109,7 +109,21 @@ DoD:
|
|||||||
- mock JSON больше не используется на критическом пути;
|
- mock JSON больше не используется на критическом пути;
|
||||||
- UI, backend и docs синхронизированы.
|
- UI, backend и docs синхронизированы.
|
||||||
|
|
||||||
## 10. Этап 9. Hardening and demo readiness
|
## 10. Этап 9. Secret store and upstream auth
|
||||||
|
|
||||||
|
Цель:
|
||||||
|
|
||||||
|
- заменить UI placeholder-модель `${secrets.*}` на рабочий backend/runtime слой secrets.
|
||||||
|
|
||||||
|
DoD:
|
||||||
|
|
||||||
|
- есть workspace-scoped `Secrets` resource;
|
||||||
|
- secret values хранятся только в зашифрованном виде;
|
||||||
|
- `AuthProfile` ссылается на `secret_id`, а не на строковый placeholder;
|
||||||
|
- runtime умеет применять bearer/basic/api-key auth к реальному upstream request;
|
||||||
|
- wizard имеет auth selector и quick-create flow для secrets/auth profiles.
|
||||||
|
|
||||||
|
## 11. Этап 10. Hardening and demo readiness
|
||||||
|
|
||||||
Цель:
|
Цель:
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ crank/
|
|||||||
Поверх существующих crates должны появиться новые логические поддомены:
|
Поверх существующих crates должны появиться новые логические поддомены:
|
||||||
|
|
||||||
- workspace/access domain;
|
- workspace/access domain;
|
||||||
|
- secret management domain;
|
||||||
- agent publishing domain;
|
- agent publishing domain;
|
||||||
- observability domain.
|
- observability domain.
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ crank/
|
|||||||
- `operation`
|
- `operation`
|
||||||
- `agent`
|
- `agent`
|
||||||
- `auth`
|
- `auth`
|
||||||
|
- `secret`
|
||||||
- `observability`
|
- `observability`
|
||||||
- `errors`
|
- `errors`
|
||||||
|
|
||||||
@@ -94,6 +96,7 @@ crank/
|
|||||||
Назначение:
|
Назначение:
|
||||||
|
|
||||||
- хранение workspace-scoped operations и version snapshots;
|
- хранение workspace-scoped operations и version snapshots;
|
||||||
|
- хранение workspace-scoped secrets и secret versions;
|
||||||
- хранение agents и agent versions;
|
- хранение agents и agent versions;
|
||||||
- auth profiles;
|
- auth profiles;
|
||||||
- platform API keys;
|
- platform API keys;
|
||||||
@@ -105,6 +108,7 @@ crank/
|
|||||||
Назначение:
|
Назначение:
|
||||||
|
|
||||||
- исполнение published operation;
|
- исполнение published operation;
|
||||||
|
- резолв `auth_profile_ref -> secret -> request auth`;
|
||||||
- запись invocation events;
|
- запись invocation events;
|
||||||
- возврат нормализованного результата.
|
- возврат нормализованного результата.
|
||||||
|
|
||||||
@@ -121,6 +125,7 @@ crank/
|
|||||||
Должен содержать сервисные группы:
|
Должен содержать сервисные группы:
|
||||||
|
|
||||||
- `workspaces`
|
- `workspaces`
|
||||||
|
- `secrets`
|
||||||
- `memberships`
|
- `memberships`
|
||||||
- `operations`
|
- `operations`
|
||||||
- `auth_profiles`
|
- `auth_profiles`
|
||||||
|
|||||||
+11
-13
@@ -36,22 +36,20 @@ var/crank/
|
|||||||
|
|
||||||
## 4. Секреты и auth profiles
|
## 4. Секреты и auth profiles
|
||||||
|
|
||||||
Для MVP:
|
Для целевой модели:
|
||||||
|
|
||||||
- operation хранит только `auth_profile_ref`;
|
- operation хранит только `auth_profile_ref`;
|
||||||
- auth profile хранит только `secret_ref`;
|
- `AuthProfile` хранит только ссылки на `secret_id`;
|
||||||
- реальные секреты не должны попадать в YAML export;
|
- plaintext секреты не должны попадать в YAML export;
|
||||||
- секреты не должны логироваться.
|
- plaintext секреты не должны логироваться;
|
||||||
|
- runtime получает секрет только на короткое время перед upstream вызовом.
|
||||||
|
|
||||||
Допустимые варианты secret storage:
|
Стартовая реализация:
|
||||||
|
|
||||||
- env-backed secret store;
|
- `PostgreSQL`-backed secret store;
|
||||||
- encrypted local secret storage.
|
- `ciphertext` хранится в БД;
|
||||||
|
- шифрование выполняется через `CRANK_MASTER_KEY`;
|
||||||
Минимальный безопасный вариант для MVP:
|
- ключ шифрования приходит только из env.
|
||||||
|
|
||||||
- `secret_ref` указывает на env variable alias или key в локальном secret store;
|
|
||||||
- приложение резолвит его на runtime.
|
|
||||||
|
|
||||||
## 5. Переменные окружения
|
## 5. Переменные окружения
|
||||||
|
|
||||||
@@ -165,6 +163,6 @@ Demo/deployment:
|
|||||||
|
|
||||||
- где лежит БД;
|
- где лежит БД;
|
||||||
- где лежат artifacts;
|
- где лежат artifacts;
|
||||||
- как резолвятся `secret_ref`;
|
- как резолвятся `secret_id` и как ротируется `CRANK_MASTER_KEY`;
|
||||||
- на каких bind-address запускаются `admin-api` и `mcp-server`;
|
- на каких bind-address запускаются `admin-api` и `mcp-server`;
|
||||||
- какой transport использует MCP server.
|
- какой transport использует MCP server.
|
||||||
|
|||||||
@@ -0,0 +1,245 @@
|
|||||||
|
# Secrets And Upstream Auth Plan
|
||||||
|
|
||||||
|
## 1. Назначение документа
|
||||||
|
|
||||||
|
Этот документ фиксирует полный план доведения upstream secrets и `AuthProfile` до рабочей продуктовой модели.
|
||||||
|
|
||||||
|
Сейчас UI показывает оператору конструкцию вида `${secrets.API_KEY}`, но backend/runtime не резолвит ее в реальный secret. Это создает ложный контракт. Цель этого документа - заменить placeholder UX на рабочую систему.
|
||||||
|
|
||||||
|
## 2. Текущее состояние
|
||||||
|
|
||||||
|
Что уже есть:
|
||||||
|
|
||||||
|
- `AuthProfile` как отдельная workspace-scoped сущность;
|
||||||
|
- `auth_profile_ref` в `ExecutionConfig`;
|
||||||
|
- CRUD endpoints для `auth_profiles`;
|
||||||
|
- UI поля для upstream auth headers;
|
||||||
|
- env `CRANK_SECRET_PROVIDER` и `CRANK_MASTER_KEY` в runtime docs.
|
||||||
|
|
||||||
|
Что еще не работает end-to-end:
|
||||||
|
|
||||||
|
- runtime не резолвит `auth_profile_ref` при выполнении operation;
|
||||||
|
- `secret_ref` не подтягивает фактическое значение секрета;
|
||||||
|
- wizard хранит auth как raw JSON headers;
|
||||||
|
- `${secrets.*}` в UI является только текстовым placeholder;
|
||||||
|
- отдельной страницы `Secrets` нет.
|
||||||
|
|
||||||
|
## 3. Целевая модель
|
||||||
|
|
||||||
|
### 3.1. `Secret`
|
||||||
|
|
||||||
|
Metadata-сущность для upstream credentials.
|
||||||
|
|
||||||
|
Поля:
|
||||||
|
|
||||||
|
- `id`
|
||||||
|
- `workspace_id`
|
||||||
|
- `name`
|
||||||
|
- `kind`
|
||||||
|
- `status`
|
||||||
|
- `current_version`
|
||||||
|
- `created_at`
|
||||||
|
- `updated_at`
|
||||||
|
|
||||||
|
Типы:
|
||||||
|
|
||||||
|
- `token`
|
||||||
|
- `username_password`
|
||||||
|
- `header`
|
||||||
|
- `generic`
|
||||||
|
|
||||||
|
### 3.2. `SecretVersion`
|
||||||
|
|
||||||
|
Значение секрета, зашифрованное мастер-ключом.
|
||||||
|
|
||||||
|
Поля:
|
||||||
|
|
||||||
|
- `secret_id`
|
||||||
|
- `version`
|
||||||
|
- `ciphertext`
|
||||||
|
- `key_version`
|
||||||
|
- `created_at`
|
||||||
|
|
||||||
|
### 3.3. `AuthProfile`
|
||||||
|
|
||||||
|
`AuthProfile` больше не хранит plaintext или `${secrets.*}`.
|
||||||
|
|
||||||
|
Вместо этого profile описывает:
|
||||||
|
|
||||||
|
- тип auth (`bearer`, `basic`, `api_key_header`, `api_key_query`);
|
||||||
|
- как secret применяется к запросу;
|
||||||
|
- какие `secret_id` для этого нужны.
|
||||||
|
|
||||||
|
Примеры:
|
||||||
|
|
||||||
|
- bearer -> `secret_id`
|
||||||
|
- basic -> `username_secret_id` + `password_secret_id`
|
||||||
|
- api key header -> `header_name` + `secret_id`
|
||||||
|
- api key query -> `param_name` + `secret_id`
|
||||||
|
|
||||||
|
## 4. Хранение и шифрование
|
||||||
|
|
||||||
|
### 4.1. Где хранить
|
||||||
|
|
||||||
|
Стартовый вариант:
|
||||||
|
|
||||||
|
- metadata в `PostgreSQL`
|
||||||
|
- ciphertext в `PostgreSQL`
|
||||||
|
|
||||||
|
Это проще, чем отдельный Vault, и достаточно для текущего продукта.
|
||||||
|
|
||||||
|
### 4.2. Чем шифровать
|
||||||
|
|
||||||
|
- `AES-256-GCM`
|
||||||
|
- мастер-ключ в `CRANK_MASTER_KEY`
|
||||||
|
- поддержка `key_version` для будущей ротации
|
||||||
|
|
||||||
|
### 4.3. Что нельзя делать
|
||||||
|
|
||||||
|
- не хранить plaintext в БД;
|
||||||
|
- не возвращать plaintext в list/get endpoints;
|
||||||
|
- не писать plaintext в logs;
|
||||||
|
- не включать plaintext в YAML export/import.
|
||||||
|
|
||||||
|
## 5. Backend API
|
||||||
|
|
||||||
|
### 5.1. Secrets endpoints
|
||||||
|
|
||||||
|
- `GET /api/admin/workspaces/{workspace_id}/secrets`
|
||||||
|
- `POST /api/admin/workspaces/{workspace_id}/secrets`
|
||||||
|
- `GET /api/admin/workspaces/{workspace_id}/secrets/{secret_id}`
|
||||||
|
- `POST /api/admin/workspaces/{workspace_id}/secrets/{secret_id}/rotate`
|
||||||
|
- `DELETE /api/admin/workspaces/{workspace_id}/secrets/{secret_id}`
|
||||||
|
|
||||||
|
### 5.2. Auth profiles
|
||||||
|
|
||||||
|
Нужно доработать:
|
||||||
|
|
||||||
|
- `POST /auth-profiles`
|
||||||
|
- `PATCH /auth-profiles/{auth_profile_id}`
|
||||||
|
|
||||||
|
Чтобы `config_json` ссылался на `secret_id`, а не на строковые placeholders.
|
||||||
|
|
||||||
|
### 5.3. Validation rules
|
||||||
|
|
||||||
|
- нельзя удалить secret, если на него ссылается auth profile;
|
||||||
|
- нельзя удалить auth profile, если на него ссылается опубликованная operation, без явного подтверждения migration path;
|
||||||
|
- rotate не должен ломать существующие published operations.
|
||||||
|
|
||||||
|
## 6. Runtime changes
|
||||||
|
|
||||||
|
Перед upstream-вызовом runtime должен:
|
||||||
|
|
||||||
|
1. проверить `auth_profile_ref`;
|
||||||
|
2. загрузить `AuthProfile`;
|
||||||
|
3. загрузить и расшифровать нужный secret;
|
||||||
|
4. применить auth к request:
|
||||||
|
- bearer -> `Authorization: Bearer ...`
|
||||||
|
- basic -> `Authorization: Basic ...`
|
||||||
|
- api key header -> произвольный header
|
||||||
|
- api key query -> query param
|
||||||
|
|
||||||
|
Это изменение должно жить в runtime/orchestration слое, а не в UI.
|
||||||
|
|
||||||
|
## 7. UI placement
|
||||||
|
|
||||||
|
### 7.1. Отдельная страница `Secrets`
|
||||||
|
|
||||||
|
Нужна как основное место управления:
|
||||||
|
|
||||||
|
- list
|
||||||
|
- create
|
||||||
|
- rotate
|
||||||
|
- disable/delete
|
||||||
|
- usage references
|
||||||
|
|
||||||
|
Эта страница должна появиться рядом с `API Keys`.
|
||||||
|
|
||||||
|
### 7.2. Quick create в wizard
|
||||||
|
|
||||||
|
На шаге upstream вместо raw `Auth headers` нужен selector:
|
||||||
|
|
||||||
|
- `No auth`
|
||||||
|
- `Bearer token`
|
||||||
|
- `API key header`
|
||||||
|
- `Basic auth`
|
||||||
|
|
||||||
|
Дальше:
|
||||||
|
|
||||||
|
- `Select existing auth profile`
|
||||||
|
- `Create auth profile`
|
||||||
|
- `Create secret`
|
||||||
|
|
||||||
|
Это позволяет не вырывать оператора из flow создания operation.
|
||||||
|
|
||||||
|
### 7.3. Что убрать
|
||||||
|
|
||||||
|
Нужно убрать из UX:
|
||||||
|
|
||||||
|
- `${secrets.API_KEY}` как рекомендованный путь;
|
||||||
|
- raw JSON headers как основной способ настройки auth.
|
||||||
|
|
||||||
|
Raw headers можно оставить только как advanced override, но не как primary flow.
|
||||||
|
|
||||||
|
## 8. Вертикальные срезы реализации
|
||||||
|
|
||||||
|
### Срез 1. Secret store foundation
|
||||||
|
|
||||||
|
DoD:
|
||||||
|
|
||||||
|
- таблицы `secrets` и `secret_versions`;
|
||||||
|
- encryption/decryption service;
|
||||||
|
- CRUD/rotate endpoints;
|
||||||
|
- integration tests на create/get/rotate/delete.
|
||||||
|
|
||||||
|
### Срез 2. Auth profile refactor
|
||||||
|
|
||||||
|
DoD:
|
||||||
|
|
||||||
|
- auth profile config ссылается на `secret_id`;
|
||||||
|
- миграция со старой модели `secret_ref`/placeholder;
|
||||||
|
- validation на dangling references.
|
||||||
|
|
||||||
|
### Срез 3. Runtime auth resolution
|
||||||
|
|
||||||
|
DoD:
|
||||||
|
|
||||||
|
- `auth_profile_ref` применяется при `REST`, `GraphQL` и `gRPC` вызовах;
|
||||||
|
- test-run использует тот же кодовый путь;
|
||||||
|
- runtime tests покрывают bearer/basic/api-key-header/api-key-query.
|
||||||
|
|
||||||
|
### Срез 4. Secrets UI
|
||||||
|
|
||||||
|
DoD:
|
||||||
|
|
||||||
|
- новая страница `Secrets`;
|
||||||
|
- list/create/rotate/delete;
|
||||||
|
- no plaintext leaks after creation.
|
||||||
|
|
||||||
|
### Срез 5. Wizard auth UX
|
||||||
|
|
||||||
|
DoD:
|
||||||
|
|
||||||
|
- step 2 получает auth selector;
|
||||||
|
- quick-create secret/profile flow;
|
||||||
|
- public upstreams можно оставлять без auth;
|
||||||
|
- placeholder `${secrets.*}` исчезает из primary UX.
|
||||||
|
|
||||||
|
## 9. Риски
|
||||||
|
|
||||||
|
- без аккуратной миграции можно сломать уже созданные auth profiles;
|
||||||
|
- если шифрование сделать без `key_version`, потом будет болезненная ротация;
|
||||||
|
- если secrets page сделать без quick-create в wizard, UX снова станет медленным;
|
||||||
|
- если quick-create сделать без отдельной страницы, управляемость secrets будет плохой.
|
||||||
|
|
||||||
|
## 10. Практический итог
|
||||||
|
|
||||||
|
Следующий правильный roadmap:
|
||||||
|
|
||||||
|
1. `feat/secret-store-foundation`
|
||||||
|
2. `feat/auth-profile-secret-resolution`
|
||||||
|
3. `feat/runtime-upstream-auth`
|
||||||
|
4. `feat/secrets-ui`
|
||||||
|
5. `feat/wizard-auth-selector`
|
||||||
|
|
||||||
|
Только после этого upstream auth можно считать реально реализованным.
|
||||||
Reference in New Issue
Block a user