---
name: cehoproxy_subscription_formats
description: "CehoProxy принимает подписку в пяти форматах, а не только ссылками; и почему замер скорости нод врёт из-под чужого VPN"
metadata: 
  node_type: memory
  type: project
  originSessionId: cf75944b-cc91-4988-aab6-28363391917a
  modified: 2026-08-07T16:09:59.909Z
---

Дополнение к [[cehoproxy_overview]] от 07.08.2026.

**Форматы подписки.** Владелец прислал свою подписку — она оказалась **массивом конфигураций xray** (так отдаёт его же `vsub-tg-bot`), и продукт её не понимал вовсе. Теперь `SubscriptionFormats` разбирает: xray/v2ray JSON (массив конфигов, имя ноды в `remarks` на уровне КОНФИГА, а не outbound), sing-box JSON, Clash/Clash.Meta YAML (только раздел `proxies`, свой мини-читатель без сторонней библиотеки — обе записи, однострочная `- {…}` и многострочная), SIP008. Плюс прежние: список ссылок и base64 поверх чего угодно. **Проверять новый формат надо не только парсером:** конфиг прогоняется через `sing-box check`, а лучше — поднять локальный mixed-прокси (root не нужен) и сходить curl'ом: так подтвердилось, что подписка живая и выход FI.

**Отсев медленных нод** — `chp speed <мс>`, поле в панели, вопрос в мастере. Замеры хранятся в `CehoConfig.NodeLatency` по ключу `протокол|адрес|порт`, порог отдельно в `MaxLatencyMs`: менять порог можно без повторного замера. Нода без замера остаётся в пуле (hysteria2 и tuic не мерятся никогда — UDP).

**Две грабли замера, обе поймы живьём:**
1. **В замер попадало время DNS.** Ноды, записанные IP-адресом, показывали 0 мс, ноды с именем — 80 мс, и числа были несравнимы. Имя теперь разрешается ДО секундомера.
2. **Из-под чужого VPN замер врёт целиком.** Свой TUN ловится по адресу (`NodeProbe.TunnelIsUp`), а посторонний — нет: gvisor принимает TCP локально, и все ноды отвечают за 0-5 мс. Опознаётся физикой — ноды в РАЗНЫХ странах не могут отвечать одинаково быстро (`LooksLikeLocalAccept`: ≥3 замера, ≥3 разных сервера, ≥2 стран, ≥70% быстрее 10 мс). В таком состоянии продукт отказывается отсеивать и объясняет причину. Мак владельца как раз всегда за туннелем (прямой выход показывает DE), так что на нём отсев не работает by design.

**`PoolEmptyException`** — отдельный тип для «в пуле не осталось нод». По нему откатываются настройки, которыми пул сузили (страны, порог). Без своего типа откатывалось и то, что к пулу отношения не имеет: «не выбрано ни одной программы» стирало верно выставленный порог.
