Основные концепции
Каталог и хранилище credentials, две плоскости, три уровня владения, одна цепочка resolve.
Пять идей объясняют весь сервис. Всё остальное — детали.
1. Каталог и хранилище — разные вещи
Каталог — это версионированный YAML, поставляемый вместе с сервисом: какие вендоры и провайдеры существуют, как должен выглядеть credential для каждого (credential_schema), какие несекретные настройки он принимает (configuration_schema), что провайдер умеет и какие уровни владения допускает. По API он read-only и одинаков для всех — см. Vendors and Providers.
Хранилище credentials — это PostgreSQL: зашифрованные секреты плюс их метаданные, у каждого есть владелец. Каталог говорит, что можно настроить; хранилище держит что настроено на самом деле.
2. Две плоскости, две системы аутентификации
| Плоскость | Пути | Кто | Аутентификация |
|---|---|---|---|
| Пользовательская плоскость | /v1/* |
люди, продуктовые бэкенды от имени пользователя | короткоживущий exchange-JWT (User Plane Tokens) |
| Сервисная плоскость | /internal/v1/* |
доверенные микросервисы | client-credentials JWT со scope (Service Plane Tokens) |
Обе плоскости обслуживаются одним развёртыванием и доменом. Пользовательский токен никогда не работает на /internal/*, а сервисный токен — на /v1/*: разные издатели, audience и валидаторы. Сервисная плоскость дальше делится по scope: provider-credentials.invoke резолвит, provider-credentials.admin администрирует, provider-credentials.reveal читает plaintext — и ни один не подразумевает другой.
3. Три уровня владения
Каждый сохранённый credential принадлежит ровно одному владельцу:
- user — личный; владелец —
subтокена вызывающего (Personal Credentials). - organization — общий для тенанта; владелец — клейм
org_id; управляется менеджерскими ролями организации (Organization Credentials). - platform — общий fallback, которым владеет оператор (User Plane Admin).
Каталог решает по каждому провайдеру, какие уровни вообще разрешены. На пользовательской плоскости владелец всегда берётся из токена — тела запросов никогда не называют владельца; только внутренняя админ-плоскость адресует произвольных владельцев явно.
4. Одна цепочка resolve
Когда рантайм перевода спрашивает «каким ключом мне работать для пользователя X, провайдера P?», сервис проходит USER → ORGANIZATION → PLATFORM в рамках политики провайдера и возвращает первый найденный активный default credential — вместе с plaintext, только через сервисную плоскость. Fallback при runtime-ошибках возвращённого ключа не существует: сломанный ключ всплывает наружу и никогда не подменяется молча. Вся история: Resolve Chain.
5. Секреты остаются запечатанными
Секреты валидируются по схеме провайдера, шифруются (AES-256-GCM, версионированный keyring) и с этого момента существуют для API ровно в трёх формах:
- маскированный — каждый читающий эндпоинт возвращает
masked_credentials(sk-1***abcd); достаточно, чтобы узнать ключ, но никогда — чтобы им воспользоваться; - отрезолвленный — plaintext для машин, только через resolve со scope
invoke; - раскрытый — plaintext для названного человека-оператора, только через reveal с его собственным scope, с audit-записью до отправки ответа.
Удаление — мягкое, с окном восстановления (по умолчанию 5 дней) до физической очистки; каждая мутация, resolve и reveal попадает в audit-ленту.
Статусы управляют всем
Сущности каталога бывают active/deprecated/disabled/removed; сохранённые credentials — active/disabled (+ мягко удалённые). Deprecated-провайдеры не принимают новых credentials; disabled-провайдеры отвечают 409 на resolve. Правила видимости по ролям — в Vendors and Providers.