Управление приложением

Полная спецификация управления приложением IMSEL VPN через подписку — по схеме, совместимой с документацией Incy/Happ. Клиент читает HTTP-заголовки ответа подписки и fallback-строки #param в теле и применяет их к настройкам и поведению.

Реализация: приём метаданных — lib/config/header_receive_config.dart; команды управления — lib/config/subscription_commands.dart; применение — Subscription.applyMeta/applyCommands + SubscriptionService; фоновое применение — WorkManager-воркер.

Как отправляет запросы клиент

Заголовок запросаЗначение
User-AgentIMSEL/<версия>/<ОС>/<hwid> — например, IMSEL/1.0.0/Android/17839452147361875676 (может быть подменён командой change-user-agent; до инициализации — fallback v2rayNG/1.8.5 для совместимости с панелями)
X-HWIDидентификатор устройства — отправляется всегда, отключения нет
Cookieтот же идентификатор устройства (HWID), что и в X-HWID — дублирование, как у Happ (cookie: <hwid>)
x-device-localeязык устройства (ru, en, …)
x-device-osплатформа (Android, Windows)
x-ver-osверсия ОС (14, 10.0.22631, …)
x-device-modelмодель устройства (23117RA68G, имя ПК на Windows; пустая — заголовок не отправляется)
Accept / Connection*/* / close

change-user-agent заменяет только User-Agent — заголовки x-device-* и X-HWID продолжают отправляться. При включённом VPN запросы идут через локальный HTTP-прокси ядра; Domain Fronting подписочных URL — см. Подписка через домен-фронтинг.

Способы передачи параметров

1. HTTP-заголовок ответа (приоритетный):

HTTP/1.1 200 OK
profile-title: Мой VPN
profile-update-interval: 6

2. Строка в теле подписки (fallback):

#profile-title: Мой VPN
#profile-update-interval: 6
vless://...

Правила:

  • Приоритет: заголовок > #param-строка. Немаркированные строки тела игнорируются.
  • Boolean (тринарная семантика): true/1 → включить; любое другое непустое значение (0, false) → выключить; отсутствие параметра → сервер не управляет, локальное значение не трогается.
  • Кодирование: текстовые значения поддерживают префикс base64: (UTF-8).
  • Имена телесных строк принимаются в вариантах with-dash и withoutdash (#profile-title: и #profiletitle:).
  • Применение — молчаливое, без подтверждения пользователя, при каждом успешном обновлении подписки (в UI и в фоне).
  • URL-значения принимаются только со схемой http:///https:// (включая фронтинг-URL); невалидные значения молча отбрасываются.

Данные подписки

Имя подписки

profile-title: Мой VPN
profile-title: base64:0JzQvtC5INCy0YHQsNC5

До 25 символов (обрезается). В base64-варианте первая строка — имя, остальные — описание. Альтернативные заголовки: subscription-name, content-disposition (также распознаётся из заголовка-строки).

Описание подписки

profile-description: Быстрые серверы в Европе
profile-description: base64:0JHRgtC40L/QsNGG0LjRjw==

Показывается мелким текстом в шапке карточки. Имеет приоритет над описанием из base64-profile-title.

Статус (трафик и срок)

subscription-userinfo: upload=1073741824;download=10737418240;total=107374182400;expire=1700000000

Трафик в байтах. expire — unix-время в секундах; значения больше 32 000 000 000 трактуются как миллисекунды и конвертируются. total=0 — безлимит (∞). Прогресс-бар: зелёный → оранжевый (≥70%) → красный (≥90%).

Интервал автообновления

profile-update-interval: 6

Часы. > 0 — включить автообновление с этим интервалом (ограничивается диапазоном 1–168 ч); 0 — выключить; отсутствие — не управлять. Управляет и фоновыми WorkManager-задачами.

Ссылки

support-url: https://t.me/your_support_bot
profile-web-page-url: https://your-site.com
premium-url: https://example.com/pricing

Кнопки в карточке подписки: поддержка (✈), веб-страница (ℹ), «Премиум» (★, только при наличии premium-url).

Объявление

announce: [текст | base64:...]
announce-url: https://example.com/news.txt

Текст объявления (до 200 символов, обрезается с «…») в рамке в карточке. announce (текст напрямую) имеет приоритет над announce-url: клиент скачивает текст по ссылке (тело — plain или base64:..., тот же таймаут и User-Agent, ошибки молча игнорируются). Устаревший sub-info (текст) работает как alias announce.

Сортировка серверов

subscriptions-sort-type: [without | ping | alphabet]

Собственное расширение IMSEL: сортировка серверов внутри подписки.


Зеркала подписки

Первый URL — без метки, каждый следующий — с меткой url:N=, в конце может стоять fallback-url=:

https://gmail.com/sub/token#m1?resolve-address=gmail.com&host=storage.googleapis.com
  |url:1=https://www.google.com/sub/token#m2?resolve-address=www.google.com&host=storage.googleapis.com
  |url:2=https://fcm.googleapis.com/sub/token#m3?resolve-address=fcm.googleapis.com&host=storage.googleapis.com
  |fallback-url=https://backup.example.com/token
  • Позиции: первый URL = позиция 0, url:1 = второй URL (позиция 1), url:N = позиция N. Диапазон меток: 0–99.
  • Разделители: | или перевод строки.
  • Каждый URL — обычный или фронтинг-URL.
  • Немаркированный список (просто URL через |/\n) тоже принимается — зеркала по порядку.
  • Зеркала пробуются по порядку до первого успешного ответа; во время загрузки top-баннер показывает позицию (2/4) и секунды до таймаута.

Управление подпиской

Смена URL (new-url)

Три режима работы по значению:

new-url: https://new-first.com/token

Одиночный URL — замена только первого зеркала (позиция 0), остальные не трогаются.

new-url: https://a.com/token|https://b.com/token|https://c.com/token

Несколько plain URL без меток — полная замена списка (легаси-режим).

new-url: https://first.com/t|url:1=https://second.com/t|fallback-url=https://fb.com/t

Помеченная структура — точечно по позициям: упомянутые позиции заменяются, fallback-url= записывается в поле запасного адреса, неупомянутые зеркала не меняются. Все URL проходят валидацию схемы; невалидные части отбрасываются.

Применяется начиная со следующего обновления; фоновая задача WorkManager автоматически привязывается к новому URL.

Точечные операции с зеркалами (new-url-N)

Заголовок new-url-N (N — позиция: 0 — первый URL, 1url:1, …) или телесная строка #new-url:N=значение (колон-форма; при нескольких командах используйте каноническую форму с дефисом — одноимённые #new-url: строки перекрывают друг друга):

КомандаДействие
new-url-1: https://drive.google.com/tok#m?resolve-address=drive.google.com&host=storage.googleapis.comзаменить зеркало url:1 целиком
new-url-3: https://append.com/tokenN = количество зеркал → добавить в конец
new-url-1: 0 (или false)удалить зеркало url:1
new-url-1: resolve-address=142.251.41.165точечно заменить только resolve-address — домен или IP-адрес
new-url-0: host=storage.googleapis.comточечно заменить только host (Host-заголовок фронтинга)

Правила:

  • Патч параметра: существующее значение заменяется, отсутствующее — дописывается в фрагмент URL.
  • N > количества зеркал («дыра») игнорируется.
  • Операция, после которой список опустел бы полностью, отклоняется целиком.
  • Порядок применения в одном ответе: new-url → точечные замены/удаления → патчи параметров → домены.

Смена домена (new-domain)

new-domain: new-domain.com
new-domain-1: drive.google.com
new-domain: 1=drive.google.com

new-domain — домен (host) во всех зеркалах; new-domain-N / колон-форма — только у зеркала на позиции N. Путь, query и фрагмент сохраняются.

Синхронизация фронтинга: если у зеркала resolve-address совпадает со старым доменом — заменяется вместе с host (фронт и резолв согласованы). host= не трогается никогда; resolve-address, не равный старому домену (например, IP), — тоже.

Запасные адреса (fallback-url)

fallback-url: https://backup-domain.com/token
fallback-url: https://f1.com/token#m?resolve-address=f1.com&host=storage.googleapis.com|https://f2.com/token

Пробуются по очереди после отказа всех зеркал (HTTP 300–599 или таймаут; таймаут — персональный настройки подписки либо глобальная, 5–15 с). Несколько URL через |/\n; фронтинг-URL разрешены. Встроенный fallback-url= в конце помеченного списка имеет тот же эффект и пробуется первым. Fallback не заменяет список зеркал.

User-Agent (change-user-agent)

change-user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)

Подменяет User-Agent для всех запросов этой подписки: зеркала, fallback и скачивание announce-url. Применяется начиная со следующего обновления.

Блокировка настроек (hide-settings)

hide-settings: 1
hide-settings: 0

1 — настройки подписки становятся только для чтения (аналог локальной блокировки); 0 — снимает блокировку (в т. ч. выставленную пользователем вручную).

Уведомления о загрузке

Клиент детально сообщает о попытках:

  • Всё упало — красное уведомление с полной сводкой: Ошибка: не удалось загрузить подписку. Попытки: зеркало 1 (gmail.com): HTTP 502; зеркало 2 (www.google.com): таймаут 15 с; запасной URL (backup.com): HTTP 404. Для тотального таймаута добавляется подсказка увеличить таймаут.
  • Успех через отказоустойчивость — предупреждение: Подписка обновлена через запасной URL. Недоступны: зеркало 1 (gmail.com): таймаут 15 с или Подписка обновлена (не все зеркала доступны: …).
  • Чистый успех — без уведомлений (только top-баннер прогресса).

Расширенные объявления

Инфо-блок sub-info

sub-info-color: red
sub-info-text: Продлите подписку со скидкой
sub-info-button-text: Купить
sub-info-button-link: https://example.com/buy

Цветной баннер в карточке. Цвет: red / blue / green (default blue; неизвестные значения → blue). Текст — до 200 символов (обрезается с «…»), кнопка — до 25. Кнопка не показывается без ссылки. Отключение: sub-info-text: 0. Блок скрывается, пока активен баннер истечения (см. ниже).

Баннер истечения sub-expire

sub-expire: 1
sub-expire-button-link: https://example.com/renew

Системный баннер по expireDate из subscription-userinfo: за ≤ 3 дней — «Ваша подписка заканчивается через N д.» (или «…сегодня»), после истечения — «Подписка закончилась!». Кнопка «Продлить» открывает ссылку. Имеет приоритет над sub-info-блоком.

Локальные уведомления об истечении (notification-subs-expire)

notification-subs-expire: 1

Локальное уведомление раз в день в течение 3 дней до истечения: «У вашей подписки [имя] скоро истечёт срок действия (через N д.), не забудьте продлить её».


Настройки подключения (серверные)

Перезаписывают соответствующие настройки карточки подписки; действуют на серверы этой подписки при генерации конфига Xray. Область действия — только эта подписка (см. ниже «Эко-система подписок»).

Фрагментация TLS

fragmentation-enable: 1
fragmentation-packets: tlshello
fragmentation-length: 10-30
fragmentation-interval: 10-30

packets: tlshello / 1 / 1-3 / all; length/interval: диапазон min-max (положительные целые, min ≤ max). Некорректные значения молча отбрасываются (параметр не применяется).

Шумовые пакеты

noises-enable: 1
noises-type: rand
noises-packet: 10-20
noises-delay: 10-50

Тип шума: rand / str / hex / base64. Принимаются оба написания — noises-type (Incy) и noises-packet-type (Happ); значение array трактуется как rand. delay — диапазон min-max мс.

DoH-резолв адреса сервера

server-address-resolve-enable: 1
server-address-resolve-dns-domain: https://common.dot.dns.yandex.net/dns-query
server-address-resolve-dns-ip: 77.88.8.8

Перед подключением домен сервера резолвится через DNS-over-HTTPS (JSON API, A-записи):

  • при нескольких A-записях выбирается IP с минимальным временем TCP-подключения (до 5 кандидатов);
  • домен в адресе outbound заменяется на IP, оригинальный домен сохраняется в SNI (sni=/host=; для vmess — поля sni/host);
  • dns-ip — bootstrap: прямое подключение к IP DoH-сервера с подменённым Host, если домен DoH не резолвится;
  • по умолчанию (без dns-domain) используется https://common.dot.dns.yandex.net/dns-query;
  • результат кэшируется на 5 минут;
  • работает для share-ссылок (включая vmess-base64); готовые JSON-конфиги не переписываются;
  • ошибка резолва не блокирует подключение — используется исходный домен.

Эко-система подписок: область действия команд

Каждая подписка в IMSEL — изолированный контейнер: свои зеркала URL + fallback, свои настройки подключения (фрагментация/шум/MUX/allowInsecure/DoH), свои метаданные (трафик/срок/объявление/баннеры), своё состояние команд (locked, autoconnect, кастомный User-Agent). Серверы каждой подписки помечены именем группы (subName) и живут в своём списке.

Цепочка применения при подключении

Подключение идёт к конкретному серверу → по его subName находится его подписка → в конфиг Xray идут настройки этой подписки:

сервер (subName: "Провайдер A")
  → findRealSubscription("Провайдер A")
  → fragment/noise/mux/allowInsecure/DoH из карточки «Провайдер A»
  → конфиг Xray этого сервера

Команда fragmentation-*/noises-*/server-address-resolve-* от панели A перезаписывает поля только подписки A — серверы подписок B и C подключаются со своими (локальными или серверными) значениями.

Цепочка эффективных настроек (ЭКО)

При генерации конфига значения выбираются по убыванию специфичности:

  1. JSON-конфиг подписки (не share-ссылки) — настройки карточки не применяются вовсе (JSON не модифицируется; кнопка «Ещё» заблокирована).
  2. Сервер без карточки подписки (группы «ручные серверы»/«импорт из буфера» без записи) — берутся глобальные настройки приложения, а не чужой подписки.
  3. Share-ссылка + подписка — настройки карточки этой подписки (именно их перезаписывают серверные команды) с учётом:
    • overrideLinkFragment («перекрывать параметры из ссылки») — форсировать fragment-параметры подписки поверх per-link значений;
    • allowInsecure — объединение флага подписки и флага самого сервера.
  4. DoH-резолв — срабатывает при подключении к серверу только если у его подписки включён server-address-resolve-enable.

Что per-subscription, а что глобально

ОбластьПараметры
Только эта подпискаfragmentation-*, noises-*, server-address-resolve-*, change-user-agent, new-url(-N), new-domain(-N), fallback-url, hide-settings, sub-info-*, sub-expire(-button-link), notification-subs-expire, subscription-autoconnect(-type), subscription-ping-onopen-enabled, subscription-always-hwid-enable, no-limit-enabled, sniffing-enable, subscription-auto-update-enable, subscription-auto-update-open-enable, а также метаданные (title/описание/трафик/срок/ссылки/объявление/интервал)
Всё приложениеping-type, check-url-via-proxy

Глобальных команд всего две — обе про пинг; UI-настроек для них нет, управляются только заголовками (последняя приславшая панель задаёт значение). Всё остальное, включая sniffing и автообновление, живёт в контейнере подписки: команда панели и кнопки UI («Ещё», кнопка Sniffing на карточке) пишут одни и те же поля. Тринарность (0 для снятия) работает одинаково в обеих областях.


Поведение приложения

Автоподключение

subscription-autoconnect: 1
subscription-autoconnect-type: lastused

Автоматическое подключение при запуске приложения (один раз за холодный старт, если VPN не активен). Критерий выбора сервера: lastused (по умолчанию; ранее выбранный сервер), lowestdelay (после завершения автопинга — минимальная задержка), random.

Автопинг при открытии

subscription-ping-onopen-enabled: 1

Автоматический пинг серверов подписки при открытии приложения. Способ — ping-type.

Способ пинга

ping-type: proxy
check-url-via-proxy: https://cp.cloudflare.com/generate_204

ping-type: proxy (GET через ядро; default) / proxy-head / tcp / icmp (без root недоступен — выполняется как TCP-пинг). check-url-via-proxy — URL проверки для proxy-режимов (по умолчанию — стандартный URL проверки приложения, тот же что в диалоге пинга).

Обе команды применяются глобально и UI-настроек в приложении не имеют — единственный способ управления: заголовок ответа подписки (снятие — ping-type с другим значением; check-url-via-proxy перезаписывает пользовательский URL пинга).

Sniffing (per-subscription)

sniffing-enable: 1

Анализ трафика (sniffing) для серверов этой подписки — перезаписывает значение профиля маршрутизации только для неё (эко-система). В UI есть кнопка на карточке подписки (иконка «подзорная труба», рядом с пингом и меню): цикл «наследовать профиль → выключен → включён»; команда пишет то же поле. Тринарность: 0 — выключить, 1 — включить, отсутствие — не менять.

Автообновление (per-subscription)

subscription-auto-update-enable: 0
subscription-auto-update-open-enable: 1

Управляют тумблерами карточки этой подписки (эко-система):

  • subscription-auto-update-enable — «Автообновление» (интервальное, 1–168 ч; интервал сохраняется, profile-update-interval может задать свой). 0 — выключить, 1 — включить;
  • subscription-auto-update-open-enable — «Обновление при открытии»: обновлять эту подписку при каждом открытии приложения.

Оба тумблера доступны пользователю в окне управления подпиской («Ещё»); команда и UI пишут одни и те же поля.

Неотключаемый HWID

subscription-always-hwid-enable: 1

Принимается и сохраняется. В IMSEL X-HWID отправляется всегда (выключателя в приложении нет), поэтому команда фактически закрепляет существующее поведение.

Лимиты памяти ядра

no-limit-enabled: 1

Принимается и сохраняется, но пока не применяется: требуется нативная поддержка memory-limit в Go-ядре (GOMEMLIMIT).


Описание сервера (serverDescription)

Подпись под именем сервера (вместо технического тега «VLESS / WS / TLS»). Добавляется в фрагмент share-ссылки после имени:

vless://uuid@server:443#Сервер1?serverDescription=0J/RgNC40LzQtdGA

Значение — base64 (при невозможности декодирования принимается как plain text). Рекомендуемая длина — до 30 символов. Поддерживается во всех форматах ссылок (vless/vmess/trojan/ss/socks/wireguard/hysteria2) и в JSON-подписках через поле meta.serverDescription.


Приложение: порядок применения

Обработка ответа подписки (UI-обновление и фоновый воркер одинаковы):

  1. Разбор метаданных (SubscriptionMeta) и команд (SubscriptionCommands) — заголовки поверх #param.
  2. applyMeta — данные (трафик/срок/ссылки/объявление/интервал).
  3. applyCommands — порядок для URL: вынос встроенного fallback-url=new-url → точечные замены/удаления → патчи resolve-address/hostnew-domain (глобальный) → new-domain-N.
  4. Глобальные команды (ping-type, check-url-via-proxy) — в настройках приложения. Sniffing и автообновление применяются per-subscription вместе с остальными полями подписки.
  5. Планирование уведомлений об истечении.
  6. Сохранение; пересинхронизация фоновой задачи при смене URL/интервала.

Защита от регресса: подписка без новых заголовков ведёт себя идентично — все команды тринарны, отсутствующий параметр ничего не меняет.

Сводная таблица параметров

ПараметрТипВалидация/лимит
profile-titleтекст / base64:≤ 25 симв. (обрезается)
profile-descriptionтекст / base64:
profile-web-page-url, support-url, premium-urlURLhttp/https
subscription-userinfok=v;…байты; expire сек (или мс > 32e9)
profile-update-intervalintчасы; clamped 1–168; 0 = выкл
announceтекст / base64:≤ 200 симв.
announce-urlURLтело ответа ≤ 200 симв.
subscriptions-sort-typeenumwithout / ping / alphabet
new-urlURL / список / структураhttp/https; метки url:0–99
new-url-NURL / 0 / патчN = 0–99; патч: resolve-address=<домен|IP>, host=<домен>
new-domain, new-domain-NдоменN = 0–99
fallback-urlURL / списокhttp/https
change-user-agentтекст
hide-settingstri-booltrue/1, 0/false
sub-info-colorenumred / blue / green
sub-info-textтекст≤ 200 симв.; 0 = отключить
sub-info-button-textтекст≤ 25 симв.
sub-info-button-link, sub-expire-button-linkURLhttp/https
sub-expiretri-bool
notification-subs-expiretri-bool
subscription-always-hwid-enabletri-bool
no-limit-enabledtri-boolсохраняется, не применяется
fragmentation-enabletri-bool
fragmentation-packetsenum/диапазонtlshello / 1 / min-max / all
fragmentation-length, fragmentation-intervalдиапазонmin-max, min ≥ 1
noises-enabletri-bool
noises-type / noises-packet-typeenumrand / str / hex / base64 (array→rand)
noises-packetтекст/диапазон
noises-delayдиапазонmin-max
server-address-resolve-enabletri-bool
server-address-resolve-dns-domainURLhttp/https
server-address-resolve-dns-ipIPv44 октета 0–255
subscription-autoconnecttri-bool
subscription-autoconnect-typeenumlastused / lowestdelay / random
subscription-ping-onopen-enabledtri-bool
ping-typeenumproxy / proxy-head / tcp / icmp (→tcp)
check-url-via-proxyURLhttp/https
sniffing-enabletri-boolper-sub; null = наследовать профиль
subscription-auto-update-enable, subscription-auto-update-open-enabletri-boolper-sub (тумблеры карточки)

Не поддерживается

  • routing, autorouting, routing-enable — управление маршрутизацией из подписки (профили Happ применяются отдельным импортом imsel://routing/... с подтверждением пользователя);
  • per-app-proxy-enable/-mode/-list, app-auto-start — платформо-зависимые (Android) параметры;
  • socks-auth-*, http-auth-*, tun-type, custom-tunnel-config — desktop-параметры;
  • no-limit-enabled — сохраняется, применение ожидает нативной поддержки ядра;
  • Provider ID / Premium API (Lite Mode, кастомные темы, баннеры, push) — не реализовано.