Skip to main content
Omniflow и Remnawave обе хранят состояние об одних и тех же клиентах, и ровно одна из них права по каждому вопросу. Remnawave авторитетна для пользователей VPN, счётчиков трафика, подключённых устройств, ссылок доступа, узлов, хостов и Internal Squads. Omniflow авторитетна для идентичности, биллинга, тарифов, заказов, платежей, возвратов, проводок кошелька, прав на услуги, поддержки, маркетинга, реферальной программы, лояльности, RBAC и истории аудита. Причина провести эту черту и держаться её в том, что две системы, каждая из которых считает состояние VPN своим, порождают класс ошибок, который никто не может разобрать: клиент, чья подписка активна в одном месте и отключена в другом, и нет способа понять, что правда. Граница делает вопрос разрешимым. Ответ Remnawave — правда о доступе; ответ Omniflow — правда о деньгах и намерении. Это также означает, что оператор может обновить Remnawave, а у Omniflow не будет мнения о её схеме. Граница структурная, а не декларативная. Omniflow никогда не пишет в базу данных Remnawave и никогда не управляет Xray. Каждый вызов идёт через HTTP-адаптер в 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

Адаптер, каждый его вызов, поддерживаемая версия и состояние здоровья.

Исполнение заказов

Операции выдачи доступа, повторы, сверка и устранение расхождений.

Подписки

Права на услуги, параллельность, апгрейды, льготный период и истечение.

Безопасность

Учётные данные, сессии, секреты, транспорт и границы приватности.