config: включение Telegram разведено с ключом доступа

- в секции [telegram] заведён обязательный ключ enabled: умолчания у него нет,
  файл без него негоден; bot_token стал только ключом доступа и при
  enabled = false не читается вовсе, а пустой при enabled = true роняет старт
- выключенный вход даёт подъём одним входом без единого обращения к Telegram и
  записью INFO вместо прежнего WARN: это выбор владельца, а не отклонение
- отказ разбора файла настроек больше не пересказывает toml — её ParseError
  несёт в тексте разбираемое значение, и оборванная строка секретного ключа
  уносила его в журнал; теперь называются путь, строка, столбец и последний ключ
This commit is contained in:
av
2026-08-13 21:46:14 +03:00
parent 903941f587
commit cd57b68215
27 changed files with 1536 additions and 123 deletions
+29 -5
View File
@@ -102,6 +102,17 @@ Ansible из `pet-project-server`). Приложение просто читае
- Загрузчик на старте проверяет, что обязательные секреты не пусты (ловит криво
отрендеренный файл) — см. «Проверка и остановка на старте».
- В логи секреты не попадают — см. [logging.md](logging.md), «Безопасность».
- **Отказ загрузки настроек не несёт содержимого файла.** Текст такого отказа
собирает библиотека разбора, и собирает она его из разбираемого куска:
`toml.ParseError` кладёт в сообщение само значение («Invalid float value: %q»).
Оборванная кавычка в строке секретного ключа — типовая поломка криво
отрендеренного шаблона выкладки — уносит ключ в журнал контейнера целиком, а
инвариант «секрет не покидает конфиг» помечен необратимым. Поэтому отказ
разбора пересобирается своими словами: путь, строка, столбец и последний ключ,
без сообщения библиотеки. Прочие отказы декодера (несовпадение типов,
неподдерживаемый тип) собраны из имён ключей и типов, значений в них нет, и их
текст остаётся как есть — иначе за разборчивость отказа платили бы там, где
платить не за что.
## Проверка и остановка на старте
@@ -119,11 +130,16 @@ Ansible из `pet-project-server`). Приложение просто читае
TOML. Пустые ключи Yandex ловятся в конструкторе распознавателя, и там процесс
выходит с кодом 1. Единого места проверки нет.
Токен бота под это расхождение больше не подпадает: он судится при сборке
клиента, до подъёма сервера, и разрез у него объявленный — пустое значение
означает отказ от входа и даёт подъём без Telegram, непустое негодное роняет
старт как ошибка настройки. Нормирует это `openspec/specs/intake`, «Недоступный
или незаданный вход Telegram не мешает подъёму».
Под это расхождение больше не подпадают два ключа секции `[telegram]` — признак
включения и ключ доступа, — и проверок у них две. Третий ключ секции,
`update_timeout`, границ по-прежнему не проверяет никто, и ноль в нём обращает
длинный опрос в непрерывный. Обязательность признака включения судит загрузчик — только разбор отличает
«ключ не задан» от «ключ задан ложным», потому что нулевое значение `bool` у
обоих одинаковое. Заполненность ключа доступа судит `TelegramConfig.Validate()` из
`main.go`, рядом с проверкой `[auth]`: пустой `bot_token` при `enabled = true`
ошибка настройки и отказ старта. Непустой негодный по-прежнему судится при сборке
клиента, до подъёма сервера. Нормирует это `openspec/specs/intake`, «Признак
включения решает, поднимается ли вход Telegram».
Секция `[auth]` — первая, у которой проверка своя и стоит на старте:
`AuthConfig.Validate()` зовётся из `main.go` сразу после загрузки и роняет
@@ -142,3 +158,11 @@ TOML. Пустые ключи Yandex ловятся в конструкторе
([ADR](../adr/ADR-2026-08-12-single-data-dir-config-key.md)).
- Умолчания задаются в `defaultConfig()`, файл их перекрывает. Новое поле
требует правки обоих мест.
- **Обязательное поле — поле, у которого умолчания нет намеренно.** Умолчание у
такого поля было бы угаданным намерением, и одна из двух ошибок стала бы
тихой. Форма записи: умолчания нет ни в `defaultConfig()` (причина — строкой
комментария у самого поля), ни по нулевому значению типа; присутствие ключа
судит **разбор**`MetaData.IsDefined` из `toml.DecodeFile`, — потому что
значение отличить «не задано» от «задано нулём» не позволяет. В
`config.dist.toml` у поля стоит значение свежей установки. Первое такое поле —
`telegram.enabled`.