Секреты для машин
Токены и пароли сервисов — под учётом
Пароль от базы, токен интеграции и ключ платёжного шлюза живут не в .env на трёх серверах и не в переписке, а в одном месте, где у каждого значения есть история, владелец и список тех, кто его читал.
Что это даёт на практике
Ротация без простояработает
Новая версия становится текущей одним действием, старая остаётся для отката. Потребители узнают о смене вебхуком или замечают её сами при следующем опросе.
Отложенное уничтожениеработает
Версия не исчезает по нажатию: она получает дату уничтожения и период ожидания. Передумали — отменили. Зачистку выполняет встроенный планировщик.
Защита от удаленияработает
Флаг deletion_protection на секрете: пока он стоит, удалить запись нельзя ни из кабинета, ни через API.
Деактивация вместо удаленияработает
У деактивированного секрета метаданные видны, а содержимое не выдаётся никому — удобный способ проверить, кто на самом деле им пользовался.
Организации и проектыработает
Все сущности принадлежат проекту, проекты — организации. Права, токены и журнал не пересекаются между проектами; граница тенанта закрыта на уровне базы.
Аудит каждого чтенияработает
В журнал попадает и успешное чтение, и отказ: попытка получить чужой секрет — такой же сигнал, как удачная выдача.
Вебхуки
Сигнал о ротации, а не рассылка значений
Подписка получает событие о новой версии, смене текущей версии, деактивации и активации секрета. Значения в теле события нет никогда: вебхук говорит «перечитай», а канал доставки остаётся один — API с проверкой прав.
Доставка идёт через таблицу-outbox в PostgreSQL: строка пишется в той же транзакции, что и доменная операция, поэтому события не теряются при перезапуске. Повторы экспоненциальные, у каждой доставки есть ключ дедупликации, тело подписано Ed25519 ключом инстанса — публичный ключ отдаётся отдельной ручкой для пиннинга.
- secret.version.created — появилась новая версия
- secret.current.changed — сменилось текущее значение, пора перечитать
- secret.deactivated / secret.activated — изменилась доступность
- Чтения payload наружу не публикуются — карта обращений к секретам остаётся аудитом
Контракт
Плоский JSON, который уже все умеют читать
# метаданные и содержимое — разные ресурсы GET /api/v1/secrets/billing/db # версии, роли, состояние GET /api/v1/secrets/billing/db/payload # значения, строка в журнале { "POSTGRES_USER": "billing", "POSTGRES_PASSWORD": "g7R…Qk" }
Ответ без обёрток выбран не для красоты: ровно такой формат ждёт generic webhook provider в External Secrets Operator, поэтому Kubernetes подключается без собственного оператора Wardhold.
Нужен разбор под ваш контур?
Расскажите, где сейчас лежат секреты и кто их читает, — предложим схему переноса и покажем, что из этого уже работает.