--decrypt-backup у воркера, — и горячей перезагрузки тоже нет.
internal/config предоставляет три загрузчика: один для API, один для бота, один для воркера, и
каждый процесс выполняет свой загрузчик один раз, при старте. Значение, не прошедшее валидацию,
приводит к отказу сразу, а не остаётся скрытой проблемой: процесс пишет invalid configuration и
завершается с кодом 1, поэтому установка никогда не работает наполовину настроенной — с shop ID
YooKassa и без секрета или с включёнными резервными копиями и без ключа.
Следствие однократной загрузки в том, что изменение переменной требует перезапуска процесса,
который её читает. Docker Compose передаёт один и тот же файл .env сервисам api, bot и
worker, поэтому на практике один файл настраивает все три, а изменение означает повторный
docker compose up -d. Чтобы начать, скопируйте .env.example в .env; о первом запуске
см. Быстрый старт, о топологии compose —
Развёртывание.
В таблицах ниже перечислены все переменные, которые читает хоть один процесс Omniflow, включая
несколько отсутствующих в .env.example, и одну, которую .env.example задаёт, но не читает никто.
Кто читает — это процессы, чей загрузчик читает переменную; о переменной, которую читает
процесс, никогда не использующий её значение, отдельно сказано в тексте под её таблицей.
Как разбираются значения
Каждая переменная проходит через один из небольшого набора считывателей, и у каждого свой режим отказа, который стоит знать до того, как вы начнёте отлаживать настройку, которая словно бы игнорируется.Что нужно каждому процессу для запуска
Бот отказывается стартовать без Valkey, вместо того чтобы работать незащищённым: на Valkey держатся
его ограничения частоты запросов по пользователю и его защита от повторного проигрывания callback,
и обе стоят между двойным нажатием и двойным списанием. Ошибка говорит об этом прямо —
APP_VALKEY_URL is required for bot rate limits and callback replay protection.
Основное
APP_DATA_ENCRYPTION_KEY — это ключ, которым запечатываются контактные данные клиентов, TOTP-секреты
операторов, сохранённые в панели настройки и секреты OIDC, cookie процесса входа клиента, учётные
данные провайдеров цифровых товаров и отпечатки контактов реферальной программы. Сгенерируйте его
один раз:
digital goods delivery disabled с
reason: APP_DATA_ENCRYPTION_KEY is not set, а кроме того работает без обновления блок-листа и
оценки аномалий в свипере и без исполнителя массовых действий, потому что все три распечатывают
учётные данные, записанные панелью. Задайте ключ на воркере, как только начнёте продавать цифровые
товары, подпишетесь на блок-лист или воспользуетесь массовыми действиями.
База данных и кеш
Служба резервного копирования разбирает
APP_DATABASE_URL второй раз — в PGHOST, PGPORT,
PGDATABASE, PGUSER, PGPASSWORD и PGSSLMODE, — чтобы пароль попадал в pg_dump через
окружение, а не через argv дочернего процесса, где его мог бы прочитать любой локальный пользователь.
Загрузчик воркера читает APP_VALKEY_URL, а сам воркер ничего из этого не использует: ни один путь
исполнения в его коде не открывает клиент Valkey. Оставить переменную заданной безвредно, и это
позволяет держать один .env пригодным для всех трёх процессов.
Remnawave
Проба готовности API обращается к Remnawave аутентифицированным запросом, который перечисляет одного
пользователя, поэтому отозванный или истёкший токен проявляется как нездоровая зависимость
remnawave в /readyz, а не как сорвавшаяся покупка позже. Три подряд нездоровых замера по
умолчанию также открывают режим обслуживания. См. Интеграция с Remnawave.
Telegram
Длинные опросы используются по умолчанию и остаются поддерживаемым режимом для разработки и как
запасной вариант. Установка webhook-URL заставляет бота при старте вызвать
setWebhook с
allowed_updates, ограниченным значениями message, edited_message, callback_query,
pre_checkout_query, my_chat_member и chat_member. Сгенерируйте секрет командой
openssl rand -hex 32. Полная настройка, включая BotFather и домен Mini App, описана в
Интеграции с Telegram.
URL и валюта
У
APP_UPDATE_FEED_URL нет значения по умолчанию, и это сделано намеренно. Чтение ленты сообщает
тому, кто её отдаёт, что эта установка существует, примерно где она находится и какую версию
запускает, — поэтому проверку владелец включает сам, а не обнаруживает, что она уже идёт. Omniflow не
поставляет и адреса для сравнения: документация не называет репозитория, а URL, зашитый в бинарник,
был бы фактом, который в установке нечем подтвердить.
Укажите любой JSON-документ, называющий версию: эндпоинт релизов GitHub отвечает полем tag_name, а
владелец, публикующий собственный файл, может отвечать полем version. Читаются обе формы. Результат
появляется в диагностическом пакете под ключом update, и лента читается не чаще раза в шесть часов,
сколько бы раз пакет ни собирали.
Лента, которая отказала, не ответила вовремя или вернула нечитаемое, сообщает unreachable, а не «всё
актуально». Подробность — это категория, и никогда не адрес: в URL ленты может быть токен.
| APP_DEFAULT_CURRENCY | RUB | api, bot, worker | нет | нет | Валюта расчётов, в которой каталог сравнивает тарифы. Приводится к верхнему регистру, затем сверяется с ^[A-Z]{3}$; всё остальное валит запуск |
От APP_PUBLIC_URL зависят два отказа при запуске, и оба существуют потому, что альтернатива —
ссылка, ведущая в никуда: бот отказывается стартовать, когда YooKassa настроена, а публичного URL
нет (APP_PUBLIC_URL is required for YooKassa hosted checkout return links), и бот, и API
отказываются, когда APP_CUSTOMER_MAGIC_LINK_ENABLED=true, а публичный URL не задан.
Passkey операторов — третье, что от него зависит, и единственное, что при запуске не приводит к
отказу. Passkey привязан к origin, поэтому без публичного URL привязывать не к чему и функция просто
выключена: экран входа не показывает кнопку, вместо того чтобы показать кнопку, которая ничего не
открывает. Планировать нужно обратное: смена этого значения после того, как операторы
зарегистрировали ключи, делает недействительными все ключи — браузер не предложит учётные данные
сайту, для которого они не создавались. См. Безопасность.
Используйте XTR для установки, продающей только через Telegram Stars. Тариф можно купить лишь
способом оплаты, который рассчитывается в одной из валют, в которых у тарифа есть цена, поэтому
назначайте тарифу цену в XTR, чтобы продавать его через Stars, и в фиатной валюте, чтобы
продавать через CryptoBot или YooKassa. См. Каталог и промоакции.
Платёжные провайдеры
Адаптер существует только тогда, когда настроены его учётные данные, поэтому отдельного
переключателя включения нет. Пару значений YooKassa нужно задавать вместе: бот отказывается
стартовать с одним без другого (
APP_YOOKASSA_SHOP_ID and APP_YOOKASSA_SECRET must be configured together), потому что shop ID без секрета — это установка, предлагающая способ оплаты, который она
не может довести до конца.
Воркер держит те же учётные данные, что и API, потому что именно он списывает деньги за
автоматические продления: воркер без адаптера мог бы разве что записать, что продление не удалось.
Настройка каждого провайдера — в Платёжных провайдерах, а сценарий
регулярных списаний — в Регулярных платежах.
Пополнение кошелька и лимиты подписок
Суммы указываются в минорных единицах
APP_DEFAULT_CURRENCY. Пресет, который лимиты отвергли бы,
вообще не отрисовывается, поэтому клиент не может нажать кнопку, которую бэкенд затем отклонит.
Эти переменные — то, что применяют работающие процессы. При первом запуске против базы данных, где
нет строки
commerce_settings, API дополнительно заполняет эту строку из этих же значений, чтобы
установка, обновляющаяся с более старой версии, сохранила лимиты, настроенные её оператором, а не
молча взяла значения по умолчанию из схемы. Дальше строку пишет только панель
(PUT /v1/panel/settings/commerce/topup и .../subscriptions), и именно сохранённая строка
показывается в панели и в обзоре клиентского веб-кабинета — но заказы и кошелёк по-прежнему
проверяются по значениям окружения, загруженным при старте. Держите переменные заданными и
согласованными с панелью и перезапускайте процессы после изменения любой из сторон.commerce settings could not be seeded; the panel will show none yet, и API продолжает работу. См.
Пополнение кошелька и корзины и
Подписки.
Коммуникация с клиентами
Окно восстановления и контроль возраста аккаунта для пробного периода читает не только бот, но и API,
и это сделано намеренно: клиентский веб-кабинет оформляет заказ по тем же правилам, что и бот, а
пробный период, в котором чат отказывает, а браузер выдаёт, был бы средством контроля с дырой. Задавайте
их одинаково для обоих процессов — один общий
.env делает это сам. Маркетинговые ограничения и время
жизни корзины читает только бот, потому что именно он отправляет сообщения и именно он держит
сохранённые корзины. Поведение описано в Уведомлениях и согласиях.
Панель оператора, клиентская панель и сессии
Оба флага cookie по умолчанию равны
true, поэтому развёртывание, которое о них забыло, всё равно
получает cookie, которую браузер отправит только по TLS. Установка любого из них в false пишет в
лог предупреждение, называющее риск, —
admin session cookie is not marked Secure; use this only for local development.
Ссылка для входа выключена по умолчанию, потому что это вторые предъявительские учётные данные,
путешествующие через чат. Включайте её там, где нельзя воспользоваться login-виджетом Telegram —
обычно из-за того, что домен не удаётся привязать в BotFather, — и команда бота /login начинает
работать. Самому входу через Telegram отдельная переменная не нужна: он проверяет полезную нагрузку
виджета с помощью APP_TELEGRAM_TOKEN.
APP_TRUSTED_PROXIES разбирается один раз при старте и используется и операторской, и клиентской
поверхностью. Некорректный элемент — это ошибка запуска (trusted proxy entry must be an IP address or CIDR block); голый IP превращается в префикс /32 или /128. Заголовок X-Forwarded-For
подделывается тривиально, поэтому он учитывается только тогда, когда непосредственный узел-отправитель
— один из настроенных прокси, а если не настроен ни один, заголовок игнорируется полностью и
используется адрес сокета: правильное поведение по умолчанию для развёртывания, выставленного напрямую.
Полученный адрес питает ограничение частоты запросов, журнал событий безопасности клиента и журнал
аудита, поэтому важны обе стороны ошибки: не доверять никому — и все запросы за прокси делят один
адрес; доверять слишком многим — и атакующий сам выбирает себе адрес. Для мостовой сети Docker за
обратным прокси обычное значение — 172.16.0.0/12. См.
Обратный прокси, Панель администратора
и Клиентский веб-кабинет.
Уведомления операторам и поверхность /ops
Если
APP_OPERATOR_CHAT_ID остаётся равным 0, нотификатор пишет operator notifications are disabled because no group is configured, а постановка в очередь становится пустой операцией, так
что уведомления даже не сохраняются. Команда /ops регистрируется безусловно и просто ничего не
отвечает Telegram-аккаунту, которого нет в списке, поэтому существование команды никогда не
подтверждается клиенту; кроме того, она отвечает только в личном чате. См.
Уведомления операторам.
Резервные копии
Образы бота и воркера собираются с включёнными клиентскими утилитами PostgreSQL, и оба монтируют
один и тот же том
backup-data в /var/lib/omniflow/backups, поэтому под compose значения по
умолчанию работают без изменений. Тот же ключ читается и напрямую — до всякой загрузки конфигурации —
командой worker --decrypt-backup, которая превращает зашифрованный дамп обратно во входные данные
для pg_restore, не используя ничего, кроме бинарника, ключа и файла.
См. Резервное копирование и восстановление.
Обнаружение режима обслуживания
Действует по ним только API. Это процесс, который и без того опрашивает каждую зависимость для
готовности, поэтому именно он ведёт контроллер; загрузчик воркера разбирает те же четыре значения и
не использует ни одного. Серии существуют, чтобы один таймаут не мог закрыть установку, а окно,
открытое оператором вручную, никогда не снимается автоматически. Автоматическое обнаружение следит
только за
postgres и remnawave — отказ Valkey виден в /readyz и сам по себе режим обслуживания
никогда не открывает. См. Режим обслуживания.
Сроки хранения и вложения поддержки
Хранение убирает только расходуемые операционные данные. История финансов, аудита, согласий,
идентичности и прав на услуги пишется только на добавление и живёт столько же, сколько установка;
подчистка её никогда не удаляет. Хранение выполняет только воркер, поэтому установка, где запущены
лишь API и бот, никогда не убирает за собой.
Окно outbox сейчас не удаляет ничего. Omniflow пишет строки
outbox_events для order.paid,
wallet.credited и addon.purchased, но ни один процесс не помечает их опубликованными, а запрос
на удаление совпадает только с опубликованными строками. Ожидайте, что outbox будет расти, а
GET /v1/admin/outbox — показывать растущее число ожидающих; см.
Задания и очереди.
Вложений, загруженных через Telegram, это не касается: их байты остаются у Telegram, а Omniflow
хранит только ссылку. См. Служба поддержки.
Анонимная телеметрия
Телеметрия включена по умолчанию, и отправляет её только API — ни бот, ни воркер клиента не создают.
APP_TELEMETRY_ENABLED=false предотвращает все сетевые запросы телеметрии, а при старте API пишет
либо anonymous telemetry enabled с disable_with=APP_TELEMETRY_ENABLED=false, либо
anonymous telemetry disabled.
Предпросмотр телеметрии в панели, в разделе Настройки, читает сохранённый документ настроек, а
не работающую конфигурацию, поэтому установка по умолчанию показывает предпросмотр как
отключённый, хотя API на самом деле отправляет heartbeat. Считайте
APP_TELEMETRY_ENABLED ответом
на вопрос, включена ли телеметрия.Операционные эндпоинты
Три детали расходятся с тем, что подсказывают имена переменных, и каждая уже ловила операторов:
- Бот игнорирует
APP_METRICS_ENABLED. Он создаёт реестр метрик безусловно и обслуживает/metricsвсякий раз, когда заданAPP_BOT_METRICS_ADDR. APP_BOT_METRICS_ADDRигнорируется, когда совпадает сAPP_BOT_HTTP_ADDR. Отдавать метрики и готовность на смотрящем в интернет приёмнике webhook значило бы публиковать внутренности установки на хосте, который должен быть доступен откуда угодно, поэтому бот вместо этого отказывается.- Очистка
APP_WORKER_HTTP_ADDRне отключает приёмник воркера. Пустое значение считается незаданным и откатывается на:8081. Вместо этого держите порт вне публичной сети средствами прокси или файрвола.
APP_BOT_METRICS_ADDR по умолчанию не задан.
Задайте его, если хотите /readyz на всех трёх процессах. Оба поставляемых примера обратного прокси
возвращают 404 для /metrics и /readyz на публичном хосте API. См.
Наблюдаемость.
Трассировка
Omniflow инструментирует входящий HTTP, обновления Telegram, задания исполнения заказов, запросы к PostgreSQL и исходящий HTTP спанами OpenTelemetry, но ни один процесс Omniflow не устанавливает провайдер трассировки, и ни одна переменнаяOTEL_* нигде в коде не читается. Когда провайдер не
установлен, глобальный провайдер является пустышкой, поэтому установка OTEL_EXPORTER_OTLP_ENDPOINT
или любой другой стандартной переменной SDK сегодня ничего не меняет. Настройки, которая включает
трассировку, не существует. Поддерживаемые операционные сигналы — метрики и структурированные логи.
Веб-приложение
Приложение Next.js вapps/web читает собственные переменные. Они задаются на сервисе web в
compose.yaml, а не в .env.
Запросы из браузера по умолчанию идут на тот же origin; развёртывание с другим origin задаёт базовый
URL клиента явно, а не через переменную окружения.
Настройки, которые панель хранит, но не применяет
Панель оператора пишет документinstallation_settings для каждого раздела — бренд, Remnawave, бот,
группа операторов, обслуживание, уведомления, телеметрия, резервные копии, политика сессий. Эти
документы читаются обратно только собственным экраном настроек панели и предпросмотром телеметрии.
Ни один работающий процесс не читает документ раздела или его запечатанные секреты, поэтому
сохранение документа не меняет никакого поведения: URL и токен Remnawave, токен бота, группа
операторов, политика обслуживания, включённость телеметрии и расписание резервных копий берутся из
переменных окружения выше, прочитанных один раз при старте. Считайте .env источником истины, а
экран настроек — записью намерений, пока это не изменится. Экран и эту границу описывают
Настройки установки.
Секреты
Эти значения являются учётными данными, и обращаться с ними следует соответственно:APP_DATABASE_URL (содержит пароль), APP_REMNAWAVE_TOKEN, APP_OPERATOR_TOKEN,
APP_TELEGRAM_TOKEN, APP_TELEGRAM_WEBHOOK_SECRET, APP_CRYPTOBOT_TOKEN, APP_YOOKASSA_SECRET и
два AES-ключа APP_DATA_ENCRYPTION_KEY и APP_BACKUP_ENCRYPTION_KEY. Никогда не коммитьте .env
или боевые учётные данные; репозиторий гоняет в CI сканер секретов именно ради этого.
Механизм вычистки логов Omniflow вырезает токены ботов, bearer- и basic-учётные данные, похожие на карточные
последовательности цифр, идентификаторы устройств и любые URL из всего, что может попасть в строку
лога, в уведомление оператору или в диагностический пакет, — на заявленном размене: чрезмерно
вычищенная строка лога это мелкое неудобство, а утёкшая ссылка на подписку или токен бота это
инцидент безопасности. Это защищает от случайностей, а не от конфигурации: токен, вставленный не в
ту переменную, остаётся токеном, который оператор может прочитать из окружения процесса. Используйте
TLS для внешних подключений к PostgreSQL, Valkey, Remnawave и платёжным провайдерам. Вся картина
описана в Безопасности.