docs: define secret store and auth profile plan

This commit is contained in:
a.tolmachev
2026-04-06 00:40:25 +03:00
parent 09604c6481
commit 420074f96a
10 changed files with 428 additions and 36 deletions
+1
View File
@@ -44,6 +44,7 @@ Crank - платформа для публикации внешних API в в
- `docs/deployment.md` - деплой, reverse proxy и CI/CD.
- `docs/demo-runbook.md` - демонстрационный сценарий.
- `docs/public-smoke-targets.md` - готовые публичные upstream-сервисы и payload-ы для smoke-проверки MCP.
- `docs/secrets-auth-plan.md` - целевая модель upstream secrets, auth profiles и пошаговый план реализации.
- `docs/rust-design.md` - правила распределения поведения в Rust.
- `docs/development-rules.md` - правила разработки и workflow.
- `docs/rust-code-rules.md` - Rust-specific coding rules.
+10 -5
View File
@@ -2,19 +2,24 @@
## Current
### `feat/public-smoke-configs`
### `feat/secrets-auth-plan`
Status: completed
DoD:
- Public REST, GraphQL, and gRPC smoke targets are documented
- Repository contains ready-to-use operation payloads for MCP verification
- gRPC smoke config includes a runtime-ready descriptor set
- Docs describe the target secret store and auth profile model
- Backend, runtime, and UI gaps are captured as vertical slices
- TASKS and implementation plan reflect the new sequence
## Next
- `feat/manual-regression-pass`
- `feat/secret-store-foundation`
## 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`
+26
View File
@@ -26,6 +26,7 @@
- `memberships`
- `invitations`
- `operations`
- `secrets`
- `auth-profiles`
- `agents`
- `platform-api-keys`
@@ -116,12 +117,26 @@
### 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`
- `POST /api/admin/workspaces/{workspace_id}/auth-profiles`
- `GET /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}`
Контракт:
- `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
- `GET /api/admin/workspaces/{workspace_id}/agents`
@@ -188,6 +203,8 @@
- samples;
- draft generation;
- gRPC descriptor upload и discovery.
- upstream auth selector;
- quick-create secret / auth profile modal.
Детальные DTO и response shapes для экранов `Operations` и `Wizard` зафиксированы отдельно в:
@@ -209,6 +226,15 @@
- list/create/revoke/delete platform API keys;
- one-time reveal значения ключа при создании.
### Secrets
Нужны:
- list/create/rotate/delete upstream secrets;
- metadata-only retrieval после создания;
- связь с auth profiles;
- usage references, чтобы оператор видел, где секрет используется.
### Logs
Нужны:
+23 -3
View File
@@ -41,6 +41,7 @@ Crank - платформа для публикации внешних API в в
Изолирует:
- операции;
- secrets;
- auth profiles;
- agents;
- platform API keys;
@@ -84,6 +85,21 @@ Crank - платформа для публикации внешних API в в
- `Invitation`
- `PlatformApiKey`
### `Upstream secrets`
Отдельный слой для доступа к внешним системам:
- `Secret`
- `SecretVersion`
- `AuthProfile`
Принцип:
- секреты принадлежат workspace;
- plaintext не хранится в открытом виде;
- `AuthProfile` описывает способ применения секрета к upstream request;
- runtime резолвит `auth_profile_ref` в реальный header/query/basic auth только в момент вызова.
### `Observability`
Отдельный продуктовый слой:
@@ -117,6 +133,7 @@ Crank - платформа для публикации внешних API в в
- `Agent` и привязка операций к агенту.
- Agent-scoped MCP endpoints.
- Platform API keys.
- Workspace-scoped encrypted secrets для upstream access.
- Workspace-scoped auth profiles для upstream access.
- Product logs и usage aggregates.
- Импорт и экспорт operation-конфигураций в `YAML`.
@@ -137,9 +154,10 @@ Crank - платформа для публикации внешних API в в
1. Выбирает workspace.
2. Создает или редактирует operation.
3. Выполняет test run.
4. Публикует operation version.
5. Привязывает operation к одному или нескольким agents.
3. При необходимости выбирает или создает upstream secret / auth profile.
4. Выполняет test run.
5. Публикует operation version.
6. Привязывает operation к одному или нескольким agents.
### Оператор агентов
@@ -209,6 +227,8 @@ GraphQL в MCP публикуется как фиксированная опер
Базовые сущности:
- `Workspace`
- `Secret`
- `SecretVersion`
- `Operation`
- `OperationVersion`
- `Agent`
+54 -8
View File
@@ -40,6 +40,8 @@
Минимальный набор workspace-scoped сущностей:
- `Secret`
- `SecretVersion`
- `Operation`
- `OperationVersion`
- `AuthProfile`
@@ -143,7 +145,40 @@
- `tool_description_override`
- `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`
- `config`
### 3.7. `PlatformApiKey`
Принцип:
- `AuthProfile` не хранит plaintext;
- config ссылается на `secret_id` или пару `secret_id`, если auth-схема составная;
- runtime применяет profile к запросу только в момент вызова upstream.
### 3.9. `PlatformApiKey`
Отдельная сущность для доступа к самой платформе.
@@ -175,7 +216,7 @@
- полный secret показывается только один раз при создании;
- в persistent storage сохраняется только `secret_hash`.
### 3.8. `User`
### 3.10. `User`
Поля:
@@ -192,7 +233,7 @@
- plaintext пароль не сохраняется;
- верификация использует `password_pepper` из env.
### 3.9. `UserSession`
### 3.11. `UserSession`
Поля:
@@ -210,7 +251,7 @@
- в persistent storage сохраняется только `secret_hash`;
- подпись и верификация используют `session_secret` из env.
### 3.10. `Membership`
### 3.12. `Membership`
Поля:
@@ -219,7 +260,7 @@
- `role`
- `created_at`
### 3.11. `InvitationToken`
### 3.13. `InvitationToken`
Поля:
@@ -236,7 +277,7 @@
- полный invite token показывается только один раз при создании;
- в persistent storage сохраняется только `token_hash`.
### 3.12. `InvocationLog`
### 3.14. `InvocationLog`
Продуктовая запись о вызове tool.
@@ -255,7 +296,7 @@
- `response_preview`
- `created_at`
### 3.13. `UsageRollup`
### 3.15. `UsageRollup`
Агрегированная статистика по периоду.
@@ -321,6 +362,11 @@
Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
Для upstream credentials это означает:
- placeholder-строки вида `${secrets.API_KEY}` не считаются реальной моделью данных;
- рабочая продуктовая модель строится только через `Secret` + `AuthProfile`.
Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в:
- `docs/operations-workspace-contracts.md`
+38 -6
View File
@@ -26,7 +26,7 @@
### 2.5. Секреты не хранятся в открытом виде
- upstream secrets живут за `secret_ref`;
- upstream secrets живут в отдельных таблицах и шифруются;
- platform API keys хранятся как hash.
## 3. Основные таблицы
@@ -36,6 +36,8 @@
- `user_sessions`
- `memberships`
- `invitation_tokens`
- `secrets`
- `secret_versions`
- `operations`
- `operation_versions`
- `published_operations`
@@ -134,7 +136,30 @@
- `created_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`
@@ -146,6 +171,11 @@
- `created_at`
- `updated_at`
Назначение:
- `config_json` хранит ссылки на `secret_id`, а не plaintext значения;
- допустимы bearer, basic, api-key-header, api-key-query профили.
Ограничение:
- `unique (workspace_id, name)`
@@ -308,7 +338,9 @@
1. добавить `workspaces` и заполнить default workspace;
2. добавить `workspace_id` в `operations` и `auth_profiles`;
3. добавить `agents` и `published_agents`;
4. внедрить `platform_api_keys`;
5. добавить `invocation_logs` и `usage_rollups`;
6. перевести MCP runtime на `published_agents`, а не на глобальный список operations.
3. добавить `secrets` и `secret_versions`;
4. перевести `auth_profiles` на secret-backed config;
5. добавить `agents` и `published_agents`;
6. внедрить `platform_api_keys`;
7. добавить `invocation_logs` и `usage_rollups`;
8. перевести MCP runtime на `published_agents`, а не на глобальный список operations.
+15 -1
View File
@@ -109,7 +109,21 @@ DoD:
- mock JSON больше не используется на критическом пути;
- 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
Цель:
+5
View File
@@ -39,6 +39,7 @@ crank/
Поверх существующих crates должны появиться новые логические поддомены:
- workspace/access domain;
- secret management domain;
- agent publishing domain;
- observability domain.
@@ -61,6 +62,7 @@ crank/
- `operation`
- `agent`
- `auth`
- `secret`
- `observability`
- `errors`
@@ -94,6 +96,7 @@ crank/
Назначение:
- хранение workspace-scoped operations и version snapshots;
- хранение workspace-scoped secrets и secret versions;
- хранение agents и agent versions;
- auth profiles;
- platform API keys;
@@ -105,6 +108,7 @@ crank/
Назначение:
- исполнение published operation;
- резолв `auth_profile_ref -> secret -> request auth`;
- запись invocation events;
- возврат нормализованного результата.
@@ -121,6 +125,7 @@ crank/
Должен содержать сервисные группы:
- `workspaces`
- `secrets`
- `memberships`
- `operations`
- `auth_profiles`
+11 -13
View File
@@ -36,22 +36,20 @@ var/crank/
## 4. Секреты и auth profiles
Для MVP:
Для целевой модели:
- operation хранит только `auth_profile_ref`;
- auth profile хранит только `secret_ref`;
- реальные секреты не должны попадать в YAML export;
- секреты не должны логироваться.
- `AuthProfile` хранит только ссылки на `secret_id`;
- plaintext секреты не должны попадать в YAML export;
- plaintext секреты не должны логироваться;
- runtime получает секрет только на короткое время перед upstream вызовом.
Допустимые варианты secret storage:
Стартовая реализация:
- env-backed secret store;
- encrypted local secret storage.
Минимальный безопасный вариант для MVP:
- `secret_ref` указывает на env variable alias или key в локальном secret store;
- приложение резолвит его на runtime.
- `PostgreSQL`-backed secret store;
- `ciphertext` хранится в БД;
- шифрование выполняется через `CRANK_MASTER_KEY`;
- ключ шифрования приходит только из env.
## 5. Переменные окружения
@@ -165,6 +163,6 @@ Demo/deployment:
- где лежит БД;
- где лежат artifacts;
- как резолвятся `secret_ref`;
- как резолвятся `secret_id` и как ротируется `CRANK_MASTER_KEY`;
- на каких bind-address запускаются `admin-api` и `mcp-server`;
- какой transport использует MCP server.
+245
View File
@@ -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 можно считать реально реализованным.