Wardhold

Секреты для машин

Токены и пароли сервисов — под учётом

Пароль от базы, токен интеграции и ключ платёжного шлюза живут не в .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.

Нужен разбор под ваш контур?

Расскажите, где сейчас лежат секреты и кто их читает, — предложим схему переноса и покажем, что из этого уже работает.