Default-записи и жизненный цикл
Один default на владельца и провайдера, явные переходы, и что на самом деле делают disable/delete.
Сохранённый credential бывает active или disabled, может быть default-записью своего владельца и может быть мягко удалён. Эти три оси управляют всем, что делает resolve, поэтому их инварианты строги.
Флаг default
is_default помечает запись, которую resolve выбирает для её владельца и провайдера. Инвариант: не более одного активного default на пару (провайдер, владелец), обеспечивается транзакционно.
make-defaultпереключает атомарно: предыдущий default теряет флаг в той же транзакции; не бывает ни момента с двумя, ни промежутка без единого. Претендуют толькоactive-записи (иначе400). Вызов на текущем default — no-op с проверкой версии.- Первый credential владельца для провайдера становится default автоматически — без всякого флага; второй create без флага оставляет default там, где он был.
createсmake_default: trueзабирает default на новую запись тем же атомарным переключением. Задокументированный409 DEFAULT_CREDENTIAL_ALREADY_EXISTS— это предохранитель конкурентности (два таких create наперегонки), а не обычный ответ; повторите раз — и один из них держит флаг.
Disable и enable
disable приостанавливает запись, не уничтожая её: resolve немедленно перестаёт её видеть, а is_default снимается в той же транзакции. Отключая свой текущий default, вы можете передать replacement_default_id — другую активную запись того же владельца и провайдера — чтобы атомарно повысить её; без него владелец сознательно остаётся без default (и resolve проваливается на следующий уровень).
enable возвращает запись в active, но не восстанавливает флаг default — повторное повышение делается явным make-default. Восстановленный ключ, молча снова становящийся живым default — ровно тот сюрприз, который это правило предотвращает.
Delete
DELETE — мягкое удаление: мгновенное исчезновение из списков, чтений, resolve и reveal; is_default снимается; шифротекст переживает окно восстановления (по умолчанию 5 дней), пока задача очистки не удалит его физически (в audit — как credential.purged). Удаление текущего default принимает тот же replacement_default_id. Повтор удаления на своей уже удалённой записи: идемпотентный 204. Версия не требуется.
Оптимистичная блокировка связывает всё вместе
PATCH, make-default, disable и enable — все требуют текущую version; каждый успех её инкрементирует. Два админа, редактирующие одну запись, не могут молча перезаписать друг друга — проигравший получает 409 CREDENTIAL_VERSION_CONFLICT и перечитывает. Полная конвенция: Errors and Conventions.
Разобранный пример всего танца — два ключа, переключение, disable с заменой: сценарий cookbook 04.