## Критерии приёмки

Дословно из записи задачи `trusted-header-login`. Файл задачи закрытие удалит —
критерии обязаны его пережить.

1. Обращение к адресу приложения с заголовком от доверенного источника идёт от
   имени учётной записи, заведённой при первом таком обращении, а повторное с тем
   же значением попадает в ту же запись. Оракул — тест обработчика: два запроса
   подряд, в хранилище одна запись пользователя.
2. Тот же заголовок с недоверенного адреса даёт `401`, а не вход под названным
   именем. Оракул — тест: запрос с адресом источника вне перечня доверенных.
3. Собственные адреса входа хранилища сессии не выдают и учётную запись не
   меняют. Оракул — тест по перечню адресов под `/api/collections/users/`: каждый
   отвечает отказом.
4. Механики OIDC в дереве не осталось: корня `/auth`, кук входа,
   `ApplyProviderSettings`, `cmd/oidcstub` и секрета клиента в конфиге. Оракул —
   поиск по этим именам плюс зелёный `task gate`.
5. Разграничение записей по владельцу работает как прежде: чужая запись
   неотличима от несуществующей. Оракул — существующие тесты владельца остаются
   зелёными.

## Рубрика ревью дизайна

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

1. Доверие ограничено источником, и источник берётся у соединения, а не у
   пересылаемого значения; настройка проверяется на старте, небезопасного
   умолчания нет.
2. Заголовок узнаётся однозначно: более одного значения не даёт «первое
   попавшееся».
3. Вырожденное значение — пустое, пробельное, сверх предела длины, с
   управляющими знаками — не узнаёт никого и не заводит ничего.
4. Ключ учётной записи стабилен, и цена его нестабильности названа в обе стороны
   — и смена, и переиспользование. Выборка по ключу имеет свой индекс.
5. Заведение первым обращением идемпотентно и устойчиво к конкуренции; отказ
   уникальности не по ключевой колонке имеет назначенный исход.
6. Результат остаётся функцией уже произошедшего: ключевая колонка неизменяема
   после заведения.
7. Второго способа стать этим человеком нет, а при двух предъявленных удостоверениях
   победитель назначен нормой, а не порядком слоёв.
8. Закрытие поверхности не отменяет законного пути заведения записи самим
   сервисом.
9. Значение заголовка — недоверенный вход на всём пути: не в журнал, не в ответ,
   не в метку метрики, и в хранилище параметром, а не подстановкой в фильтр.
10. Охват слоя назван, перечень открытых адресов закрыт, посторонний заголовок
    ответа открытых адресов не меняет.
11. Срок узнавания назван: всё, что переживает запрос, либо отсутствует, либо
    имеет назначенный срок, и этот срок и есть задержка отзыва.
12. Исход узнавания наблюдаем владельцем и неразличим для отправителя.

## 1. Схема хранилища

- [x] 1.1 Новый шаг схемы `202608220001_trusted_header_login.go`: колонка
      `provider_login` в коллекции `users` с уникальным индексом, почта
      переведена в необязательную, `OAuth2.Enabled = false` и
      `OAuth2.Providers = nil`. Применённый `202608120001_oidc_login` не
      трогается.
- [x] 1.2 Тот же шаг снимает **все** правила доступа коллекции `users` в пустое —
      `ListRule`, `ViewRule`, `CreateRule`, `UpdateRule`, `DeleteRule`. Сегодня
      четыре из них библиотечные (`id = @request.auth.id`), и правка своей записи
      открыта: это путь захвата чужого имени через ключевую колонку.
- [x] 1.3 Шаг зарегистрирован в `migrations.go`, имя ключевой колонки объявлено
      константой рядом с именами коллекций.
- [x] 1.4 `schema_test.go` сторожит `users` наравне с шестью коллекциями,
      которые он уже проверяет: все пять правил пусты.
- [x] 1.5 Откат шага возвращает то, что было до него, кроме открытого создания
      записи и открытой правки, и не падает на проверке коллекции.
- [x] 1.6 `docs/database.md` описывает новую колонку, снятые настройки и снятые
      правила доступа.

## 2. Настройка

- [x] 2.1 Секция входа конфига переписана: адреса провайдера, идентификатор и
      секрет клиента, адрес возврата и признак защищённой куки убраны, перечень
      доверенных адресов заведён: секция `[auth]` остаётся под своим именем, в
      ней один ключ `trusted_proxies` (решение человека 2026-08-22).
- [x] 2.2 Проверка настройки на старте: пустой перечень и нечитаемая строка
      роняют старт с именем ключа.
- [x] 2.3 `config.example.toml` переписан вместе с секцией, раздел про локальный
      вход заменён на подставной прокси.

## 3. Узнавание пришедшего

- [x] 3.1 Слой узнавания в транспорте HTTP: читает заголовок, судит адрес пира по
      перечню, ставит учётную запись предъявителя только когда её ещё нет.
      Вешается корневым, но действует на объявленной области — корень приложения
      плюс адрес выдачи файлового токена; область выводится из перечня адресного
      пространства, а не пишется вторым списком.
- [x] 3.2 Приём значения заголовка: пустое и пробельное не узнают никого, более
      одного значения не узнаёт никого, предел длины и отказ на управляющие
      знаки, сравнение точное. Значение уходит в хранилище параметром, а не
      подстановкой в текст фильтра.
- [x] 3.3 Поиск и заведение учётной записи — **методом пакета хранилища**, а не
      куском в транспорте: у правила один дом, и `api-tokens` возьмёт его же.
      Имя и почта берутся только при заведении, найденная запись не
      переписывается; отказ уникальности по ключевой колонке ведёт к повторному
      поиску, по любой другой — к заведению записи без почты.
- [x] 3.4 Отказ хранилища при узнавании кончается отказом сервиса, а не
      молчаливым проходом неузнанным.
- [x] 3.5 Журнал: исходы «заголовок с недоверенного адреса» и «учётная запись
      заведена» — с адресом пира и идентификатором записи, без значения
      заголовка; перечень доверенных адресов называется строкой при подъёме.
- [x] 3.6 `ApplyProviderSettings`, `ProviderSettings` и `ProviderName` убраны из
      пакета хранилища; `SessionDuration` убран вместе со сроком сессии.

## 4. Снос механики OIDC

- [x] 4.1 `internal/controller/http/auth.go` удалён целиком вместе с корнем
      `/auth` в перечне адресного пространства.
- [x] 4.2 `SessionFromCookie` и `BlockSessionRefresh` удалены; места их привязки
      переписаны на новый слой.
- [x] 4.3 `cmd/oidcstub` удалён.
- [x] 4.4 Приложение: адрес, которым экран уводил ко входу, убран; неузнавание
      показывается строкой и отличается от прочей неудачи. Юнит-тесты приложения
      обновлены.

## 5. Способ представиться без прокси

- [x] 5.1 Заведён `cmd/devtools` с подкомандой `proxy`: слушает свой порт,
      ставит заголовок, переправляет запрос сервису. Подкоманду `admin` заводит
      задача `dev-run-task`. В образ пакет не едет: строка сборки `Dockerfile`
      называет точку входа поимённо.
- [x] 5.2 `CLAUDE.md`, раздел «Команды» и раздел «Запреты» — про локальный вход
      без прокси; `README.md`, если он про это говорит.

## 6. Проверки

- [x] 6.1 Тест: первое обращение с доверенного адреса заводит учётную запись,
      второе с тем же значением попадает в ту же — в хранилище одна запись
      (критерий 1).
- [x] 6.2 Тест: тот же заголовок с недоверенного адреса даёт `401`, и учётной
      записи не появляется (критерий 2).
- [x] 6.3 Тест по перечню собственных адресов входа хранилища под
      `/api/collections/users/`: каждый отвечает отказом и учётной записи не
      меняет (критерий 3).
- [x] 6.4 Тест: годный собственный токен побеждает заголовок, а протухший
      узнаванию не мешает.
- [x] 6.4a Тест: узнанный не правит свою запись в коллекции пользователей
      запросом к хранилищу и не перечисляет коллекцию — путь захвата чужого имени
      закрыт.
- [x] 6.4b Тест: пустой заголовок, два значения одного заголовка и значение
      сверх предела длины не узнают никого и записи не заводят.
- [x] 6.4c Тест: два одновременных первых обращения одним значением дают одну
      запись.
- [x] 6.4d Тест: занятый адрес почты не мешает завести запись — она заводится
      без почты.
- [x] 6.4e Тест: запрос с заголовком на `GET /health` учётной записи не заводит.
- [x] 6.4f Тест: прежние адреса под корнем `/auth` отдают разметку приложения.
- [x] 6.5 Тест: значение заголовка не попадает в журнал.
- [x] 6.6 Тест: ответ на успешный запрос не ставит браузеру куки.
- [x] 6.7 Тест: токен файла выдаётся узнанному по заголовку, и путь к файлу
      записи проходит целиком.
- [x] 6.8 Существующие тесты владельца и разграничения переписаны на новый способ
      представиться и остаются зелёными (критерий 5).
- [x] 6.9 Тесты входа, обмена кода, куки, продления и выхода удалены вместе с
      предметом.

## 7. Документы и закрытие

- [x] 7.1 `docs/security.md`: периметр, недоверенный вход, разграничение доступа;
      четвёртый сдвиг про секрет в базе снят; требование к контуру — прокси
      **ставит** заголовок, а не пропускает пришедший — названо поимённо; там же
      цена переиспользования логина у провайдера.
- [x] 7.2 `CLAUDE.md`: изъятие из инварианта о секрете снято.
- [x] 7.3 `docs/architecture.md` приведён в соответствие; в «Единые точки
      проекта» добавлена строка про дом правила узнавания предъявителя.
- [x] 7.3a `docs/passport.md`: строки про сессию OIDC у потребителей и в границе
      «Управление учётными записями» — учётную запись сервис не заводит по своей
      воле, а зеркалит имя, названное провайдером.
- [x] 7.4 Поиск по `oidc`, `transcriber_session`, `transcriber_login`,
      `ApplyProviderSettings`, `/auth/` в дереве не находит живого кода
      (критерий 4).
- [x] 7.5 `task gate` зелёный, кроме унаследованного `hadolint DL3066` — отказ
      воспроизводится на чистом `origin/master`, объявлен долгом в `CLAUDE.md`
      (критерий 4).

## 8. Отработка ревью кода

- [x] 8.1 Слой узнавания переехал за ограничитель частоты: отвергнутый запрос
      больше не заводит учётной записи.
- [x] 8.2 Заведение учётной записи пишется в журнал; два значения заголовка —
      предупреждением, а не отладочным уровнем.
- [x] 8.3 Имя и почта принимаются, а не кладутся как есть: длинное имя больше не
      запирает человека вечным отказом сервиса.
- [x] 8.4 Отказ уникальности судится по коду, а не по имени колонки: негодная
      почта отличима от занятой.
- [x] 8.5 Предел логина считается в знаках, а не в байтах.
- [x] 8.6 Уникальный индекс по ключу сделан частичным — как соседний индекс
      почты; подъём на непустой базе больше не роняет накатку.
- [x] 8.7 Ключ учётной записи закрыт от правки рукой в панели модельным хуком.
- [x] 8.8 Хранилищу назван заголовок адреса спрашивающего: бюджет ограничителя
      перестал быть общим на весь сервис.
- [x] 8.9 Приложение показывает текст сервера, своего словаря под коды ответа не
      заводит; текст `401` больше не зовёт ко входу, которого нет.
- [x] 8.10 Оснастка берёт имена заголовков константами транспорта.
- [x] 8.11 Три записи конвенций приведены к действительности; словарь полей
      журнала пополнен, изъятие про вывод оснастки записано.
- [x] 8.12 Порог отката образа назван в «Эксплуатации»; требование к контуру про
      `X-Forwarded-For` и цена ширины перечня — в модели угроз.
- [x] 8.13 Образец конфига сужен до адреса прокси.
- [x] 7.6 Поведенческая проверка: сервис поднят локально, приложение открывается,
      учётная запись заводится первым обращением, запрос без заголовка получает
      отказ.
