# ADR-031: Служебный выход перед повторным входом

## Контекст

Ключевой блоб отдаёт только `POST /api/login`: `GET /api/me` его не возвращает, отдельного эндпоинта в `docs/protocol.md` нет. Поэтому смена пароля проходит через вход. Вход заводит новую сессию и перезаписывает cookie, а cookie — `HttpOnly`: прежний токен после этого недостижим, закрыть ту сессию клиенту уже нечем. Оставлять её живой нельзя — украденная cookie пережила бы смену пароля, ради которой всё и затевалось. Значит, выход идёт первым, до входа.

Но `POST /api/logout` — обычный непубличный запрос, и `401 unauthenticated` на нём по `docs/ui.md` («Сеть и состояния») выбрасывает на экран входа. Отсюда отказ. Смена пароля с неверным старым паролем закрывает сессию и падает на входе; клиент остаётся на настройках и показывает «неверный пароль». Вторая попытка, уже с верным паролем, начинается с того же служебного выхода, получает `401` — и уходит на экран входа молча: ошибка пишется в узел, которого в документе уже нет. Удаление аккаунта после такой попытки получает `401` на `DELETE /api/me` и тоже уезжает на экран входа, ничего не удалив.

## Решение

- Смена пароля и удаление аккаунта начинаются со служебного выхода, потом входят заново. Порядок «выход → вход» не оставляет на сервере сессию, токена от которой нет ни у кого.
- Служебный выход не заканчивает сеанс для пользователя. `401 unauthenticated` на нём означает «сессии и так нет» и считается успехом: следующий шаг открывает новую. Обработчик истёкшей сессии на таком ответе не зовётся, ошибка не бросается. Прочие отказы — нет сети, `500` — поднимаются наверх и показываются как есть: при живой сессии входить заново нельзя.
- Мимо обработчика идёт ровно этот вызов. `401 unauthenticated` на любом другом запросе по-прежнему ведёт на экран входа с сохранением IndexedDB.
- Удаление аккаунта входит прямым `POST /api/login`, без разбора блоба: аккаунт с испорченным блобом обязан удаляться.
- Кнопка «выйти» пользуется тем же вызовом: сеанс там заканчивает сам клиент — стирает базу и рисует экран входа, — а не ответ сервера.

## Следствия

- Сорвавшаяся смена пароля оставляет клиент без сессии, но на своём экране и со своей строкой: «неверный пароль», «нет соединения». Следующая попытка — смена пароля или удаление аккаунта — начинается с того же служебного выхода и проходит целиком.
- Перезагрузка страницы в этом состоянии показывает экран входа: `GET /api/me` отвечает `401`, IndexedDB цела. Это обычный сценарий истёкшей сессии, отдельного обхождения не требует.
- Сервер не меняется: `POST /api/logout` и `POST /api/login` работают как записано в `docs/protocol.md`.
- В `docs/ui.md` правило уточняется до `401 unauthenticated`: `401 invalid_credentials` — ошибка формы, на экран входа она не выбрасывала и раньше.
