Reveal
Plaintext для названного оператора — собственный scope, обязательная атрибуция, audit-запись до ответа.
Операторы, редактирующие credentials через Internal Admin API, видят только маски — и это правильно ровно до того дня, когда ключ может быть неверен, а единственным лекарством была бы слепая перезапись. POST /internal/v1/admin/credentials/{credential_id}/reveal — осознанное исключение: расшифровать одну запись и показать её человеку.
curl -s -X POST "$CREDS_BASE/internal/v1/admin/credentials/$CRED_ID/reveal" \
-H "Authorization: Bearer $REVEAL_TOKEN" \
-H "X-Admin-Actor: panel:alice"
Ответ несёт расшифрованные credentials, открытую configuration, координаты владельца и revealed_at.
Четыре намеренных ограничения
- Собственный scope.
provider-credentials.reveal— не подразумевается ниadmin(редактировать ≠ видеть), ниinvoke(машины резолвят, люди раскрывают). Реестр auth-сервера выдаёт его отдельно, только панели; см. Service Plane Tokens. - Названный оператор, обязательно. В отличие от прочих админ-вызовов, отсутствующий
X-Admin-Actor— это400. Reveal не оставляет после себя ничего, кроме своего audit-события; анонимное, оно говорило бы лишь «какой-то клиент прочитал секрет» — бесполезно. Анонимное раскрытие plaintext отвергнуто конструкцией. - Audit до ответа. Событие
credential.revealed(только имена полей, никогда значения) коммитится в собственной транзакции до отправки ответа — след существует, даже если ответ так и не дошёл. Событие также освобождено от очистки по ретенции audit: reveal редки, человечны и достойны хранения. - Собственный скудный бюджет. По умолчанию 10/мин на оператора (Rate Limits) — сильно ниже админских 60/мин, так что украденная сессия панели не сможет обойти всё хранилище со скоростью пагинации.
Семантика, которую стоит знать
- Работает на записях любого уровня и любого статуса, включая
disabled— предположительно сломанный ключ инспектируют именно тогда, когда он выключен. Мягко удалённые записи —404: удаление значит «исчезло». - Это
POST, а неGET: reveal — аудируемое действие, а не кэшируемый ресурс. Id остаётся в пути; значения едут только в теле, так что ни один секрет никогда не попадает в access-лог. - Шифротекст, который не расшифровывается —
500 CREDENTIAL_DECRYPTION_FAILED, сам аудируемый какcredential.decryption_failed— такой ответ является пожарной сигнализацией о keyring, а не кандидатом на повтор.
Разобранный пример, включая audit-событие, которое он оставляет: сценарий cookbook 08.