Добавление клиентских credentials — runbook
Пошаговая инструкция добавления учётной записи клиента у поставщика.
Предварительные требования
- У клиента есть валидная учётная запись у поставщика (соответствующая
auth_schemaпоставщика — login/pass, API key, OAuth, etc.). - У клиента согласовано с поставщиком использование API.
- Клиент аутентифицирован в нашем личном кабинете.
Auth-схемы (HEAD 5e4ce54, 2026-05-06)
Поставщик объявляет auth_scheme в backend/internal/core/cabinet/credentials/supplier_catalog/catalog.go (Go-const, не runtime config). Реально используемый набор:
auth_scheme | Поля | Поставщики |
|---|---|---|
basic | login + password | ETM, IEK, Russvet |
api_token | api_token | Systeme Electric |
master_key | master_key | DKC |
Расширения oauth2 / bearer_token / custom остаются target — см. ADR-0010 (Pluggable credential schemas). Сейчас пять поставщиков покрывают первые три auth-схемы. Добавление новой схемы требует:
- Новый
AuthSchemeenum вsupplier_catalog/catalog.go. - Frontend ветка
AuthFieldsBySchema.tsxпод новую схему. - Connector соответствующего поставщика парсит JSON-payload секрета.
UI клиентского кабинета рендерится автоматически по secret_fields из SupplierMeta (catalog endpoint GET /api/cabinet/credentials/suppliers/catalog).
Сценарий A: клиент добавляет сам через UI
Live на 2026-05-06 — Cabinet Credentials cycle закрыт (
tracium_cabinet_credentials_execution.md). UI наhttps://tracium.dev/cabinet/credentials. AddCredentialModal — 3-step wizard.
Шаг 1. Запуск
Клиент → Личный кабинет → Учётные записи поставщиков → «Добавить» → выбор поставщика из catalog’а.
Шаг 2. Выбор схемы
Если у поставщика несколько поддерживаемых схем — клиент выбирает (или подставляется default).
Шаг 3. Инструкция
Система показывает supplier-specific инструкцию:
- Как получить нужные секреты у поставщика.
- Какие договорные действия требуются с их стороны.
- Какие данные мы будем запрашивать и как часто.
Шаг 4. Ввод данных
Клиент вводит секреты в форму, сгенерированную по auth schema. Поля с secret: true помечены и скрыты в UI.
Форма отправляет данные напрямую в backend → шифрование → запись в БД.
Шаг 5. Согласие
Клиент подтверждает условия обработки.
Шаг 6. Fingerprint и проверка дедупликации
-
Backend вычисляет fingerprint по auth_payload (детерминированный, на основе
auth_schema):fingerprint = sha256_hex( supplier_id || ':' || auth_schema || ':' || canonical_identity_field(auth_schema, auth_payload) )где
canonical_identity_field:auth_schema поле для hash login_passwordlowercase(trim(login))api_keysha256(api_key)(мы НЕ храним сам ключ в hash)oauth2provider_user_id(получается при первом успешном validation request)bearer_tokensha256(bearer_token)customопределяется в spec.md коннектора, обязательно reproducible Реализация —
platform/credentials/fingerprint.go; референс-вектора покрыты unit-тестами на каждый supplier’а. -
Если fingerprint совпадает с существующей
activecredential другого customer’а — авто-merge вSupplierCredentialGroup(по правилам в10-business/contexts/credentials.md§“Auto-merge правила”). -
Если совпадение только частичное (тот же login, разный пароль; тот же key_id, другой key) — НЕ merge; запись попадает в
moderation_queueдля оператора каталога. -
Клиент об этом не информируется (это административная деталь).
Шаг 7. Валидация
- Создаётся запись
SupplierCredentialсо статусомvalidating. - Коннектор делает тестовый запрос (например, ETM: login + info request).
- Успех → статус
active,last_validated_at = now(). - Ошибка → статус
failing, ошибка показывается клиенту.
Шаг 8. Старт работы
- Credential подхватывается ingestion’ом со следующего scheduled tick’а.
- Клиенту показывается статус и метрика “первый успешный fetch”.
Сценарий B: ассистированное добавление через поддержку
То же, но шаги 1-5 выполняются поддержкой по запросу клиента. Все остальные шаги идентичны.
Для аудита: каждое такое добавление имеет created_by = support-agent-id.
Сценарий C: API для клиентов (Phase 6+)
Программное добавление через наш API:
POST /v1/credentials
{
"supplier_id": "etm",
"label": "Основной договор",
"auth_schema": "login_password",
"auth_payload": {
"login": "...",
"password": "..."
},
"features": ["catalog", "prices", "stock"]
}
Все требования сценария A применимы.
Процедура валидации
Каждая credential проходит валидацию:
- Сразу при создании — синхронно.
- По расписанию — раз в сутки.
- При каждом N-том запросе — sampling.
Валидация выполняется через Connector.Validate(ctx, cred).
Обработка ошибок
Если credential начинает отвечать ошибками auth:
- Помечаем
failingпосле 3 подряд неудач. - Бакет приостанавливается.
- Клиенту → уведомление.
- В UI кабинета → красный badge.
- Pricing для этого клиента временно работает на системной credential с пометкой.
- После повторной успешной валидации → возврат в active.
Idle credential: если не используется N дней — статус idle, на UI клиента — предложение проверить или удалить.
Отзыв
Клиент отзывает credential
- Кабинет → Credentials → “Отозвать”.
- Подтверждение.
- Событие
SupplierCredentialRevoked. - Если credential — единственный участник группы → группа удаляется. Иначе — primary переключается.
- Бакет удаляется.
- История observations не удаляется (для аудита), но помечается
from_revoked_credentialи не используется в новых pricing-запросах.
Удаление аккаунта клиента
Каскадно отзывает все его credentials. Observations анонимизируются (customer_ref → null) или архивируются согласно политике хранения.
Безопасность
См. ADR-0010 (envelope encryption), ADR-0018 (master key + DEK rotation), 10-business/contexts/credentials.md §“Безопасность”.
Bootstrap master-ключа (для оператора по env)
| Env | Что задать |
|---|---|
| prod / staging (Vault Transit) | MASTER_KEY_PROVIDER=vault, VAULT_ADDR, VAULT_TOKEN (через k8s secret), VAULT_TRANSIT_KEY=tracium-master-v1 |
| prod / staging (KMS) | MASTER_KEY_PROVIDER=kms, KMS_KEY_ID=arn:... (или эквивалент yandex-cloud), AWS_REGION/YC_KEY_FOLDER |
| dev (sealed-file) | MASTER_KEY_PROVIDER=sealed_file, MASTER_KEY_FILE=/etc/tracium/master.key.sealed, MASTER_KEY_PASSPHRASE (env) |
| single-dev local | MASTER_KEY_PROVIDER=plaintext_env, MASTER_KEY_RAW=base64-32bytes, обязательно TRACIUM_ALLOW_INSECURE_MASTER_KEY=1 (без него backend паникует на старте); запрещено в любом deployment кроме одного локального разработчика |
Процедура bootstrap:
- (prod/staging) Создать transit key в Vault:
vault write -f transit/keys/tracium-master-v1. Backend получает токен с capabilitiesencrypt, decrypt, rewrapна этот ключ. - Запустить backend → проверить
/health— должно показатьmaster_key.provider=vault, key_id=tracium-master-v1. - Создать первый системный credential через admin UI; убедиться, что в
credential_dekпоявилась запись сmaster_key_id=tracium-master-v1.
События аудита
Каждое из событий пишется в event_store (event-sourced) и в credential_decrypt_audit для расшифровок:
SupplierCredentialCreated{credential_id, customer_id, supplier_id, scope, created_by}SupplierCredentialValidated{credential_id, outcome, validated_by}SupplierCredentialMarkedFailing{credential_id, reason}SupplierCredentialRevoked{credential_id, revoked_by, reason}SupplierCredentialGroupMerged{group_id, members[]}CredentialDekRotated{customer_id, reason, old_dek_id, new_dek_id}CredentialDecrypted(только вcredential_decrypt_audit, не в event_store):actor, credential_id, customer_id, master_key_id, reason, correlation_id
Break-glass
Расшифровка credential вне обычного pricing pipeline (для расследования инцидента / поддержки):
- Оператор открывает admin UI → Credentials → выбирает credential → “Request decryption for investigation”.
- Указывает
reason(free text) иincident_ref(Linear / Jira тикет — обязательно). - Запрос попадает в очередь approval — требуется второй approver с ролью
security(принцип 4 глаз). - После двух approve → расшифровка выполняется, plaintext отображается в UI на одну загрузку страницы (не сохраняется в browser storage), пишется событие
CredentialDecrypted{reason="manual_break_glass", ...}. - Автоматическая нотификация в security-канал (Slack / Telegram).
При попытке decrypt без второго approver’а — операция блокируется на уровне UI и API.
Транспорт
- Все клиентские формы передают секреты только по HTTPS (TLS 1.2+; TLS 1.3 в prod).
- CSP-политика admin/customer UI:
default-src 'self'; form-action 'self'. - В публичном API — обязательная валидация content-type, размер payload ≤ 64 KB на endpoint
POST /v1/credentials.
Метрики
Для каждой credential:
- Успешных запросов / ошибок за период.
- Использование rate budget.
- Дата последней валидации.
- Покрытие observations.
Открытые вопросы
- Какой backoff и период повторной валидации использовать после серии ошибок логина.
- Кто подтверждает автоматическое объединение одинаковых credentials в одну группу, если fingerprint совпал.
- Какой UX уведомлений считаем обязательным, если pricing временно перешёл на системную credential.