Перейти к содержанию

Premium API

Система управления подписками с шифрованием на основе доменных хешей. Позволяет провайдерам настраивать внешний вид приложения, параметры серверов и уведомления для своих пользователей.

Обзор

Приложение при добавлении подписки извлекает домен из URL и запрашивает конфигурацию провайдера через зашифрованный API. Домен никогда не передаётся в открытом виде — используется SHA-256 хеш.


API

Endpoint

GET /api/subscription/config?h=<sha256hex>&hwid=<sha256hex>

Запрос

Параметр Тип Обязательный Описание
h string да SHA-256 хеш домена (64 hex-символа)
hwid string нет SHA-256 хеш HWID устройства (64 hex-символа). Для проверки лимита устройств

Хеш вычисляется от домена в нижнем регистре:

SHA256("example.com") → "a379a6f6eeafb9a55e378c118034e2751e682fab9f2d30ab13d2125586ce1947"

Ответ

Сервер всегда возвращает зашифрованный ответ:

{
    "encrypted": true,
    "iv": "<base64>",
    "data": "<base64>",
    "tag": "<base64>"
}

Поля ответа (после расшифровки)

Поле Тип Описание
success boolean Успешность запроса
domain string Домен подписки
isPremium boolean Активен ли Premium у провайдера
deviceLimitExceeded boolean Лимит устройств провайдера превышен (см. Лимиты устройств)
logoUrl string? URL логотипа провайдера
settings object Настройки провайдера (см. ниже)
theme object Кастомная тема (см. ниже)

Rate Limit

30 запросов в минуту на IP-адрес. При превышении — ответ 429.


Шифрование

Ответы API зашифрованы алгоритмом AES-256-GCM. Ключ шифрования деривируется на основе домена — только клиент, знающий оригинальный домен и комбинацию, может расшифровать ответ.

Компоненты ответа:

Поле Описание
iv Initialization vector (base64, 12 байт)
data Зашифрованные данные (base64)
tag Authentication tag (base64, 16 байт)

Детали деривации ключа и реализация шифрования являются внутренними и не публикуются.


Структура конфигурации

Настройки провайдера (settings)

Базовые

Поле Тип Описание
serverDescription string Описание сервера (до 30 символов)
alwaysHwidEnable boolean Принудительная отправка HWID (пользователь не может отключить)
expiryNotifications boolean Локальные уведомления об истечении подписки
showServerDescription boolean Показывать описание серверов. По умолчанию true
keepExpanded boolean / null Не сворачивать эту подписку по умолчанию; пользователь может переопределить настройку (см. ниже)

Не сворачивать подписку

Настройка задаётся в Premium-панели провайдера и передаётся существующим API конфигурации как settings.keepExpanded, а не HTTP-заголовком подписки:

{
  "settings": {
    "keepExpanded": true
  }
}

true оставляет список провайдера раскрытым на Главной и в разделе «Серверы». false, null или отсутствие поля разрешает сворачивание по умолчанию. Значение провайдера применяется при активном Premium. Это начальная настройка, а не запрет для пользователя: в меню подписки «⋯ → Настройки» он может включить или выключить «Не сворачивать». Явный локальный выбор сохраняется после перезапуска и обновления подписки и имеет приоритет над дальнейшими изменениями провайдера.

При включённой настройке обычное сворачивание и команда «Свернуть всё» оставляют эту подписку раскрытой. Другие подписки и вручную добавленные серверы не затрагиваются. Поддерживается на iOS/tvOS, Android/TV и Desktop; старые клиенты игнорируют необязательное поле.

Domain fronting и фрагментация

Поле Тип Описание
resolveAddress string IP для domain fronting
hostHeader string Host-заголовок для domain fronting
fragmentEnabled boolean TCP-фрагментация в hev-socks5-tunnel
fragmentLength string Диапазон длины фрагментов (напр. "10-30")
fragmentInterval string Диапазон интервала (напр. "20-40" мс)
fragmentPackets string Количество фрагментов (напр. "5-10")

DNS-резолв адреса сервера (DoH)

Поле Тип Описание
serverAddressResolveEnable boolean Предварительный резолв адреса сервера через DoH
serverAddressResolveDnsDomain string URL DoH-сервера (напр. https://common.dot.dns.yandex.net/dns-query)
serverAddressResolveDnsIp string IP DoH-сервера (используется до резолва его домена)

Lite Mode

Упрощённый интерфейс с ссылками на бот, канал, поддержку и (опционально) премиум. Подробнее про иконки — icon-presets.md.

Поле Тип Описание
liteMode boolean Включить упрощённый режим
botUrl string? Ссылка на Telegram-бот
channelUrl string? Ссылка на Telegram-канал
supportUrl string? Ссылка на поддержку
botIconKey string? Ключ иконки бота из пресет-набора (см. icon-presets.md)
channelIconKey string? Ключ иконки канала
supportIconKey string? Ключ иконки поддержки

null или неизвестный ключ → клиент использует дефолтную иконку (send для бота, megaphone для канала, help для поддержки).

Админ-доступ (admin HWIDs)

Устройства в списке adminHwids могут:

  • Просматривать и редактировать конфиги серверов прямо в приложении.
  • Отправлять провайдерские уведомления без модерации (auto-approve при совпадении с targetSegment.hwid).

Подробности — admin-hwids.md.

Поле Тип Описание
adminHwids string[] Список HWID устройств с админ-правами

Баннер подписки

Баннер произвольного цвета внутри карточки подписки. Используется для анонсов и технических работ («тех. работы на сервере N»). Показывается всем пользователям подписки.

Поле Тип Описание
bannerEnabled boolean Показывать баннер
bannerText string? Текст баннера (белым по bannerBgColor)
bannerButtonText string? Текст кнопки (если пусто — кнопка скрыта)
bannerButtonUrl string? URL кнопки
bannerBgColor string? Цвет фона баннера (hex, напр. "#E53E3E", по умолчанию красный)
bannerButtonColor string? Цвет кнопки (hex, по умолчанию зелёный)

Баннер об истечении подписки

Отдельный баннер для подписок, до истечения которых осталось 3 дня или меньше, а также уже истёкших подписок (клиент использует дату истечения). Приглашает продлить подписку. Набор полей независим от баннера выше.

Поле Тип Описание
expiryBannerEnabled boolean Показывать баннер об истечении
expiryBannerText string? Текст баннера
expiryBannerButtonText string? Текст кнопки (если пусто — кнопка скрыта)
expiryBannerButtonUrl string? URL кнопки (если не задан — кнопка не показывается)
expiryBannerBgColor string? Цвет фона (hex, по умолчанию "#D97706")
expiryBannerButtonColor string? Цвет кнопки (hex, по умолчанию зелёный)
expiryBannerTextColor string? Цвет текста баннера (hex)
expiryBannerButtonTextColor string? Цвет подписи кнопки (hex)

Приоритет: обычный баннер объявления/технических работ имеет приоритет над обычным баннером истечения. Последний включается панелью или непустым заголовком expiry-banner-text и показывается за 3 дня до истечения или после него. Отдельное напоминание о продлении, если настроено, сохраняет свои правила показа.

Все семь полей содержимого и цветов можно переопределить заголовками баннера истечения, в том числе для напоминания о продлении после истечения. Дата истечения пользовательской подписки по-прежнему берётся из subscription-userinfo.

Платформы: iOS, Android, Desktop (incy-linux).

Принудительные настройки

Переопределяют выбор пользователя в настройках приложения, пока подписка активна.

Поле Тип Описание
forceConnectionStyle string? Стиль кнопки подключения: "classic" (крупная круглая кнопка) или "compact" (узкий toggle внизу)

Ping и сортировка

Применяются при первом подключении подписки. На iOS и Android эти значения задаются per-подписку (у каждой подписки свои пинг/протокол/сортировка/стиль); пользователь может переопределить их в настройках подписки. На Desktop переопределяют глобальные настройки приложения.

Поле Тип Описание
defaultPingProtocol string? Тип пинга: incy, tcp, proxy_head, proxy_get, icmp
pingTestUrl string? URL для HTTP-пинга (используется при incy / proxy_head / proxy_get)
defaultSortOrder string? Сортировка серверов: none, ping, name
defaultPingDisplayFormat string? Вид отображения пинга: time (цифры, мс), bars (шкала-полоски), both (цифры + шкала) или dots (зелёные точки — ответил/нет). Режим dots поддерживается только на iOS; Android/Desktop показывают time/bars/both
pingOnUpdate boolean? Авто-пинг всех серверов после обновления подписки

incy — INCY Ping: реальный HTTP GET через прокси, но значение делится на ~3.3, чтобы читаться как привычный пинг (а не полный проксированный round-trip). Метод по умолчанию в приложении.

Контент

Поле Тип Описание
announceUrl string? URL для объявлений провайдера в приложении
webPageUrl string? Ссылка на веб-страницу провайдера

Резервные домены (fallbackHosts)

Поле Тип Описание
fallbackHosts string[] Список запасных хостов. Если основной домен подписки не отвечает при обновлении, клиент повторяет тот же запрос, подставляя эти хосты по очереди (меняется только хост, путь/токен те же). Перебираются сверху вниз.

Клиент кэширует fallbackHosts на подписке, поэтому перебор работает даже когда основной домен полностью недоступен. На 404/410 (подписка удалена) перебор не выполняется. В премиум-панели задаётся кнопкой «Резервные» рядом с доменом.

Кастомная тема (theme)

Расширенная палитра, применяется только когда пользователь видит premium-подписку. Поддерживает плоские цвета и 2-4-стоповые градиенты.

Плоские цвета

Поле Тип Описание
enabled boolean Кастомная тема включена
darkMode boolean Тёмный режим
accent string Основной акцентный цвет (hex, напр. "#FF6B6B")
accentSecondary string Вторичный акцентный цвет
background string Цвет фона
card string Цвет карточек
surface string Цвет поверхности
textPrimary string Основной цвет текста
textSecondary string Вторичный цвет текста

Градиенты

Если задан, перекрывает плоские accent/accentSecondary для акцентных элементов и background для фона.

Поле Тип Описание
accentGradient.stops GradientStop[] 2-4 стопа ({ color, position }), position в диапазоне 0.0..1.0
accentGradient.angle number Угол градиента в градусах 0..360
backgroundGradient.enabled boolean Включить градиентный фон
backgroundGradient.stops GradientStop[] 2-3 стопа
backgroundGradient.angle number Угол градиента

Каждый стоп:

{ "color": "#B8D94A", "position": 0.0 }

Лимиты устройств

Premium-провайдеры имеют тарифы с ограничением по количеству устройств. Лимит задаётся полем maxDevices провайдера.

Как работает

  1. Клиент отправляет hwid=SHA256(rawHwid) в запросе конфигурации
  2. Сервер проверяет, зарегистрировано ли устройство с таким hwidHash для доменов данного провайдера
  3. Если устройство уже зарегистрировано — всегда получает premium (существующие устройства не блокируются)
  4. Если устройство не зарегистрировано — сервер считает общее количество устройств провайдера:
  5. Если totalDevices < maxDevices — premium выдаётся
  6. Если totalDevices >= maxDevices — возвращается isPremium: false + deviceLimitExceeded: true

Поведение клиента при deviceLimitExceeded: true

  • Premium-функции отключены (как при isPremium: false)
  • Кастомная тема провайдера не применяется
  • Premium-настройки (фрагментация, fronting и т.д.) не используются
  • VPN продолжает работать в базовом режиме
  • Устройство регистрируется (но без premium)

Тарифы

Лимит Описание
1000 Стандартный тариф
3000 Средний тариф
6000 Максимальный тариф
null Безлимитный (нет ограничений)

Регистрация устройства

При добавлении подписки приложение регистрирует устройство для отслеживания активных подключений и push-уведомлений.

Данные регистрации

Поле Тип Описание
hwid string Аппаратный идентификатор устройства (подробнее)
hwidHash string SHA-256 хеш HWID (для серверной проверки лимитов)
uid string Анонимный UID устройства
platform string "android", "ios", "linux", "windows", "macos"
appVersion string Версия приложения
osVersion string Версия ОС
locale string Язык устройства
subscriptionDomainHash string SHA-256 хеш домена подписки
fcmToken string Push-токен для уведомлений (FCM / APNs)
lastActive timestamp Время последней активности

Поле hwidHash добавляется при регистрации и используется сервером для проверки лимита устройств без знания исходного HWID.

Синхронизация подписки

Сервер может установить флаг subscriptionNeedsSync = true на устройстве. При обнаружении этого флага приложение:

  1. Читает поле subscriptionNewDomain из документа устройства
  2. Обновляет URL подписки на новый домен
  3. Сбрасывает флаг subscriptionNeedsSync

Это позволяет провайдеру мигрировать пользователей на новый домен при блокировке старого.


Кеширование

  • In-memory кеш: конфигурация хранится в памяти на время работы приложения
  • Disk кеш: сохраняется в SecureStorage для offline-доступа
  • Fallback: при ошибке 503 используется кешированная конфигурация
  • Очистка: конфигурация удаляется из кеша, если домен больше не является premium

Certificate Pinning

Запросы к Premium API на Android и Desktop защищены certificate pinning (SHA-256 пины TLS-сертификата). Это предотвращает MITM-атаки на канал связи с API.

Проверка переноса из заголовков подписки

GET /api/subscription/resolve?h=<sourceHash>&target=<targetHash>&origin=<responseHostHash> проверяет верификацию и владельца исходного, нового и фактического домена ответа. Все значения — SHA-256 от домена в нижнем регистре. Ответ содержит success, approved, sourceHash, targetHash, originHash и заголовок Cache-Control: no-store. Разрешение не создаёт глобального переноса домена и не требует передачи URL/токена подписки. Клиент сверяет все три хеша и применяет решение только к текущей подписке. Отсутствие approved: true означает отказ. Некорректные хеши дают HTTP 400, сбой проверки — HTTP 503. Запрос без target и origin сохраняет прежнюю логику разрешения общего переноса домена.

Провайдер передаёт новый адрес в HTTP-ответе подписки, а не в общих настройках Premium: new-domain / new-url.