from nkl_manager import NklManager
manager = NklManager() # ~/.nkl-manager/server.toml, scheduler запущен
Один вызов вместо прямой работы
с сервером секретов.
Проект никогда не обращается к серверу секретов напрямую — он
вызывает get_secrets() со своей identity, локальным cache namespace и
именами нужных секретов. Всё остальное: шифрованный кэш,
per-contract lock, плановый refresh, TTL, деактивация — управляется
внутри nkl-manager.
Быстрый старт
Один менеджер на процесс; сервер секретов настраивается один раз через TOML.
pip install "nkl-manager>=2.0.0".
Версия 2 добавляет machine enrollment, обязательный защищённый Protocol v2,
DPAPI-защиту ключей и post-install configure.
result = manager.get_secrets(
project_id="checkout-service",
contract_id="payments-api",
contract_version=3,
secrets=["api_key", "webhook_secret"],
)
if result.success:
api_key = result.secrets["api_key"]
else:
log.error(result.error.code)
SecretsResult(success=False, error=...), а не как
исключение.
get_secrets(): алгоритм
Валидация → попытка из кэша → per-contract lock → refresh → (при сбое) stale-фолбэк.
- validate_request()
- Registry.get() по (project, contract, version)
- Кэш свежий и полный?
- → вернуть без сети
- RefreshLock.try_acquire()
- Повторная проверка кэша под locком
- server_client.fetch(...)
- Успех → запись кэша + registry
- Сбой + strict=False + есть stale-cache → отдать stale
- Иначе → success=False
strict=False по умолчанию
Если сервер недоступен, но на диске есть кэш, покрывающий все
запрошенные секреты, он отдаётся с stale=True
вместо ошибки. При strict=True это поведение
отключено: без свежих данных — только success=False.
Реактивация
Если существующая запись помечена INACTIVE
(см. неактивные контракты), она
трактуется как отсутствующая — контракт запрашивается заново,
как впервые.
Архитектура
NklManager — единственная публичная точка входа; всё остальное — внутренние компоненты.
Кэш и шифрование
Секреты на диске всегда зашифрованы; ключ хранится отдельно от метаданных.
cryptography.fernet)Registry (registry.db)
SQLite, метаданные о контрактах — секретные значения сюда никогда не попадают.
status, requested_secrets, server_revision,
fetched_at, expires_at, last_requested_at,
effective_ttl_seconds, ttl_source,
last_refresh_status, last_refresh_at, last_refresh_error,
cache_path
Locks и scheduler
Один lock на контракт — параллельные запросы к разным контрактам не блокируют друг друга.
Ключ — project_id:contract_id:contract_version. In-process и межпроцессный одновременно.
RefreshTimeoutError → success=False вместо бесконечного ожидания.
run_scheduled_refresh() обновляет все активные записи и чистит неактивные; сбой одного контракта не останавливает остальные.
Неактивные контракты
Контракты, которые давно не запрашивались, деактивируются, а не хранятся вечно.
| Период без обращений | Действие |
|---|---|
| < 30 дней | Обычное обновление по расписанию |
| 30–60 дней | Кандидат на деактивацию при следующей уборке |
| 60–90 дней (inactive) | Статус INACTIVE; больше не обновляется планировщиком |
| > 90 дней | Запись и кэш удаляются полностью |
INACTIVE-контракту не ошибка — он
обрабатывается как новый и снова становится ACTIVE.
Self-update (опционально)
package-updater — отдельная необязательная зависимость; основной пакет работает без неё.
Установлен
Если package-updater установлен отдельно, при каждом
импорте _bootstrap.run_update_check() вызывает
PackageUpdater.check_declared(package="nkl-manager")
до импорта остального пакета — ровно так, как требует контракт
самообновления.
Не установлен
Полный no-op: ModuleNotFoundError перехватывается,
логируется на уровне debug и не всплывает как warning или
ошибка. Основная функциональность nkl-manager не зависит от
updater вообще.
Конфигурация сервера секретов
Post-install configure сохраняет HTTPS host и TTL; optional token защищается DPAPI и не попадает в TOML.
nkl-manager configure https://secrets.example.com \
--default-ttl-seconds 900 \
--prompt-token
[server]
fetch-url = "https://secrets.example.com/api/v2/secrets/fetch"
client-id = "machine-id"
key-id = "nkl-rsa-key-id"
auth-token-file = "C:\\...\\server-token.protected"
protocol = 2
[manager]
default-ttl-seconds = 900
Ошибки
Каждая ошибка — стабильный code; сообщения никогда не содержат значения секретов.
INVALID_REQUEST
Запрос не прошёл валидацию (contract section 4).
CONTRACT_NOT_FOUND
Нет кэша и невозможен fetch с сервера.
CONTRACT_VERSION_UNAVAILABLE
У сервера нет данных для запрошенной версии контракта.
SERVER_UNAVAILABLE
Сервер секретов недоступен или вернул ошибку.
PARTIAL_SERVER_RESPONSE
Ответ сервера не содержит часть запрошенных секретов.
CACHE_CORRUPTED
Файл кэша существует, но структурно невалиден.
CACHE_DECRYPTION_FAILED
Кэш не расшифровался: неверный/отсутствующий ключ либо подделка.
REFRESH_TIMEOUT
Не удалось получить lock — другой процесс уже обновляет контракт.
STORAGE_ERROR
Сбой файловой системы или registry (диск, права доступа).
Полный API reference
Все классы, методы и функции библиотеки — с сигнатурами, аргументами, типами и примерами использования.