Files
transcriber/docs/conventions/config.md
T
av c44f0e7582 HTTP API закрыт за вход через OIDC у Authelia
- шаг схемы закрывает поверхность, которую хранилище приносит открытой:
  собственную регистрацию, вход по паролю и одноразовый код — без этого
  закрытие приёма обходилось двумя запросами
- продление сессии выключено, срок семь суток: иначе отзыв доступа у
  провайдера до сервиса не доходит никогда
- файл записи отдаётся вошедшему по токену файла — пересмотр
  ADR-2026-08-12-file-link-open-but-not-logged
2026-08-12 17:44:22 +03:00

137 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Конфигурация
Конвенция: *как* устроена и грузится конфигурация transcriber (TOML).
Правила оформления кода (How), не спецификация поведения.
**Взято из проекта jellybit.** Расхождения с сегодняшним кодом названы по месту.
Главные: образец называется `config.dist.toml`, а не `config.example.toml`;
комментариями снабжена половина полей; валидации на старте нет вовсе, кроме
проверки пустых ключей внутри адаптеров.
**Механизировано:** ничего. Запрет `os.Getenv` для конфигурации правилом линтера
не выражен, и `godotenv` в `main.go` загружает `.env` — то есть окружение сейчас
участвует.
## Принципы
- **Конфигурация — только TOML.** Переменные окружения для конфигурации **не
используем**: окружение наследуется дочерними процессами и видно через
`/proc/<pid>/environ` — для секретов это слабее файла под `0600`.
*Расхождение:* `main.go` зовёт `godotenv.Load()` и молча продолжает без файла.
- Грузим **один раз при старте** в одну типизированную структуру `Config`
(под-структуры по секциям). Дальше по коду читаем только её — чтения файла в
прикладном коде нет, только загрузчик `internal/config`.
- Конфиг **неизменяем** после старта; смена параметров — перезапуск процесса.
## Файл и поиск
- Имя конфига по умолчанию — **`config.toml`**, ищется в **рабочем каталоге**
процесса.
- Путь переопределяется опцией **`-c path`** или **`--config=path`**.
- Образец в репозитории — **`config.dist.toml`** (см. ниже); реальный
`config.toml` не коммитится.
## config.dist.toml — самодокументируемый образец
`config.dist.toml` коммитим как единый справочник по конфигу: все секции и все
поля. **Каждое поле снабжаем комментарием**, из которого ясно:
- **зачем** поле — что оно меняет в поведении;
- **допустимые значения** — перечисление или границы;
- **единицы измерения**, если применимы — секунды, байты, доля `01`.
```toml
[server]
port = <N> # порт HTTP-сервера
shutdown_timeout = <N> # ждать мягкой остановки сервера, секунды
force_shutdown_timeout = <N> # ждать остановки воркеров, секунды
users_while_list = ["<@name>"] # кому отвечает бот; строка автора Telegram
```
Значения намеренно заменены плейсхолдерами: предмет конвенции — форма
комментария, а числа, совпадающие с фактом до цифры, от факта неотличимы и
начинают врать молча при смене умолчания. Действующие умолчания и их смысл живут
одним домом — таблица «Настройки с числовым значением» в
[../database.md](../database.md); `config.dist.toml` — источник истины по составу
полей.
Секретные поля оставляем пустыми — значение приходит из выкладки (см. «Секреты»).
*Расхождение:* секции `[server]` в `config.dist.toml` не хватает поля
`users_while_list`, из-за чего бот на свежем конфиге отвечает отказом всем.
*Расхождение:* адреса провайдера в секции `[auth]` образца заполнены примерами
вида `https://auth.example.com/...`, а не оставлены пустыми: пустой адрес не
говорит, какой формы значение здесь ждут. Пустым оставлен только
`client_secret` — он и есть секрет.
## Поля по дискриминатору `type`
Когда набор полей секции зависит от поля-дискриминатора `type` (выбор одного из
бекендов или внешних сервисов), обязательность и опциональность полей определяет
значение `type`, а не фиксированный список секции.
- **Проверка — по `type`.** Для каждого поддерживаемого значения свой набор
обязательных полей; поля других значений не требуются. Неизвестное значение —
ошибка на старте с перечислением поддерживаемых.
- **Образец — по `type`.** В `config.dist.toml`:
- основное (умолчательное) значение **предзаполнено** рабочими значениями;
- альтернативные — **блоками-комментариями ниже**, каждый со своим описанием
полей (зачем, границы, единицы — как у обычных полей);
- так из примера видны все варианты и поля каждого, не открывая код.
Дискриминатора в transcriber пока нет; правило записано на случай второго
распознавателя.
## Секреты
Секреты доставляет **выкладка**, рендеря их прямо в `config.toml` (transcriber:
Ansible из `pet-project-server`). Приложение просто читает TOML — отдельного слоя
секретов в коде нет. Источник истины секрета — внешнее хранилище выкладки, не
репозиторий и не окружение.
- Секретные поля transcriber: `telegram.bot_token`, `yandex.speech_kit_api_key`,
`yandex.object_storage_access_key_id`,
`yandex.object_storage_secret_access_key`, `auth.client_secret`.
- Отрендеренный `config.toml` (с секретами) **не коммитится**; права `0600`,
владелец — пользователь процесса (`1000:1000`).
- В `config.dist.toml` секретные поля — пустые строки.
*Расхождение:* сейчас там стоят подсказки вида `your_..._here`, а не пустые
строки, и загрузчик их не отличает от настоящего значения.
- Загрузчик на старте проверяет, что обязательные секреты не пусты (ловит криво
отрендеренный файл) — см. «Проверка и остановка на старте».
- В логи секреты не попадают — см. [logging.md](logging.md), «Безопасность».
## Проверка и остановка на старте
Конфиг проверяем **на старте, до приёма трафика**. Негодный конфиг — лог `ERROR`
и выход с ненулевым кодом, не стартуем наполовину.
Что проверяем:
- обязательные поля заданы;
- каталоги хранилища существуют и доступны на запись;
- границы числовых полей соблюдены;
- ключи внешних сервисов не пусты.
*Расхождение:* `LoadConfig` проверяет только существование файла и разбирает
TOML. Пустой токен бота ловится в `NewTelegramController` уже после старта, и
приложение продолжает работу без бота; пустые ключи Yandex ловятся в
конструкторе распознавателя, и вот там процесс уже выходит с кодом 1. Единого
места проверки нет.
Секция `[auth]` — первая, у которой проверка своя и стоит на старте:
`AuthConfig.Validate()` зовётся из `main.go` сразу после загрузки и роняет
процесс с перечнем незаполненных ключей. Причина в цене умолчания: поднявшись с
молча выключенным входом, сервис остался бы открытым наружу, а узнать об этом
было бы неоткуда. Сообщение называет **имена ключей**, а не значения — значение
`client_secret` в журнал попасть не должно.
## Структура в коде
- Весь разбор и проверка — в `internal/config`; наружу отдаётся готовая `Config`.
- Одна корневая структура `Config` с под-структурами по секциям (`Server`,
`Database`, `Storage`, `Yandex`, `Telegram`).
- Умолчания задаются в `defaultConfig()`, файл их перекрывает. Новое поле
требует правки обоих мест.