# 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 можно считать реально реализованным.