Skip to content

Operator API Headers ru

Nemu-x edited this page Jul 12, 2026 · 2 revisions

ClashFest Operator API — справочник заголовков

🌐 English · Русский · 中文

Легенда статусов:

  • v1 — реализовано сейчас (или запланировано в текущей ветке)
  • v2 — следующая волна (расширенное инфо оператора + показ политик)
  • v3 — позже (упрощение UI, контролируемое оператором)
  • proposed — принято в спеку, ещё не запланировано

Все заголовки матчатся регистронезависимо. Пустые / blank значения = «заголовок отсутствует». Строковые поля могут быть plain UTF-8 или с префиксом base64: (X-Brand-Name: base64:U3dpZnRWUE4=) — клиент декодит оба.

Основная тема ClashFest — тёмная. Где есть «светлая» альтернатива — это опциональный оверрайд.


0. Мастер-переключатель

X-Branding-Enabled

Тип boolean
Статус v1
Требуется для Любого косметического брендинга. Без этого заголовка со значением true все X-Brand-* identity/tab/info поля игнорируются, и клиент показывает дефолтный UI ClashFest. Исключение: заголовки политики оператора (§4b, напр. X-Brand-Hide-Global-Mode) применяются без этого переключателя.
По умолчанию отсутствует / false / nullбрендинг выключен
Заметки Брендинг — явный opt-in на каждую подписку. Задать X-Brand-Name, X-Brand-Logo-URL и т.д. без X-Branding-Enabled: true — no-op: заголовки парсятся и персистятся, но UI остаётся дефолтным. Чтобы откатить кривой деплой — уберите этот заголовок (или поставьте false); brand-состояние на клиенте откатывается сразу после следующего обновления подписки.

Пример включения брендинга:

X-Branding-Enabled: true
X-Brand-Name: SwiftVPN
X-Brand-Logo-URL: https://cdn.example.com/logo-dark.png
X-Brand-Accent-Color: #5E35B1

Пример выключения / kill switch:

X-Branding-Enabled: false

1. Идентичность бренда

X-Brand-Name

Тип string
Макс. длина 32 символа
Статус v1
Применяется к Заголовку главного экрана (заменяет «ClashFest»); экрану About
Фолбэк «ClashFest»
Валидация trim; control-символы удаляются; blank → игнорируется

Пример:

X-Brand-Name: SwiftVPN

X-Brand-Tagline

Тип string
Макс. длина 64 символа
Статус v1
Применяется к Подзаголовку About; опциональному сабтайтлу под именем бренда в хедере
Фолбэк пусто (тэглайн не показан)

Пример:

X-Brand-Tagline: Fast and private since 2024

X-Brand-Logo-URL

Тип URL (только https)
Статус v1
Применяется к Круглой плитке лого в хедере (слева от имени); иконке About
Форматы PNG, WebP, JPEG. Без SVG (см. Security)
Рек. размер 256×256, ≤200KB
Жёсткий лимит 512KB
Кэш диск <filesDir>/brand/<sha256(url)>, атомарная запись
Валидация только https, whitelist content-type, SSRF-guard (нет приватных IP / редиректов на приватные IP), лимит размера
Фолбэк иконка лаунчера
Заметки Это основное лого, по умолчанию. Ожидается корректным на тёмном фоне (тёмная тема — дефолт).

Пример:

X-Brand-Logo-URL: https://swiftvpn.example.com/static/logo-256.png

X-Brand-Logo-Light-URL

Тип URL (только https)
Статус v1
Применяется к То же, что X-Brand-Logo-URL, но для светлой темы
Валидация как у X-Brand-Logo-URL
Фолбэк X-Brand-Logo-URL (тёмное лого используется на светлой теме, если оператор не отдал светлый вариант)
Заметки Опционально. Большинству операторов хватает одного лого.

X-Brand-Accent-Color

Тип hex-цвет #RRGGBB
Статус v1
Применяется к Runtime-оверрайд colorPrimary — power-кнопка, тумблеры, свитчи, прогресс-бары, filled-чипы, selected-состояния, акцентные поверхности по всему приложению
Валидация regex ^#[0-9A-Fa-f]{6}$; отклоняется, если контраст с поверхностью < 3:1 (минимум WCAG AA для крупного текста)
Фолбэк встроенный акцент темы
Заметки Это ручка «полного white-label». Выберите цвет, работающий и в тёмной, и в светлой теме (контраст-фильтр отклонит односторонний). Рек. насыщенность 30–70%.

Пример:

X-Brand-Accent-Color: #5E35B1

2. Инфо оператора / внешние ссылки

Все URL-поля имеют одну валидацию: должно быть https://, tg://, mailto: или t.me/ (auto-промоут в https://t.me/). Иначе игнорируется.

X-Brand-Website-URL

Тип URL · Статус v1 · Применяется к

X-Brand-Support-URL

Тип URL · Статус v1 (расширение существующего support-url) · Применяется к

Обратная совместимость: также принимает support-url, Profile-Support-URL, Subscription-Support-URL. При наличии обоих X-Brand-Support-URL выигрывает.

X-Brand-Telegram-URL

Тип URL (https / tg / t.me) · Статус v1 · Применяется к

X-Brand-Bot-URL

Тип URL (https / tg / t.me) · Статус v1 · Применяется к

X-Brand-Privacy-URL · X-Brand-Terms-URL · X-Brand-Help-URL

Тип URL · Статус v1 · Применяется к

X-Brand-Status-URL

Тип URL · Статус v2 · Применяется к

X-Brand-Renew-URL

Тип URL
Статус v2
Применяется к Когда истечение подписки критично (<3 дней или уже истекло):
• тап по critical-expiry чипу на карточке профиля
• кнопка «Renew» в шторке профиля рядом с инфо об истечении
• пункт «Renew subscription» в overflow-меню профиля
About тоже получает кнопку Renew, когда URL задан.
Заметки Все точки входа появляются только при наличии URL. Нет URL → нет Renew-UI.

X-Brand-Cabinet-URL

Тип URL (https / tg / mailto) — почти всегда с шаблонной переменной панели
Статус v3
Применяется к Кнопке «My account» на Operator-табе, tonal-secondary под Renew CTA (или одна, если Renew нет).
Заметки Оператор строит пер-юзер URL через шаблонный идентификатор панели — {{SHORT_UUID}}, {{ID}}, {{USERNAME}}. Примеры:
• Telegram Mini App: https://t.me/<bot>?startapp={{SHORT_UUID}}
• Web-кабинет: https://billing.example.com/account?ref={{ID}}
• Bot со start-payload: https://t.me/<bot>?start={{SHORT_UUID}}
Клиент открывает через ACTION_VIEW — Android роутит tg:// / https://t.me/... в Telegram, прочие https:// в браузер.

3. Пользовательский контекст

Клиент использует только одно поле для юзер-facing идентичности. Пер-юзер персонализация живёт в profile-title — оператор кладёт туда что угодно. Свободная форма, контролируется панелью.

profile-title (существующий, не-Brand)

Тип string · Статус v1 · Применяется к

X-Brand-User-Display-Name

Тип string · Макс. 64 · Статус v2 · Применяется к
Заметки Заполняется шаблонной переменной, напр. X-Brand-User-Display-Name: {{USERNAME}}. См. Шаблонные переменные.

X-Brand-Greeting

Тип string · Макс. 120 · Статус v2 · Применяется к
Заметки Свободная форма, обычно через шаблоны панели: X-Brand-Greeting: Welcome back, {{USERNAME}}! {{DAYS_LEFT}} days remaining. Если отсутствует, но задан X-Brand-User-Display-Name — фолбэк на встроенное «Hello, !».

4. UX-дефолты — упрощение, контролируемое оператором

X-Brand-Show-Operator-Tab

Тип boolean · Статус v1
Применяется к Добавляет отдельный таб «Operator» в нижнюю навигацию с лого + именем + тэглайном + Renew CTA + списком инфо-ссылок.
Заметки Явный opt-in. Одни identity-заголовки (name / logo / accent) НЕ добавляют таб автоматически. Свяжите с X-Brand-Hide-Routing, чтобы заменить Routing, а не добавлять 5-й таб.

X-Brand-Hide-Routing

Тип boolean · Статус v1
Применяется к В паре с X-Brand-Show-Operator-Tab=true Operator-таб заменяет Routing в нижней навигации (по-прежнему 4 таба). В одиночку — no-op.

4b. Политика оператора — применяется БЕЗ X-Branding-Enabled

Всё выше — косметический брендинг и требует X-Branding-Enabled: true. Заголовки ниже — политика оператора (ограничения поведения, не внешний вид) — применяются по одному наличию, не требуют включённого брендинга и переживают kill-switch X-Branding-Enabled: false (он стирает только косметику).

X-Brand-Hide-Global-Mode

Тип boolean · Статус v4
Нужен X-Branding-Enabled? Нет — единственный заголовок, работающий полностью unbranded.
Применяется к Скрывает кнопку Global на Главной и пинит приложение в Rule (если юзер был в Global — вернёт в Rule). Строка «Mode» и кнопка Rule остаются.
Заметки Контроль оператора, не брендинг: не даёт юзерам пускать весь трафик через прокси в обход правил. Работает при X-Branding-Enabled absent / true / false.

5. Политика подписки

Subscription-Userinfo (существующий)

Формат upload=N; download=N; total=N; expire=UNIX · Статус v1 · Применяется к

profile-update-interval (существующий)

Тип integer (часы) · Статус v1 · Применяется к

share-links (существующий)

Тип boolean (true/1/yes/on = отключить шаринг) · Статус v1
Применяется к Скрывает «Copy node link» / «Share» в пикере И блокирует редактирование URL подписки только для этой подписки
Заметки Хранится пер-профиль. Разные подписки могут иметь разную политику шаринга.

X-Network-Stack

Тип enum: system | gvisor | mixed | auto · Статус v1
Нужен X-Branding-Enabled? Нет — это политика оператора, работает без брендинга.
Применяется к TUN-стек, передаваемый в VpnService. system/gvisor/mixed лочат стек для этой подписки, перебивая ручную настройку «Режим стека» у пользователя. auto = оператор не лочит (отдаёт настройке пользователя / дефолту system).
По умолчанию Хедер отсутствует → дефолт клиента (system).
Заметки Хранится пер-профиль (subscriptionNetworkStackFor(uuid)), как share-links — разные подписки могут форсить разный стек на одном устройстве. Приоритет: хедер оператора > ручная настройка > Auto (тянет tun.stack из подписки) > дефолт system. Дефолт приложения — system; пользователь может выбрать auto, чтобы следовать tun.stack подписки, а этот хедер перебивает и то, и другое. Рекомендуется system — kernel-стек быстрее на teardown и легче по батарее, чем userspace-нетстек gVisor; gvisor/mixed — только если реально нужно устройству/сети. Написания (регистронезависимо): X-Network-Stack, Network-Stack, X-NetworkStack, X-NetworkStack-enabled.

Пример (залочить всех пользователей этой подписки на kernel-стек):

X-Network-Stack: system

x-hwid-active / x-hwid-not-supported / x-hwid-max-devices-reached / x-hwid-limit (существующие)

Тип boolean · Статус v1 · Применяется к

6. Анонсы

announce / Announcement (существующий)

Тип string (UTF-8 или base64:) · Макс. 256 · Статус v1 · Применяется к

announce-url / Announcement-URL (существующий)

Тип URL · Статус v1 · Применяется к

Что мы намеренно НЕ шлём (и почему)

  • X-Brand-Default-Mode — mihomo уже читает mode: из YAML.
  • X-Brand-Recommended-Group — Selector по дефолту берёт первый прокси из списка; оператор рулит через YAML.
  • X-Brand-Locale / X-Brand-Theme — это пользовательские настройки; тихий оверрайд оператором — hostile UX.
  • X-Brand-Max-Devices / X-Brand-Current-Devices — без текущего счётчика чип читается как филлер; текущий счёт ненадёжен через response-заголовки.

Если будущий кейс реально потребует одного из них — сначала добавим под proposed и обсудим.

Clone this wiki locally