> ## Documentation Index
> Fetch the complete documentation index at: https://veriqa.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Hardening к продакшну

> Чек-лист перед выпуском интеграции Veriqa в продакшн.

Это чек-лист того, что команды чаще всего упускают перед выходом в прод. Это **не** полный
гайд по деплою — фокус только на легко пропускаемых пунктах. Состояние галочек хранится
локально в браузере; нужна копия — print-to-PDF браузера.

<Note>
  **Что продукт собирает, где хранит и сколько держит** — инвентарь, умолчания сбора, сроки
  хранения и что уходит за периметр — вынесено на отдельную страницу:
  [Безопасность и обработка данных](/docs/ru/guides/data-handling). Этот чек-лист — про решения,
  которые принимаете вы; та страница — про факты, на которых вы их принимаете.
</Note>

### Issuer и сертификаты

* [ ] Стабильный HTTPS issuer, актуальный JWKS, спланированная замена ключей.

Issuer должен быть постоянным HTTPS-адресом — он попадает в `iss` токенов и в discovery.
Клиенты обязаны доверять ключам через discovery / JWKS, а не через зашитую статику: тогда
замена ключа не требует правок на их стороне. Вне `Development` сертификаты подписи и шифрования
обязательны в обоих режимах поставки, встроенном и self-hosted, — без них хост не стартует
(ключи: [справочник конфигурации](/docs/ru/reference/configuration#server)).

<Warning>
  **Ротация без простоя не поддерживается.** Конфигурация принимает **один** сертификат
  подписи и **один** сертификат шифрования, поэтому JWKS отдаёт один ключ подписи —
  перекрытия старого и нового нет. Замена сертификата инвалидирует живые токены, подписанные
  прежним ключом; то же предупреждение — в
  [self-hosted-старте](/docs/ru/quickstart/self-hosted#3-предоставьте-сертификаты-токенов).
  Планируйте замену ключей как операцию с инвалидацией сессий, а не как фоновую.
</Warning>

### Клиентские секреты и хранение

* [ ] `client_secret` и токены каналов — вне репозитория, через окружение, с шифрованием и ротацией.

Секреты (клиентские `client_secret`, токены ботов, ключи API каналов) передаются только через
переменные окружения / секрет-стор, не через файлы конфигурации и не в гите. Шифруйте at-rest и
ротируйте.

### OIDC PKCE и state

* [ ] PKCE (`S256`) обязателен для public-клиентов; валидируйте `state` и `nonce`.

Для публичных клиентов PKCE с методом `S256` обязателен. `state` защищает от CSRF на
редиректе, `nonce` привязывает `id_token` к сессии. Veriqa дополнительно проверяет одноразовый
browser-nonce в callback — не отключайте эти проверки.

### Redirect URIs

* [ ] Whitelist exact-match; без wildcard в проде; `https` только (кроме loopback в dev).

Redirect URI сверяется точным совпадением. Wildcard в продакшне запрещён, `http` допустим
только для loopback-адресов (`localhost`, `127.0.0.1`, `[::1]`) при разработке.

### Scopes и claims

* [ ] Минимальный набор scopes; `openid` обязателен; не запрашивать лишний PII.

Запрашивайте минимум: `openid` обязателен, остальное — по необходимости. Не просите `email`,
если он не нужен; не храните PII сверх требуемого. `/userinfo` отдаёт только claims,
разрешённые scopes и конфигурацией.

### Rate limiting и anti-abuse

* [ ] Лимиты ядра подобраны под трафик каждого маршрута; per-IP-ограничитель стоит на reverse-proxy / WAF.

Ядро ограничивает каждую публичную поверхность **одним общим бакетом на маршрут** — `authorize`,
`token`, callback, polling, вебхуки, SignalR, стартовая страница Email, страницы подтверждения
(дефолты — в [конфигурации](/docs/ru/reference/configuration#veriqa-ratelimit)). Поверх этого оно считает
попытки входа **по пользователю** (channel identity), чтобы подтверждение нельзя было перебрать, и
даёт каждому relying party **собственный бюджет** на server-to-server входе подтверждения. Общие
бакеты подбирайте под суммарный трафик маршрута: они защищают доступность, а не одного посетителя
от другого.

Чего ядро **не** делает — не считает ни один лимит по IP клиента и не блокирует адреса.
Per-IP-лимит ловит единственное, чего не видят два других ключа, — **горизонтальный перебор**: много
аккаунтов с одного адреса, каждый в пределах своего пользовательского лимита, — и этот слой живёт на
краю, который у вас уже есть: `limit_req` в NGINX, rate-правило WAF, fail2ban по `429` в access-логе.
Тем же шагом развёртывания настраивается `ForwardedHeaders:KnownProxies` (см.
[self-hosted, шаг 6](/docs/ru/quickstart/self-hosted#6-поставьте-за-reverse-proxy)): край видит реальный
адрес клиента, ядро доверяет только тому, что край пробросил. Установка без ограничителя на краю
открыта для такого перебора, поэтому поставьте его до выхода в прод.

### Ротация refresh-токенов

* [ ] Включить rotation и revocation; реагировать на повторное использование.

Включите ротацию и отзыв refresh-токенов. При повторном использовании (replay) отзывайте всё
семейство токенов.

### Логирование и аудит

* [ ] Никогда не логировать токены / секреты / PII; включить аудит транзакций.

Токены, секреты и персональные данные в логи не попадают. Включите журнал аудита транзакций —
он даёт прослеживаемость входов и подтверждений.

Журнал пишется по событиям транзакции — создание, подтверждение, завершение, отказ,
истечение — и по security-событиям каналов. Каждая запись фиксирует время, действующее лицо,
код события (`action`), объект и исход (`result`).

<Note>
  **Два известных ограничения журнала** — учтите их, планируя отчётность и разбор инцидентов.

  **Выпуск токенов не аудируется.** Ни первичный выпуск по authorization code, ни ротация
  refresh-токена аудит-записи не создают: журнал пишется по событиям транзакции, а выпуск
  токенов через них не проходит и к транзакции не привязан — тем более ротация, которая
  случается уже вне её жизненного цикла. Не стройте доказательство выдачи токена на
  аудит-журнале: выпуск и ротацию контролируют отзыв токена и отзыв всего семейства при
  replay (см. [Ротация refresh-токенов](#ротация-refresh-токенов)).

  **Исход канальной security-записи неинформативен.** У записей, порождённых
  security-событиями канала, поле исхода одинаково — «отказ», и так для любого кода канала.
  «Отказ» в такой строке не значит «что-то сломалось»: смысл события несёт код в поле
  `action`, а его словарь даёт документация конкретного канала. Фильтруйте и разбирайте
  такие записи по `action`, а не по исходу.
</Note>

**Параметры подтверждённой операции — по явному включению.** По умолчанию записи
транзакции подтверждения не содержат, *что именно* подтверждали: объявленный вид действия и значения
слотов — предметные данные вашего приложения и могут содержать персональные. Если журнал
должен отвечать на вопрос «какая операция и с какими параметрами была подтверждена», включите
это осознанно:

```csharp Program.cs theme={null}
services.AddVeriqaAuditTrail(audit =>
{
    audit.UseInMemorySink();
    audit.ConfigureRecord(record => record.IncludeConfirmationParameters = true);
});
```

Включённые параметры копируются **в саму запись**, а не берутся по ссылке на транзакцию:
завершённая транзакция удаляется через минуты, а запись живёт весь срок retention. Их
получает **каждая** аудит-запись такой транзакции — не только запись подтверждения, но и
создание, завершение, отказ, истечение: вопрос «какая это была операция» задают и к ним. Из
процесса эти параметры не уходят — во внешнюю шину событий они не публикуются.

### Уровень проверки входящего события канала

* [ ] `InboundVerification` объявлен у каждой канальной секции — и объявлен осознанно.

Каждая канальная секция обязана нести `InboundVerification`: развёртывание с включённым
поставляемым каналом без значения не стартует. Стартовая проверка перебирает тот же
набор каналов, из которого ядро строит список включённых: все четыре поставляемых канала в нём есть,
а канал стороннего адаптера, подключённый одним вызовом `AddChannel`, — нет, и его секцию
проверяете глазами вы. Смотрите не на то, что ключ есть, а на то, **что в нём написано**: значение
обязано соответствовать режиму, в котором канал реально работает (таблица — в
[Настройке каналов](/docs/ru/guides/channels)). Расхождение здесь тихое: канал в вебхук-режиме, которому
объявили `outbound_fetch`, стартует молча.

`none` — **осознанный выбор, а не умолчание**: он означает «подлинность входящего события не
проверяется», то есть эндпоинт подтверждения этого канала принимает запрос от кого угодно.
Ставьте его, только если действительно приняли этот риск, и заносите такое решение туда, где
хранятся остальные решения о рисках.

### Доступность каналов

* [ ] Определить поведение при недоступности канала.

Заранее решите, что происходит, если канал недоступен: предложить альтернативный канал или
показать понятную ошибку — но не «зависать» в незавершённой транзакции.

### Локализация и доступность

* [ ] WCAG 2.2 AA; locale-файлы для языков вашей аудитории.

Страницы входа и подтверждения проектируются под WCAG 2.2 AA. Отдельный хост из `Dockerfile`
поставляет переводы на английский, русский и китайский; во встроенном режиме каталог локалей
поставляет ваш хост, и без него страница отображается на английском (см.
[Локализацию](/docs/ru/guides/customization#локализация)). Формального аудита доступности с
публикуемым отчётом не проводилось — если он требуется вашему регламенту, планируйте его на своей
стороне.

### Версионирование и миграции

* [ ] SemVer SDK; для реляционных хранилищ — миграции, а не авто-создание схемы.

Следуйте SemVer и планируйте апгрейды между minor / major. Реляционные хранилища
инициализируйте миграциями (история схемы), а не авто-созданием.

### Операционные основы

* [ ] Health-probe, шифрованный канал к БД, secret storage, внешний JS на странице входа выключен.

Health-probe хоста, шифрованное соединение с БД, хранение секретов в секрет-сторе. Внешний JS на
странице аутентификации по умолчанию запрещён — включайте только осознанно (см.
[кастомизацию](/docs/ru/guides/customization#внешний-css)).

## Дальше

<Card title="OIDC + Veriqa: разбор" icon="key" href="/docs/ru/concepts/oidc-explainer">
  Разберитесь, где именно Veriqa встаёт в OIDC-поток.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.