internal/remnawave, который предоставляет ровно
тринадцать операций, а всё остальное в кодовой базе принимает remnawave.Service, remnawave.Provisioner или
remnawave.Importer как интерфейс.
Чем владеет Remnawave
Чем владеет Omniflow
Единственная связь между системами
Системы соединяет один столбец:subscriptions.remnawave_user_id, обнуляемый bigint под уникальным
частичным индексом, поэтому две подписки Omniflow никогда не смогут претендовать на одного пользователя
Remnawave. Больше ничего об удалённом пользователе не дублируется.
Удалённое имя пользователя вычисляется, а не хранится: omniflow_ плюс первые шестнадцать шестнадцатеричных
символов UUID подписки. Поскольку оно детерминированное, операция create, выполняемая после сбоя, находит уже
созданного пользователя вместо того, чтобы создать второго: воркер ищет кандидата по имени перед созданием, а
установки, обновлённые с более ранней версии, дополнительно пробуют старое имя, производное от клиента, чтобы
существующий пользователь Remnawave был усыновлён, а не продублирован.
Каждая запись в Remnawave — это сначала запись в Omniflow: строка fulfillment_operations с желаемым
состоянием, ключом идемпотентности и идентификатором корреляции, применяемая надёжной задачей. Клик в панели,
который сам записал бы в Remnawave, был бы тем единственным изменением, которое сверка не смогла бы объяснить.
Что Omniflow намеренно не хранит
- Ссылку доступа. Столбца для неё в
database/schema.sqlнет. Она запрашивается из Remnawave на каждый запрос, а ответы, которые её несут, отправляются сCache-Control: no-storeиReferrer-Policy: no-referrer, потому что ссылка — это предъявительский ключ доступа: у кого она есть, у того и доступ клиента. Единственное средство после утечки — ротация, и она закрыта явнымconfirm: trueплюс недавним входом в систему. - Идентификаторы устройств. Ни одна панель никогда не получает HWID. Устройство адресуется дайджестом —
панель оператора использует
sha256(hwid), усечённый до шестнадцати шестнадцатеричных символов, панель клиента — собственную ручку, — а при удалении дайджест разрешается обратно на сервере: перечисляются устройства клиента и ищется совпадение. Ценой одного дополнительного вызова Remnawave покупается то, что идентификатор никогда не попадает в браузер. - Идентификаторы сквадов в истории. Строка
fulfillment_historyзаписываетendsAt, факт настройки лимита трафика и лимита устройств и количество сквадов — никогда идентификаторы сквадов, ссылки или тела ответов провайдера. - Полезные нагрузки провайдеров в операторских представлениях. Сырое тело вебхука не возвращается ни
GET /v1/admin/webhooks, ни экраном вебхуков в панели: это собственная полезная нагрузка провайдера, и она может содержать платёжные данные. Списки задач резюмируют аргументы и редактируют последнюю ошибку. - Ничего идентифицирующего в телеметрии. Анонимный heartbeat несёт номер схемы, UUID установки, имя
события, версию сборки, имя сервиса,
GOOS,GOARCHи метку времени. Идентификаторы клиентов, Telegram ID, IP-адреса, имена хостов, домены, URL, суммы, валюты, показатели трафика, ссылки доступа, токены, содержимое сообщений, названия тарифов и произвольный текст не собираются никогда. См. Телеметрия.
Что ломается при нарушении границы
Правка Remnawave напрямую. Ничто не мешает оператору открыть панель Remnawave и вручную изменить срок действия, лимит устройств или набор сквадов. Omniflow не считает это авторитетным: следующий проходreconcile читает удалённого пользователя, сравнивает его с правом на услугу, записывает разницу и возвращает
намерение Omniflow. Ручная правка откатывается, и причина в том, что право на услугу — это то, за что заплатил
клиент.
Удаление пользователя Remnawave. Проход сверки записывает расхождение missing_remote, а не молча
воссоздаёт состояние, и последующее применение создаёт пользователя заново под детерминированным именем. Ничто
не тарифицируется дважды и ничто не теряется.
Считать Omniflow авторитетным по актуальному потреблению. Трафик и количество устройств читаются вживую.
Когда Remnawave не отвечает, обзор для клиента деградирует — каждая подписка помечается live: false, а
полезная нагрузка degraded, — вместо того чтобы показывать устаревшую цифру как актуальную.
Доверять статусу одной системы применительно к другой. Блокировка клиента в панели оператора записывает
событие жизненного цикла Omniflow и строку аудита; она не ставит в очередь изменение выдачи доступа. Панель
объясняет почему: блокировка — это не удаление, а жизненный цикл права на услугу принадлежит конвейеру
исполнения, а не клику в панели. Чтобы прекратить доступ сейчас, выполните операцию подписки disable, которая
проходит через тот же конвейер, что и всё остальное.
Расхождения: запись о несогласии
Операцияreconcile сравнивает пользователя Remnawave с правом на услугу по сроку действия, лимиту трафика,
лимиту устройств, набору Internal Squads и статусу и записывает каждое несовпадение как строку
entitlement_drifts с JSON expected и observed. На практике встречаются виды missing_remote, expiry,
traffic, device_limit, squads и status. Расхождение всегда оценивается относительно того пользователя
Remnawave, которым владеет данная подписка, поэтому вторая подписка клиента никогда не оценивается по
удалённому состоянию первой.
Операторы смотрят их в System → Drift в /admin (GET /v1/panel/system/drift, право доступа
system.read) или через GET /v1/admin/fulfillment/drifts?limit=<1..500> на токенном интерфейсе.
Обнаружение и исправление происходят в одной транзакции, поэтому расхождение, открытое проходом сверки,
который затем завершился успешно, закрывается через микросекунды. Поэтому экран расхождений — это вид на те
несогласия, которые корректирующая работа сейчас не может устранить, а не общий журнал всего, что когда-либо
менялось в Remnawave. Вид
external_edit присутствует в ограничении CHECK таблицы и в перечислении API,
но его ничто не записывает: изменённый извне пользователь фиксируется как расхождение по конкретному полю.APP_RETENTION_DRIFT_DAYS (по умолчанию 90). Права на услуги и история исполнения не удаляются никогда.
Деньги и доступ отказывают независимо
Состояние платежа, состояние права на услугу и состояние Remnawave — три разные вещи, которые отказывают по отдельности, и схема намеренно держит их раздельно. Успешный платёж переживает недоступность Remnawave: деньги записаны, право на услугу записано, а выдача доступа повторяется. Ничто не переписывает финансовую историю под сбой выдачи доступа. Режим обслуживания проведён по той же линии. Он блокирует новые покупки и удерживает исходящую выдачу доступа и никогда не трогает уже взятые деньги — расчёты, возвраты и история заказов продолжают работать всё это время. Удержанная задача исполнения не проваливается, а откладывается: операция остаётся вretrying с
last_error_code = maintenance_active, счётчик её попыток возвращается назад, чтобы удержание не стоило
бюджета повторов, а задача откладывается на одну минуту. См.
Режим обслуживания.
Внутри Omniflow: надёжное, эфемерное и только на добавление
PostgreSQL — единственный надёжный источник истины. Valkey хранит счётчики ограничения частоты запросов (omniflow:rate:<scope>:<subject>), одноразовые токены защиты от повтора (omniflow:once:<scope>:<token>),
блокировки и кеш. Потеря Valkey стоит этих счётчиков и больше ничего — именно поэтому стек compose запускает
его с полностью отключённым сохранением на диск.
Финансовые записи, записи аудита, идентичности, вебхуков и outbox доступны только на добавление, если
задокументированный жизненный цикл явно не разрешает изменение. Хранение отражает это: ежечасный проход удаляет
истёкшие сессии бота, брошенные сессии оформления заказа, устаревшие строки приёма вебхуков, истёкшие вложения
поддержки, опубликованные события outbox, старые события телеметрии и разрешённые строки расхождений. Он
никогда не трогает финансовую историю, историю аудита, согласий, идентичности и прав на услуги, которая
хранится всё время жизни установки.
Промокод никогда не удаляется, а только деактивируется, потому что ссылающиеся на него погашения — финансовые
записи. Отозванный способ оплаты сохраняет свою строку, чтобы историческое списание по-прежнему разрешалось в
тот способ, которым оно было сделано. Удаление клиентского OIDC-провайдера сохраняет связанные идентичности,
потому что их удаление молча лишило бы способа входа тех клиентов, которые им пользуются.
Где конфигурация авторитетна
Одну границу стоит назвать прямо, потому что она выглядит как владение, но им не является: панель оператора может сохранять настройки установки, но работающие процессы их не читают. Строкиinstallation_settings создаются пустыми и читаются ровно двумя вызывающими — самим экраном настроек и
предпросмотром телеметрии. URL и токен Remnawave, токен бота, группа операторов, политика режима обслуживания,
включение телеметрии и расписание резервного копирования читаются из переменных окружения один раз при старте
процесса. Сохранение раздела меняет то, что показывает панель, а не то, что делает процесс, и ротация токена
Remnawave означает правку окружения и перезапуск. Проверки соединения с Remnawave тоже нет:
POST /v1/panel/settings/providers/{provider}/test проверяет только платёжные адаптеры и отвечает
422 provider_not_loaded на любое другое имя.
Настройки установки описывают, какие разделы существуют и на что
каждый из них влияет сегодня.
Смежные материалы
Интеграция с Remnawave
Адаптер, каждый его вызов, поддерживаемая версия и состояние здоровья.
Исполнение заказов
Операции выдачи доступа, повторы, сверка и устранение расхождений.
Подписки
Права на услуги, параллельность, апгрейды, льготный период и истечение.
Безопасность
Учётные данные, сессии, секреты, транспорт и границы приватности.