<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://wiki.mywolfram.ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=%D0%92%D0%BB%D0%B0%D0%B4%D0%B8%D0%BC%D0%B8%D1%80</id>
	<title>wolfram - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.mywolfram.ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=%D0%92%D0%BB%D0%B0%D0%B4%D0%B8%D0%BC%D0%B8%D1%80"/>
	<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/%D0%92%D0%BB%D0%B0%D0%B4%D0%B8%D0%BC%D0%B8%D1%80"/>
	<updated>2026-08-23T06:19:48Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A1%D1%83%D0%B2%D0%B5%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BD%D0%B5%D1%82&amp;diff=1046</id>
		<title>Суверенный интернет</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A1%D1%83%D0%B2%D0%B5%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BD%D0%B5%D1%82&amp;diff=1046"/>
		<updated>2026-08-14T12:27:27Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Эта страница посвящается всему что связано с ограничим доступа в интернет.&lt;br /&gt;
&lt;br /&gt;
На данный момент 20.04.26 мы имеем полную блокировку по белыми IP у операторов мобильной связи. И предпосылки к блокировкам на уровне провайдеров домашнего интернета.&lt;br /&gt;
&lt;br /&gt;
* [[Обход НКР|Установка и настройка связки Zapret + Podkop на OpenWRT]]&lt;br /&gt;
* [[Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация]]&lt;br /&gt;
* [[Hysteria 2 каскад]]&lt;br /&gt;
* [[Whitelist-bypass: VK Creator (headless на сервере) + Joiner (Android)]]&lt;br /&gt;
* [[Установка MTProto Proxy (mtg) на Linux-сервере]]&lt;br /&gt;
* [[VK Turn Proxy + FreeTurn + VLESS]]&lt;br /&gt;
* [[NaïveProxy: установка полной каскадной цепочки]]&lt;br /&gt;
* [[OlcRTC: туннель через WebRTC-сервисы|olcRTC: туннель через легальные WebRTC-сервисы]]&lt;br /&gt;
* [[mieru: каскадный шифрованный SOCKS5-туннель]]&lt;br /&gt;
* [[Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)]]&lt;br /&gt;
* [[Mieru и NaïveProxy: каскадные туннели под управлением панели s-ui-x]]&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Re:HomeProxy_%D0%BD%D0%B0_OpenWrt:_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D0%BA_%D0%B8_%D0%B3%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&amp;diff=1045</id>
		<title>Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Re:HomeProxy_%D0%BD%D0%B0_OpenWrt:_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D0%BA_%D0%B8_%D0%B3%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&amp;diff=1045"/>
		<updated>2026-08-14T11:59:10Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Упрощён раздел установки, добавлены скриншоты&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация =&lt;br /&gt;
Пошаговая инструкция по установке &#039;&#039;&#039;Re:HomeProxy&#039;&#039;&#039; на роутер с OpenWrt — от чистой системы до работающего обхода блокировок. Рассчитана на пользователя, который умеет подключаться к роутеру по SSH.&lt;br /&gt;
&lt;br /&gt;
Проверено на &#039;&#039;&#039;Xiaomi Redmi Router AX6000&#039;&#039;&#039; (&amp;lt;code&amp;gt;aarch64_cortex-a53&amp;lt;/code&amp;gt;) с &#039;&#039;&#039;OpenWrt 24.10.0&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как читать команды:&#039;&#039;&#039; каждая команда приведена &#039;&#039;&#039;отдельной строкой&#039;&#039;&#039; и выполняется самостоятельно — копируйте и вставляйте их по одной, дожидаясь завершения предыдущей. Ничего править перед запуском не нужно, кроме мест, где это указано явно.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Одна панель в LuCI, которая управляет всем: туннелем, списками заблокированных ресурсов и обходом DPI.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Re:HomeProxy&#039;&#039;&#039;&lt;br /&gt;
|Веб-панель в LuCI. Управляет узлами, маршрутизацией и обходом DPI.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box-extended&#039;&#039;&#039;&lt;br /&gt;
|Ядро. Поддерживает VLESS (REALITY, XHTTP), Hysteria2, Trojan, Shadowsocks, AnyTLS, WireGuard, AmneziaWG, Mieru, NaïveProxy и др.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Списки маршрутизации&#039;&#039;&#039;&lt;br /&gt;
|Готовые наборы доменов и IP (Re-filter, Russia Inside, отдельные сервисы). Обновляются автоматически раз в сутки.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Zapret 2&#039;&#039;&#039;&lt;br /&gt;
|Обход DPI без туннеля — для сервисов, которые не заблокированы, а замедлены (например, видеохостинги). Трафик идёт напрямую, не нагружая VPS.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Принцип простой:&#039;&#039;&#039; списки решают, &#039;&#039;какой&#039;&#039; трафик особенный, а узел или Zapret — &#039;&#039;через что&#039;&#039; его пустить. Всё, что не попало ни в один список, идёт напрямую.&lt;br /&gt;
&lt;br /&gt;
== Что понадобится ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|OpenWrt / ImmortalWrt &#039;&#039;&#039;24.10&#039;&#039;&#039; и новее (&amp;lt;code&amp;gt;opkg&amp;lt;/code&amp;gt;) либо &#039;&#039;&#039;25.12&#039;&#039;&#039; и новее (&amp;lt;code&amp;gt;apk&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ&#039;&#039;&#039;&lt;br /&gt;
|Веб-интерфейс LuCI и SSH к роутеру&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Узел&#039;&#039;&#039;&lt;br /&gt;
|Ссылка на ваш сервер: &amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;naive+https://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss://&amp;lt;/code&amp;gt; и т.п.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Установка =&lt;br /&gt;
&lt;br /&gt;
== Шаг 1. Установить панель ==&lt;br /&gt;
Подключитесь к роутеру по SSH и выполните команды по очереди.&lt;br /&gt;
&lt;br /&gt;
Обновить список пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить зависимости:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg install ucode-mod-digest kmod-nft-tproxy kmod-tun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Перейти в каталог для загрузок:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скачать панель:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
wget -O rhp.ipk https://github.com/1andrevich/homeproxy-hiddify/releases/download/2026.06.29-r1/luci-app-re-homeproxy_2026.06.29-r1_all.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg install /tmp/rhp.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скачать русский язык интерфейса:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
wget -O rhp-ru.ipk https://github.com/1andrevich/homeproxy-hiddify/releases/download/2026.06.29-r1/luci-i18n-homeproxy-ru_2026.06.29-r1_all.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить язык:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg install /tmp/rhp-ru.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Актуальную версию&#039;&#039;&#039; пакета смотрите на [https://github.com/1andrevich/homeproxy-hiddify/releases странице релизов] — в командах указана &amp;lt;code&amp;gt;2026.06.29-r1&amp;lt;/code&amp;gt;. Для OpenWrt 25.12 и новее замените в ссылках &amp;lt;code&amp;gt;.ipk&amp;lt;/code&amp;gt; на &amp;lt;code&amp;gt;.apk&amp;lt;/code&amp;gt;, а команду установки — на &amp;lt;code&amp;gt;apk add --allow-untrusted /tmp/rhp.apk&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Шаг 2. Установить ядро ==&lt;br /&gt;
Ядро &amp;lt;code&amp;gt;sing-box-extended&amp;lt;/code&amp;gt; ставится вручную — сборка из репозитория OpenWrt собрана без поддержки &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039;, поэтому берём универсальную musl-сборку, где есть все протоколы.&lt;br /&gt;
&lt;br /&gt;
Скачать ядро:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
wget -O sb.tar.gz https://github.com/shtorm-7/sing-box-extended/releases/download/v1.13.18-extended-2.6.5/sing-box-1.13.18-extended-2.6.5-linux-arm64-musl.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Распаковать:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tar xzf sb.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cp /tmp/sing-box-1.13.18-extended-2.6.5-linux-arm64-musl/sing-box /usr/bin/sing-box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделать исполняемым:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod +x /usr/bin/sing-box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Убрать временные файлы:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rm -rf /tmp/sing-box-1.13.18-extended-2.6.5-linux-arm64-musl /tmp/sb.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — архитектура и путь.&#039;&#039;&#039; В ссылке указана архитектура &amp;lt;code&amp;gt;arm64&amp;lt;/code&amp;gt;; если у вас другая, возьмите подходящий файл &amp;lt;code&amp;gt;linux-&amp;lt;арх&amp;gt;-musl&amp;lt;/code&amp;gt; на [https://github.com/shtorm-7/sing-box-extended/releases странице релизов ядра]. Копировать ядро нужно строго в &amp;lt;code&amp;gt;/usr/bin/sing-box&amp;lt;/code&amp;gt; — по другому пути служба его не запустит.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Шаг 3. Запустить службу ==&lt;br /&gt;
Включить автозапуск:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy enable&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Запустить:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дальше вся настройка — в веб-интерфейсе: &#039;&#039;&#039;Сервисы → Re:HomeProxy&#039;&#039;&#039;. Если пункт меню не появился, обновите страницу с очисткой кэша (&amp;lt;code&amp;gt;Ctrl+F5&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Шаг 4. Добавить узел ==&lt;br /&gt;
Откройте вкладку &#039;&#039;&#039;Настройки узлов&#039;&#039;&#039;. Список пока пуст.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rehomeproxy-nodes-empty.png|центр|мини|900x900пкс|Вкладка «Настройки узлов» до добавления сервера.]]&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;Импортировать ссылку-конфигурацию для подключения&#039;&#039;&#039; и вставьте ссылку на ваш сервер.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rehomeproxy-import-link.png|центр|мини|600x600пкс|Окно импорта. Поддерживаются Amnezia (vpn://), Hysteria, Mieru, NaïveProxy (naive://), Shadowsocks (ss://), SSH, Trojan, v2rayN (VMess), WireGuard и VLESS (vless://).]]&lt;br /&gt;
&lt;br /&gt;
После нажатия &#039;&#039;&#039;Импорт&#039;&#039;&#039; узел появится в списке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rehomeproxy-node-added.png|центр|мини|900x900пкс|Узел добавлен: видны его тип, адрес и порт.]]&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;Применить&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Шаг 5. Выбрать основной узел ==&lt;br /&gt;
Перейдите на вкладку &#039;&#039;&#039;Настройки клиента → Настройки маршрутизации&#039;&#039;&#039; и выберите добавленный сервер в поле &#039;&#039;&#039;Основной узел&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rehomeproxy-routing-settings.png|центр|мини|900x900пкс|Основной узел выбран. Остальные параметры можно оставить по умолчанию.]]&lt;br /&gt;
&lt;br /&gt;
Остальные поля менять не нужно — значения по умолчанию подходят для большинства случаев. Нажмите &#039;&#039;&#039;Применить&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Проверка:&#039;&#039;&#039; в заголовке страницы надпись должна смениться на зелёную — &#039;&#039;&#039;Re:HomeProxy (sing-box ...) РАБОТАЕТ&#039;&#039;&#039;. Если она красная и написано &#039;&#039;&#039;NOT RUNNING&#039;&#039;&#039; — вернитесь к шагу 2, ядро не установлено или лежит не по тому пути.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Шаг 6. Установить Zapret ==&lt;br /&gt;
Zapret нужен для сервисов, которые замедляют, а не блокируют — их не обязательно гнать через сервер.&lt;br /&gt;
&lt;br /&gt;
Откройте &#039;&#039;&#039;Настройки узлов → Zapret&#039;&#039;&#039; и нажмите &#039;&#039;&#039;Установить&#039;&#039;&#039;. Панель скачает и установит всё сама.&lt;br /&gt;
&lt;br /&gt;
== Шаг 7. Настроить правила ==&lt;br /&gt;
Это главный шаг: здесь вы указываете, что и через что пускать. Откройте &#039;&#039;&#039;Настройки клиента → Правила прокси&#039;&#039;&#039; и кнопкой &#039;&#039;&#039;Добавить&#039;&#039;&#039; создайте нужные строки.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rehomeproxy-proxy-rules.png|центр|мини|900x900пкс|Три типовых правила: Telegram через основной узел, YouTube и Discord через Zapret.]]&lt;br /&gt;
&lt;br /&gt;
В каждой строке заполняются три поля: &#039;&#039;&#039;Включить&#039;&#039;&#039; (галочка), &#039;&#039;&#039;Отправитель&#039;&#039;&#039; (какой список) и &#039;&#039;&#039;Узел&#039;&#039;&#039; (через что пускать).&lt;br /&gt;
&lt;br /&gt;
Рекомендуемый набор для начала:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Отправитель&lt;br /&gt;
!Узел&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Re-filter&#039;&#039;&#039;&lt;br /&gt;
|Как основной узел&lt;br /&gt;
|Всё, что заблокировано в России (60000+ доменов и 25000+ IP)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Telegram&#039;&#039;&#039;&lt;br /&gt;
|Как основной узел&lt;br /&gt;
|Мессенджер подключается к серверам по IP, поэтому нужен именно список сервиса&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;YouTube&#039;&#039;&#039;&lt;br /&gt;
|Zapret&lt;br /&gt;
|Видео идёт напрямую и не расходует трафик сервера&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Discord&#039;&#039;&#039;&lt;br /&gt;
|Zapret&lt;br /&gt;
|То же самое; для голосовой связи надёжнее выбрать основной узел&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;Применить&#039;&#039;&#039;. Списки скачаются автоматически и дальше будут обновляться раз в сутки — вручную их вести не нужно.&lt;br /&gt;
&lt;br /&gt;
== Шаг 8. Добавить cloudflare-ech.com ==&lt;br /&gt;
Откройте &#039;&#039;&#039;Настройки клиента → Контроль доступа → Список доменов для проксирования&#039;&#039;&#039; и добавьте одну строку:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
cloudflare-ech.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:Rehomeproxy-domain-list.png|центр|мини|900x900пкс|Список доменов для проксирования. Сюда же можно дописывать свои сайты, по одному в строке.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — без этой строки часть сайтов не откроется в браузере.&#039;&#039;&#039; Современные браузеры скрывают имя сайта в защищённом соединении (технология ECH), и панель не может определить, что за сайт запрошен. В результате он идёт напрямую и блокируется. Эта строка возвращает такие соединения в туннель. Подробности — в разделе «Почему нужен cloudflare-ech.com» ниже.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;Применить&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Готово.&#039;&#039;&#039; Обход работает для всех устройств в сети — телефонов, телевизоров, компьютеров. Ничего настраивать на них не нужно.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Дополнения и тонкости =&lt;br /&gt;
Этот раздел не нужен для базовой установки — читайте по мере необходимости.&lt;br /&gt;
&lt;br /&gt;
== Как применить изменения списка доменов ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; если вы изменили &#039;&#039;&#039;только&#039;&#039;&#039; список доменов и ничего больше, кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039; может сообщить, что изменений нет, — и ядро продолжит работать со старым списком. Так устроено поле списка: оно сохраняет файл отдельно от остальных настроек.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Чтобы изменения вступили в силу, откройте вкладку &#039;&#039;&#039;Диагностика&#039;&#039;&#039; и нажмите &#039;&#039;&#039;Перезапустить службу&#039;&#039;&#039;. Либо меняйте список вместе с любой другой настройкой — тогда обычная кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039; сработает как обычно.&lt;br /&gt;
&lt;br /&gt;
== Почему нужен cloudflare-ech.com ==&lt;br /&gt;
Сайты, работающие через Cloudflare, публикуют специальную DNS-запись, после которой браузер включает &#039;&#039;&#039;ECH (Encrypted Client Hello)&#039;&#039;&#039; и &#039;&#039;&#039;шифрует имя запрашиваемого сайта&#039;&#039;&#039; внутри защищённого соединения. Панель определяет сайт именно по этому имени, поэтому распознать его не может, не находит в списках и отправляет соединение напрямую — где его и обрывает провайдер.&lt;br /&gt;
&lt;br /&gt;
Характерный признак: сайт есть в списке, но в браузере выдаёт ошибку защищённого соединения (в Firefox — &amp;lt;code&amp;gt;PR_END_OF_FILE_ERROR&amp;lt;/code&amp;gt;), при этом проверка через &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; с того же компьютера проходит успешно. Так происходит потому, что &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; технологию ECH не использует и передаёт имя сайта открыто.&lt;br /&gt;
&lt;br /&gt;
При включённом ECH скрывается только внутреннее имя, а внешнее остаётся видимым, и у всех сайтов за Cloudflare оно одинаковое — &amp;lt;code&amp;gt;cloudflare-ech.com&amp;lt;/code&amp;gt;. Добавив его в список, вы возвращаете такие соединения в туннель. Решение работает для &#039;&#039;&#039;любого устройства в сети&#039;&#039;&#039; независимо от браузера и его настроек.&lt;br /&gt;
&lt;br /&gt;
Проверить, использует ли конкретный сайт ECH (подставьте нужный адрес вместо &amp;lt;code&amp;gt;example.com&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s &#039;https://cloudflare-dns.com/dns-query?name=example.com&amp;amp;type=HTTPS&#039; -H &#039;accept: application/dns-json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Если в ответе есть &amp;lt;code&amp;gt;ech=&amp;lt;/code&amp;gt;, имя сайта будет скрыто.&lt;br /&gt;
&lt;br /&gt;
== Почему списки сервисов важнее ручных доменов ==&lt;br /&gt;
Многие приложения — мессенджеры, игры, часть мобильных программ — подключаются к своим серверам &#039;&#039;&#039;по IP-адресам, без имени сайта&#039;&#039;&#039;. Правила по доменам на такие соединения не действуют в принципе: панели нечего сопоставлять со списком.&lt;br /&gt;
&lt;br /&gt;
Готовые списки сервисов содержат не только домены, но и IP-подсети, поэтому закрывают оба случая. Показательный пример — Telegram: помимо доменов (&amp;lt;code&amp;gt;t.me&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;telegra.ph&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;telesco.pe&amp;lt;/code&amp;gt;) список включает подсети серверов (&amp;lt;code&amp;gt;149.154.160.0/20&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;91.108.4.0/22&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;91.108.56.0/22&amp;lt;/code&amp;gt; и другие). Без них веб-версия откроется, а сам мессенджер подключиться не сможет.&lt;br /&gt;
&lt;br /&gt;
Поэтому для мессенджеров и приложений всегда добавляйте &#039;&#039;&#039;готовое правило сервиса&#039;&#039;&#039; в «Правила прокси», а не отдельные домены в список.&lt;br /&gt;
&lt;br /&gt;
== Zapret не работает сам по себе ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; установка Zapret ничего не обходит сама по себе. Он обрабатывает только тот трафик, который вы явно на него направили правилом в &#039;&#039;&#039;Правилах прокси&#039;&#039;&#039;. Пока ни одно правило не указывает на &#039;&#039;&#039;Zapret&#039;&#039;&#039;, он просто запущен и бездействует.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить, что трафик действительно пошёл в обработку:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nft list chain inet fw4 homeproxy_zapret_queue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ненулевые счётчики пакетов в строках правил означают, что механизм работает.&lt;br /&gt;
&lt;br /&gt;
Стратегия обхода по умолчанию рассчитана на типовые ограничения российских провайдеров. Если сервис всё равно не открывается — на вкладке &#039;&#039;&#039;Zapret&#039;&#039;&#039; есть тестер стратегий: он проверяет разные варианты и показывает, какие работают у вашего провайдера.&lt;br /&gt;
&lt;br /&gt;
== Диагностика ==&lt;br /&gt;
Вкладка &#039;&#039;&#039;Диагностика&#039;&#039;&#039; показывает состояние ядра, версию, запущенные службы, занятые порты и позволяет проверить доступность контрольных сайтов.&lt;br /&gt;
&lt;br /&gt;
Из командной строки — состояние службы:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Журнал событий:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tail -30 /var/run/homeproxy/homeproxy.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Журнал ядра:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tail -30 /var/run/homeproxy/hiddify-c.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение:&#039;&#039;&#039; множество строк &amp;lt;code&amp;gt;connection refused&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;i/o timeout&amp;lt;/code&amp;gt; на портах &amp;lt;code&amp;gt;6881&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;51413&amp;lt;/code&amp;gt; и произвольных высоких портах — это обычно торренты: клиент перебирает источники, часть которых недоступна. К работе туннеля отношения не имеет.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Как узнать, каким путём ушло соединение ===&lt;br /&gt;
Временно переключите &#039;&#039;&#039;Уровень журнала&#039;&#039;&#039; на &amp;lt;code&amp;gt;info&amp;lt;/code&amp;gt;, откройте сайт с любого устройства и посмотрите записи вида:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
outbound/vless[main-out]: outbound connection to 172.67.182.196:443&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Обозначение в скобках показывает путь: &amp;lt;code&amp;gt;main-out&amp;lt;/code&amp;gt; — через ваш сервер, &amp;lt;code&amp;gt;direct-out&amp;lt;/code&amp;gt; — напрямую, &amp;lt;code&amp;gt;zapret-out&amp;lt;/code&amp;gt; — с обходом DPI. После проверки верните уровень &amp;lt;code&amp;gt;warn&amp;lt;/code&amp;gt;, иначе журнал быстро разрастается.&lt;br /&gt;
&lt;br /&gt;
=== Проверка с устройства в сети ===&lt;br /&gt;
Проверять нужно с обычного компьютера или телефона, а не с самого роутера — его собственный трафик обрабатывается иначе.&lt;br /&gt;
&lt;br /&gt;
Какой адрес видит внешний мир:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Доступность сайта (подставьте нужный адрес):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s -o /dev/null -m 15 -w &#039;HTTP %{http_code} за %{time_total}s\n&#039; https://example.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Типовые неполадки =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина и решение&lt;br /&gt;
|-&lt;br /&gt;
|В заголовке панели красное &#039;&#039;&#039;NOT RUNNING&#039;&#039;&#039;&lt;br /&gt;
|Ядро не установлено или лежит не по пути &amp;lt;code&amp;gt;/usr/bin/sing-box&amp;lt;/code&amp;gt; — см. Шаг 2&lt;br /&gt;
|-&lt;br /&gt;
|Служба запущена, но ничего не проксируется&lt;br /&gt;
|Не выбран &#039;&#039;&#039;Основной узел&#039;&#039;&#039; — см. Шаг 5&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается &#039;&#039;&#039;в браузере&#039;&#039;&#039;, хотя &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; работает&lt;br /&gt;
|Не добавлен &amp;lt;code&amp;gt;cloudflare-ech.com&amp;lt;/code&amp;gt; — см. Шаг 8&lt;br /&gt;
|-&lt;br /&gt;
|Изменил список доменов, ничего не поменялось&lt;br /&gt;
|&#039;&#039;&#039;Диагностика → Перезапустить службу&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Мессенджер не подключается, хотя его сайт открывается&lt;br /&gt;
|Нужно готовое правило сервиса с IP-подсетями, а не домены&lt;br /&gt;
|-&lt;br /&gt;
|Zapret включён, но сервис по-прежнему замедлен&lt;br /&gt;
|Нет правила, направляющего трафик на Zapret&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; отсутствует в списке типов узлов&lt;br /&gt;
|Ядро установлено из репозитория OpenWrt. Нужна musl-сборка — см. Шаг 2&lt;br /&gt;
|-&lt;br /&gt;
|Панель открывается по несколько секунд&lt;br /&gt;
|Ядро сжато упаковщиком UPX — замените на обычный файл&lt;br /&gt;
|-&lt;br /&gt;
|Страницы «висят» по 30 секунд, потом открываются&lt;br /&gt;
|Браузер уходит в QUIC мимо перехвата. Заблокируйте UDP/443 действием &#039;&#039;&#039;REJECT&#039;&#039;&#039; (не DROP)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Удаление =&lt;br /&gt;
Остановить службу:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy stop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Убрать из автозапуска:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy disable&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удалить панель:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg remove luci-app-re-homeproxy luci-i18n-homeproxy-ru&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удалить ядро:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rm -f /usr/bin/sing-box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удалить настройки и списки:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rm -rf /etc/homeproxy /etc/config/homeproxy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Правила межсетевого экрана снимаются автоматически при остановке службы. Проброс портов и остальные настройки роутера не затрагиваются.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Ссылки =&lt;br /&gt;
* [https://github.com/1andrevich/homeproxy-hiddify Re:HomeProxy — исходный код и релизы]&lt;br /&gt;
* [https://github.com/shtorm-7/sing-box-extended sing-box-extended — ядро]&lt;br /&gt;
* [https://github.com/1andrevich/Re-filter-lists Re-filter — список заблокированных ресурсов]&lt;br /&gt;
* [https://github.com/itdoginfo/allow-domains itdoginfo/allow-domains — списки по сервисам]&lt;br /&gt;
* [https://github.com/bol-van/zapret2 Zapret 2]&lt;br /&gt;
* [https://github.com/hufrea/byedpi ByeDPI]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Сеть]]&lt;br /&gt;
[[Категория:OpenWrt]]&lt;br /&gt;
[[Категория:Обход блокировок]]&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-import-link.png&amp;diff=1044</id>
		<title>Файл:Rehomeproxy-import-link.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-import-link.png&amp;diff=1044"/>
		<updated>2026-08-14T11:56:55Z</updated>

		<summary type="html">&lt;p&gt;Владимир: == Краткое описание ==
Скриншоты интерфейса Re:HomeProxy
&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Краткое описание ==&lt;br /&gt;
Скриншоты интерфейса Re:HomeProxy&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-routing-settings.png&amp;diff=1043</id>
		<title>Файл:Rehomeproxy-routing-settings.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-routing-settings.png&amp;diff=1043"/>
		<updated>2026-08-14T11:56:55Z</updated>

		<summary type="html">&lt;p&gt;Владимир: == Краткое описание ==
Скриншоты интерфейса Re:HomeProxy
&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Краткое описание ==&lt;br /&gt;
Скриншоты интерфейса Re:HomeProxy&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-node-added.png&amp;diff=1042</id>
		<title>Файл:Rehomeproxy-node-added.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-node-added.png&amp;diff=1042"/>
		<updated>2026-08-14T11:56:54Z</updated>

		<summary type="html">&lt;p&gt;Владимир: == Краткое описание ==
Скриншоты интерфейса Re:HomeProxy
&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Краткое описание ==&lt;br /&gt;
Скриншоты интерфейса Re:HomeProxy&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-domain-list.png&amp;diff=1041</id>
		<title>Файл:Rehomeproxy-domain-list.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-domain-list.png&amp;diff=1041"/>
		<updated>2026-08-14T11:56:54Z</updated>

		<summary type="html">&lt;p&gt;Владимир: == Краткое описание ==
Скриншоты интерфейса Re:HomeProxy
&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Краткое описание ==&lt;br /&gt;
Скриншоты интерфейса Re:HomeProxy&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-nodes-empty.png&amp;diff=1040</id>
		<title>Файл:Rehomeproxy-nodes-empty.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-nodes-empty.png&amp;diff=1040"/>
		<updated>2026-08-14T11:56:54Z</updated>

		<summary type="html">&lt;p&gt;Владимир: == Краткое описание ==
Скриншоты интерфейса Re:HomeProxy
&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Краткое описание ==&lt;br /&gt;
Скриншоты интерфейса Re:HomeProxy&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-proxy-rules.png&amp;diff=1039</id>
		<title>Файл:Rehomeproxy-proxy-rules.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Rehomeproxy-proxy-rules.png&amp;diff=1039"/>
		<updated>2026-08-14T11:56:54Z</updated>

		<summary type="html">&lt;p&gt;Владимир: == Краткое описание ==
Скриншоты интерфейса Re:HomeProxy
&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Краткое описание ==&lt;br /&gt;
Скриншоты интерфейса Re:HomeProxy&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Re:HomeProxy_%D0%BD%D0%B0_OpenWrt:_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D0%BA_%D0%B8_%D0%B3%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&amp;diff=1038</id>
		<title>Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Re:HomeProxy_%D0%BD%D0%B0_OpenWrt:_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D0%BA_%D0%B8_%D0%B3%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&amp;diff=1038"/>
		<updated>2026-08-14T11:14:58Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Все команды разбиты на однострочные&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация =&lt;br /&gt;
Подробная пошаговая инструкция по развёртыванию &#039;&#039;&#039;Re:HomeProxy&#039;&#039;&#039; на чистом роутере с OpenWrt — от установки пакета до работающей выборочной маршрутизации. Рассчитана на пользователя, который умеет работать в терминале, но не знаком с внутренним устройством роутерных систем.&lt;br /&gt;
&lt;br /&gt;
Проверено на &#039;&#039;&#039;Xiaomi Redmi Router AX6000&#039;&#039;&#039; (&amp;lt;code&amp;gt;aarch64_cortex-a53&amp;lt;/code&amp;gt;, mediatek/filogic) с &#039;&#039;&#039;OpenWrt 24.10.0&#039;&#039;&#039;. Подходит для любого устройства с OpenWrt 24.10 (opkg) или 25.12+ (apk).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как читать команды в этой инструкции:&#039;&#039;&#039; каждая команда приведена &#039;&#039;&#039;отдельной строкой&#039;&#039;&#039; и выполняется самостоятельно — копируйте и вставляйте их по одной, дожидаясь завершения предыдущей. Ни одна команда не разбита переносами и не требует правки перед запуском, кроме тех, где явно указано, что нужно подставить своё значение.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Одна панель в LuCI, которая управляет всем стеком обхода блокировок: туннелем, списками маршрутизации и обходом DPI без VPN.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Re:HomeProxy&#039;&#039;&#039; (&amp;lt;code&amp;gt;luci-app-re-homeproxy&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Веб-панель в LuCI. Форк ImmortalWrt HomeProxy. Управляет узлами, списками маршрутизации, правилами доступа и обходом DPI. Сама по себе трафик не обрабатывает — генерирует конфигурацию для ядра и правила nftables.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box-extended&#039;&#039;&#039;&lt;br /&gt;
|Ядро (движок). Форк sing-box с расширенным набором протоколов: VLESS (в т.ч. REALITY, XHTTP), Hysteria2, Trojan, Shadowsocks, ShadowTLS, AnyTLS, WireGuard, AmneziaWG, &#039;&#039;&#039;Mieru&#039;&#039;&#039;, &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039;, SSH и др.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Списки маршрутизации&#039;&#039;&#039;&lt;br /&gt;
|Готовые наборы доменов и IP (Re-filter, Russia Inside, точечные по сервисам). Скачиваются ядром автоматически и обновляются раз в сутки.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Zapret 2&#039;&#039;&#039; (&amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обход DPI без туннеля: искажает TLS-рукопожатие «на лету» через NFQUEUE. Подходит для замедленных, но не заблокированных сервисов — трафик идёт напрямую, минуя VPS.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ByeDPI&#039;&#039;&#039; (&amp;lt;code&amp;gt;ciadpi&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Альтернативный движок обхода DPI, работает как локальный SOCKS5-прокси. 47 пресетов стратегий и встроенный тестер.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Принцип работы ===&lt;br /&gt;
В основе — разделение на две независимые оси:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Списки&#039;&#039;&#039; решают, &#039;&#039;какой&#039;&#039; трафик особенный.&lt;br /&gt;
* &#039;&#039;&#039;Узел / Zapret / ByeDPI&#039;&#039;&#039; решают, &#039;&#039;через что&#039;&#039; его пустить.&lt;br /&gt;
&lt;br /&gt;
Они комбинируются: каждое правило — это пара «список → способ доставки». Например, заблокированные РКН сайты идут через туннель, а замедленный YouTube — напрямую с лечением DPI, не нагружая VPS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание:&#039;&#039;&#039; Весь трафик LAN перехватывается через nftables (&amp;lt;code&amp;gt;redirect&amp;lt;/code&amp;gt; для TCP, &amp;lt;code&amp;gt;tproxy&amp;lt;/code&amp;gt; для UDP) и передаётся ядру. Ядро определяет домен по SNI из TLS-рукопожатия либо по DNS-ответу, сверяет со списками и выбирает маршрут. Всё, что не попало ни в один список, идёт напрямую.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|OpenWrt / ImmortalWrt &#039;&#039;&#039;24.10&#039;&#039;&#039; и новее (менеджер пакетов &amp;lt;code&amp;gt;opkg&amp;lt;/code&amp;gt;) либо &#039;&#039;&#039;25.12&#039;&#039;&#039; и новее (&amp;lt;code&amp;gt;apk&amp;lt;/code&amp;gt;). Для 23.05 в разделе Releases есть legacy-сборка с суффиксом &amp;lt;code&amp;gt;_all-legacy.ipk&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Свободное место&#039;&#039;&#039;&lt;br /&gt;
|Минимум &#039;&#039;&#039;40 МБ&#039;&#039;&#039; на overlay при сжимающей файловой системе (ubifs/jffs2), либо &#039;&#039;&#039;90 МБ&#039;&#039;&#039; на несжимающей (ext4/f2fs). Основной объём — ядро.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оперативная память&#039;&#039;&#039;&lt;br /&gt;
|От &#039;&#039;&#039;256 МБ&#039;&#039;&#039;. Ядро — крупное Go-приложение.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Архитектура&#039;&#039;&#039;&lt;br /&gt;
|Любая, для которой собран пакет ядра: aarch64 (cortex-a53/a72/a76, generic), x86_64, arm, mips/mipsel.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ&#039;&#039;&#039;&lt;br /&gt;
|Веб-интерфейс LuCI и SSH-доступ к роутеру.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Узел (опционально)&#039;&#039;&#039;&lt;br /&gt;
|Сервер с VLESS / Hysteria2 / Trojan / Mieru / NaïveProxy и т.п. Нужен только для туннеля — обход DPI через Zapret и ByeDPI работает и без него.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — как правильно оценить свободное место.&#039;&#039;&#039; Команда &amp;lt;code&amp;gt;df -h&amp;lt;/code&amp;gt; показывает несколько разделов, и легко перепутать. Ориентироваться нужно &#039;&#039;&#039;только&#039;&#039;&#039; на строку с точкой монтирования &amp;lt;code&amp;gt;/overlay&amp;lt;/code&amp;gt; (или &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;). Раздел &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt;, смонтированный в &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; — это &#039;&#039;&#039;оперативная память&#039;&#039;&#039; (обычно половина от объёма ОЗУ), а не хранилище: он очищается при перезагрузке и для установки не годится, хотя его размер часто выглядит самым внушительным в выводе.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Проверка перед установкой ===&lt;br /&gt;
Подключитесь к роутеру по SSH и выполните команды по очереди.&lt;br /&gt;
&lt;br /&gt;
Версия OpenWrt и архитектура устройства:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat /etc/openwrt_release&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Свободное место — смотреть строку с &amp;lt;code&amp;gt;/overlay&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -h&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тип файловой системы overlay (&amp;lt;code&amp;gt;ubifs&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;jffs2&amp;lt;/code&amp;gt; сжимают файлы, &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;f2fs&amp;lt;/code&amp;gt; — нет):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mount | grep /overlay&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Объём оперативной памяти:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
free -m&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение про сжатие.&#039;&#039;&#039; На &#039;&#039;&#039;ubifs&#039;&#039;&#039; и &#039;&#039;&#039;jffs2&#039;&#039;&#039; файлы сжимаются прозрачно, и крупный бинарник ядра занимает примерно в 2–3 раза меньше своего номинального размера. Проверять реальный расход нужно по &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; &#039;&#039;&#039;до и после&#039;&#039;&#039; установки: команда &amp;lt;code&amp;gt;du&amp;lt;/code&amp;gt; на этих ФС показывает логический размер файла, а не занятое место.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка панели =&lt;br /&gt;
&lt;br /&gt;
== 1.1. Зависимости ==&lt;br /&gt;
Панели требуются четыре пакета: &amp;lt;code&amp;gt;libc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;firewall4&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kmod-nft-tproxy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ucode-mod-digest&amp;lt;/code&amp;gt;. В типовой сборке OpenWrt 24.10 первые три уже присутствуют.&lt;br /&gt;
&lt;br /&gt;
Обновить список пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить недостающее:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg install ucode-mod-digest kmod-nft-tproxy kmod-tun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;kmod-tun&amp;lt;/code&amp;gt; нужен, если планируется режим TUN; для базового режима &amp;lt;code&amp;gt;Redirect + TProxy&amp;lt;/code&amp;gt; достаточно &amp;lt;code&amp;gt;kmod-nft-tproxy&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== 1.2. Установка пакета ==&lt;br /&gt;
Пакет ставится не из штатных репозиториев, а из релизов проекта. Актуальную версию можно посмотреть на [https://github.com/1andrevich/homeproxy-hiddify/releases странице релизов] — в командах ниже она указана как &amp;lt;code&amp;gt;2026.06.29-r1&amp;lt;/code&amp;gt;, при необходимости подставьте свою.&lt;br /&gt;
&lt;br /&gt;
Перейти в каталог для загрузок:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скачать пакет панели:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
wget -O rhp.ipk https://github.com/1andrevich/homeproxy-hiddify/releases/download/2026.06.29-r1/luci-app-re-homeproxy_2026.06.29-r1_all.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg install /tmp/rhp.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скачать русскую локализацию интерфейса:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
wget -O rhp-ru.ipk https://github.com/1andrevich/homeproxy-hiddify/releases/download/2026.06.29-r1/luci-i18n-homeproxy-ru_2026.06.29-r1_all.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить локализацию:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg install /tmp/rhp-ru.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Для OpenWrt 25.12 и новее&#039;&#039;&#039; в тех же ссылках замените расширение &amp;lt;code&amp;gt;.ipk&amp;lt;/code&amp;gt; на &amp;lt;code&amp;gt;.apk&amp;lt;/code&amp;gt;, а команду установки — на &amp;lt;code&amp;gt;apk add --allow-untrusted /tmp/rhp.apk&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сам пакет панели весит около &#039;&#039;&#039;650 КБ&#039;&#039;&#039; — основное место займёт ядро на следующем этапе.&lt;br /&gt;
&lt;br /&gt;
== 1.3. Проверка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg list-installed | grep homeproxy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки в LuCI появится раздел &#039;&#039;&#039;Сервисы → Re:HomeProxy&#039;&#039;&#039;. Если пункт меню не виден — обновите страницу с очисткой кэша (&amp;lt;code&amp;gt;Ctrl+F5&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка ядра =&lt;br /&gt;
Панель без ядра не работает: на её странице будет висеть красная надпись &#039;&#039;&#039;NOT RUNNING&#039;&#039;&#039;, а в журнале — &amp;lt;code&amp;gt;No supported core found&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Выбор ядра ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Ядро&lt;br /&gt;
!Особенности&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;hiddify-core&#039;&#039;&#039;&lt;br /&gt;
|Вариант по умолчанию. Ставится в один клик, есть компактная (UPX) сборка для устройств с малым объёмом флеш-памяти.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box-extended&#039;&#039;&#039;&lt;br /&gt;
|Самый широкий набор протоколов: AmneziaWG / WARP, Mieru, NaïveProxy, TrustTunnel, MASQUE, Snell, MTProxy. &#039;&#039;&#039;Рекомендуется&#039;&#039;&#039;, если нужны эти протоколы.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 2.2. Установка в один клик ==&lt;br /&gt;
В панели: &#039;&#039;&#039;Core &amp;amp; Tools&#039;&#039;&#039; (в русской локализации — &#039;&#039;&#039;Ядро и службы&#039;&#039;&#039;) → выбрать ядро → &#039;&#039;&#039;Установить&#039;&#039;&#039;. Панель сама определит архитектуру, проверит свободное место, скачает и установит пакет.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — в OpenWrt-сборке sing-box-extended нет NaïveProxy.&#039;&#039;&#039; Пакет для OpenWrt собирается без флага &amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt;, поэтому пункт &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; в списке типов узлов не появится, а попытка использовать такой узел даст ошибку &amp;lt;code&amp;gt;naive outbound is not included in this build&amp;lt;/code&amp;gt;. Если NaïveProxy нужен — устанавливайте ядро вручную по инструкции ниже. Mieru в OpenWrt-сборке присутствует.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.3. Ручная установка ядра с поддержкой NaïveProxy ==&lt;br /&gt;
Вместо пакета для OpenWrt берётся ассет &amp;lt;code&amp;gt;linux-&amp;lt;арх&amp;gt;-musl&amp;lt;/code&amp;gt; из того же релиза. Он собран с флагами &amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;with_musl&amp;lt;/code&amp;gt;, библиотека cronet влинкована статически, поэтому дополнительных файлов не требуется. OpenWrt тоже построен на musl, и бинарник запускается как есть.&lt;br /&gt;
&lt;br /&gt;
Актуальную версию и список файлов смотрите на [https://github.com/shtorm-7/sing-box-extended/releases странице релизов ядра]. В командах ниже используется версия &amp;lt;code&amp;gt;1.13.18-extended-2.6.5&amp;lt;/code&amp;gt; и архитектура &amp;lt;code&amp;gt;arm64&amp;lt;/code&amp;gt; — подставьте свои значения.&lt;br /&gt;
&lt;br /&gt;
Перейти в каталог для загрузок (это оперативная память, флеш не расходуется):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скачать musl-сборку ядра:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
wget -O sb.tar.gz https://github.com/shtorm-7/sing-box-extended/releases/download/v1.13.18-extended-2.6.5/sing-box-1.13.18-extended-2.6.5-linux-arm64-musl.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Распаковать архив:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tar xzf sb.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Перейти в распакованный каталог:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp/sing-box-1.13.18-extended-2.6.5-linux-arm64-musl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделать файл исполняемым:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod +x sing-box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить флаги сборки &#039;&#039;&#039;до&#039;&#039;&#039; установки:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
./sing-box version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В строке &amp;lt;code&amp;gt;Tags:&amp;lt;/code&amp;gt; должны присутствовать &amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt; (для NaïveProxy) и &amp;lt;code&amp;gt;with_quic&amp;lt;/code&amp;gt; (для Mieru и Hysteria2).&lt;br /&gt;
&lt;br /&gt;
Записать свободное место, чтобы потом сравнить:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -h /&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установить ядро:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cp sing-box /usr/bin/sing-box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделать исполняемым:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod +x /usr/bin/sing-box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сбросить кэш записи на флеш:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sync&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оценить реальный расход места:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -h /&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить, что установленное ядро запускается:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/usr/bin/sing-box version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Убрать временные файлы:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rm -rf /tmp/sing-box-1.13.18-extended-2.6.5-linux-arm64-musl /tmp/sb.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — путь строго &amp;lt;code&amp;gt;/usr/bin/sing-box&amp;lt;/code&amp;gt;.&#039;&#039;&#039; В конфигурации есть параметры &amp;lt;code&amp;gt;custom_core_path&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;custom_core_type&amp;lt;/code&amp;gt;, но стартовый скрипт &amp;lt;code&amp;gt;/etc/init.d/homeproxy&amp;lt;/code&amp;gt; их не читает — в нём жёстко задано &amp;lt;code&amp;gt;PROG_SINGBOX=&amp;quot;/usr/bin/sing-box&amp;quot;&amp;lt;/code&amp;gt;. Ядро, положенное в произвольный каталог, панель в статусе покажет, но сервис с ним не запустится.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — не сжимайте ядро упаковщиком UPX.&#039;&#039;&#039; Сжатый бинарник распаковывается в оперативную память &#039;&#039;&#039;при каждом запуске процесса&#039;&#039;&#039;, а панель вызывает &amp;lt;code&amp;gt;sing-box version&amp;lt;/code&amp;gt; при каждой отрисовке своих страниц (так она определяет доступные протоколы по флагам сборки). В результате интерфейс становится непригодным для работы: замер на aarch64 даёт &#039;&#039;&#039;3,1 с&#039;&#039;&#039; против &#039;&#039;&#039;0,05 с&#039;&#039;&#039; на каждый вызов — разница в шестьдесят раз. На скорость трафика это не влияет (ядро стартует один раз), страдает только отзывчивость панели. Экономия при этом невелика: на сжимающей файловой системе выигрыш составляет около 19 МБ.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — как проверить ядро, ничего не устанавливая.&#039;&#039;&#039; Каталог &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; на OpenWrt находится в оперативной памяти. Скачанный туда бинарник можно запускать прямо на месте — командами &amp;lt;code&amp;gt;./sing-box version&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;./sing-box check -c конфиг.json&amp;lt;/code&amp;gt;. Так проверяются и флаги сборки, и совместимость конфигурации, и живой трафик, а флеш-память роутера при этом не затрагивается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.4. Проверка, что панель видит ядро ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ubus call luci.homeproxy singbox_get_features&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В ответе должны быть &amp;lt;code&amp;gt;&amp;quot;core_type&amp;quot;: &amp;quot;singbox&amp;quot;&amp;lt;/code&amp;gt;, версия и набор флагов. Ключевые:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Флаг&lt;br /&gt;
!Что даёт&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt;&lt;br /&gt;
|Пункт &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; в списке типов узлов&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;with_quic&amp;lt;/code&amp;gt;&lt;br /&gt;
|&#039;&#039;&#039;Mieru&#039;&#039;&#039;, &#039;&#039;&#039;Hysteria&#039;&#039;&#039;, &#039;&#039;&#039;Hysteria2&#039;&#039;&#039;, &#039;&#039;&#039;TUIC&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hp_has_tproxy&amp;lt;/code&amp;gt;&lt;br /&gt;
|Режим &#039;&#039;&#039;TProxy&#039;&#039;&#039; для UDP&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hp_has_tun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Режим &#039;&#039;&#039;TUN&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Запуск и добавление узла =&lt;br /&gt;
&lt;br /&gt;
== 3.1. Запуск службы ==&lt;br /&gt;
Включить автозапуск:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy enable&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Запустить:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить состояние:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание:&#039;&#039;&#039; Статус &amp;lt;code&amp;gt;active with no instances&amp;lt;/code&amp;gt; и запись &amp;lt;code&amp;gt;no main_node configured, skipping config generation&amp;lt;/code&amp;gt; в журнале — это &#039;&#039;&#039;нормальное&#039;&#039;&#039; состояние до того, как задан основной узел. Ядро запускается только при выбранном узле. Пока узел не задан, трафик не перехватывается и сеть работает как обычно.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Добавление узла ==&lt;br /&gt;
&#039;&#039;&#039;Node Settings&#039;&#039;&#039; → &#039;&#039;&#039;Добавить&#039;&#039;&#039;. Поддерживаются два пути:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Импорт ссылки или подписки&#039;&#039;&#039; — вставить &amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;naive+https://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss://&amp;lt;/code&amp;gt; и т.п., либо URL подписки (sing-box JSON / Hiddify, base64, обычные share-ссылки, конфигурации Xray/V2Ray).&lt;br /&gt;
* &#039;&#039;&#039;Ручное заполнение&#039;&#039;&#039; — выбрать тип и заполнить поля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение по Mieru:&#039;&#039;&#039; диапазон портов в поле &#039;&#039;&#039;Port range&#039;&#039;&#039; записывается &#039;&#039;&#039;через дефис&#039;&#039;&#039; — &amp;lt;code&amp;gt;2012-2022&amp;lt;/code&amp;gt;. Форматы &amp;lt;code&amp;gt;2012:2022&amp;lt;/code&amp;gt; и одиночное значение в поле диапазона дают ошибку &amp;lt;code&amp;gt;invalid server_ports format&amp;lt;/code&amp;gt;. Одиночный порт без диапазона задаётся в обычном поле порта.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.3. Основные параметры ==&lt;br /&gt;
&#039;&#039;&#039;Client Settings → Routing Settings&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Рекомендация&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Main node&#039;&#039;&#039;&lt;br /&gt;
|Созданный узел. Значение &#039;&#039;&#039;Отключить&#039;&#039;&#039; оставляет всю маршрутизацию прямой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Routing mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Russia (Proxy Banned)&#039;&#039;&#039; — по умолчанию всё идёт напрямую, через туннель — только попавшее в списки.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Proxy mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Redirect TCP + TProxy UDP&#039;&#039;&#039; — базовый режим, не требует TUN.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Russia DNS server&#039;&#039;&#039;&lt;br /&gt;
|Быстрый российский резолвер для доменов, идущих напрямую.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Secure DNS server&#039;&#039;&#039;&lt;br /&gt;
|DoH/DoT-резолвер для проксируемых доменов — провайдер не видит, какие сайты запрашиваются.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Do not proxify torrents&#039;&#039;&#039;&lt;br /&gt;
|Включить: торренты пойдут напрямую и не будут нагружать туннель.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После изменений — &#039;&#039;&#039;Применить&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Списки маршрутизации =&lt;br /&gt;
Списки определяют, какой трафик считать особенным. Есть два механизма, и они дополняют друг друга.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Автообновляемые списки (основной способ) ==&lt;br /&gt;
&#039;&#039;&#039;Client Settings → Proxy Rules → Добавить&#039;&#039;&#039;. Каждое правило — строка из трёх полей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enable&#039;&#039;&#039;&lt;br /&gt;
|галочка&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Source ⤵️&#039;&#039;&#039;&lt;br /&gt;
|источник списка&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Node 🔗&#039;&#039;&#039;&lt;br /&gt;
|способ доставки: &#039;&#039;&#039;Same as main node&#039;&#039;&#039;, &#039;&#039;&#039;Separate URLTest&#039;&#039;&#039;, конкретный узел, &#039;&#039;&#039;ByeDPI&#039;&#039;&#039; или &#039;&#039;&#039;Zapret&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Доступные источники:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Источник&lt;br /&gt;
!Содержимое&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Re-filter&#039;&#039;&#039;&lt;br /&gt;
|Более 60000 доменов и 25000 IP — сводный перечень ресурсов, заблокированных в России&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Russia Inside&#039;&#039;&#039;&lt;br /&gt;
|Свыше 1000 записей: базовый набор сервисов, которым в России нужен туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Точечные&#039;&#039;&#039;&lt;br /&gt;
|YouTube, Discord, Telegram, Meta, TikTok, Twitter/X, Roblox, HDRezka, Google AI, Google Play, Cloudflare, CloudFront, Hetzner, OVH, DigitalOcean, аниме-стриминги, новостные сайты и др.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Такие списки ядро скачивает само и &#039;&#039;&#039;обновляет раз в сутки&#039;&#039;&#039; (&amp;lt;code&amp;gt;update_interval: 1d&amp;lt;/code&amp;gt;) — поддерживать их вручную не нужно.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание про приоритет:&#039;&#039;&#039; порядок правил задавать не нужно, панель расставляет их сама: сначала точечные списки по сервисам, затем Russia Inside, последним — самый широкий Re-filter. Благодаря этому частное правило всегда выигрывает у общего, и, например, YouTube можно отправить через Zapret, а всё остальное из Re-filter — через туннель.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение — почему списки по сервисам важнее ручных доменов.&#039;&#039;&#039; Многие приложения подключаются к своим серверам &#039;&#039;&#039;по IP-адресам, без доменного имени&#039;&#039;&#039; — так работают мессенджеры, игровые клиенты и часть мобильных приложений. Доменные правила на такие соединения не действуют в принципе: ядру нечего сопоставлять со списком. Готовые списки по сервисам содержат, помимо доменов, ещё и IP-подсети, поэтому закрывают оба случая. Показательный пример — Telegram: список включает домены (&amp;lt;code&amp;gt;t.me&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;telegra.ph&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;telesco.pe&amp;lt;/code&amp;gt;) и подсети дата-центров (&amp;lt;code&amp;gt;149.154.160.0/20&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;91.108.4.0/22&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;91.108.56.0/22&amp;lt;/code&amp;gt; и другие). Без подсетей веб-версия откроется, а сам мессенджер подключиться не сможет.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4.2. Ручной список доменов ==&lt;br /&gt;
&#039;&#039;&#039;Access Control → Proxy Domain List&#039;&#039;&#039; — произвольный перечень доменов, по одному в строке. Имеет смысл для того, чего нет в готовых списках.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — правка только списков не перезапускает ядро.&#039;&#039;&#039; Поле ручного списка не связано с конфигурацией UCI: оно записывает файл напрямую. LuCI отслеживает изменения именно в UCI, поэтому после правки одного лишь списка панель отвечает, что изменений нет, и ядро продолжает работать со старой конфигурацией. Применить изменения можно двумя способами: перейти на вкладку &#039;&#039;&#039;Диагностика&#039;&#039;&#039; и нажать &#039;&#039;&#039;Перезапустить службу&#039;&#039;&#039;, либо изменить список вместе с любой настройкой на других вкладках — тогда сработает обычная кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4.3. Обязательный пункт: ECH ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — без этой записи проксирование не работает в браузерах.&#039;&#039;&#039; Добавьте в &#039;&#039;&#039;Proxy Domain List&#039;&#039;&#039; отдельной строкой: &amp;lt;code&amp;gt;cloudflare-ech.com&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Суть проблемы.&#039;&#039;&#039; Сайты за Cloudflare публикуют DNS-запись типа HTTPS (RR 65) с параметром &amp;lt;code&amp;gt;ech=&amp;lt;/code&amp;gt;. Получив её, современный браузер применяет &#039;&#039;&#039;ECH (Encrypted Client Hello)&#039;&#039;&#039; и &#039;&#039;&#039;шифрует имя сервера (SNI)&#039;&#039;&#039; в TLS-рукопожатии. Ядро определяет домен именно по SNI, поэтому распознать сайт не может, не находит его в списках и отправляет соединение напрямую — где его и обрывает DPI.&lt;br /&gt;
&lt;br /&gt;
Характерная картина: сайт есть в списке, но в браузере выдаёт ошибку установления защищённого соединения (в Firefox — &amp;lt;code&amp;gt;PR_END_OF_FILE_ERROR&amp;lt;/code&amp;gt;), тогда как &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; с того же устройства на тот же адрес работает и корректно уходит через туннель. Расхождение объясняется просто: &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; не поддерживает ECH и отправляет открытый SNI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Почему помогает одна строка.&#039;&#039;&#039; При ECH шифруется только внутреннее имя, а внешнее остаётся видимым — и для всех сайтов за Cloudflare оно равно &amp;lt;code&amp;gt;cloudflare-ech.com&amp;lt;/code&amp;gt;. Ядро видит его, находит в списке и отправляет соединение в туннель. Решение действует на &#039;&#039;&#039;любого клиента сети&#039;&#039;&#039; независимо от браузера и его настроек DNS — настраивать устройства пользователей не требуется.&lt;br /&gt;
&lt;br /&gt;
Проверить, использует ли конкретный домен ECH, можно с любой машины (подставьте нужный домен вместо &amp;lt;code&amp;gt;example.com&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s &#039;https://cloudflare-dns.com/dns-query?name=example.com&amp;amp;type=HTTPS&#039; -H &#039;accept: application/dns-json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Наличие &amp;lt;code&amp;gt;ech=&amp;lt;/code&amp;gt; в ответе означает, что у этого домена SNI будет скрыт.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 5. Обход DPI без туннеля =&lt;br /&gt;
Для сервисов, которые не заблокированы, а &#039;&#039;&#039;замедлены&#039;&#039;&#039; (характерный пример — видеохостинги), туннель избыточен: он тратит трафик VPS и режет скорость. Достаточно исказить рукопожатие так, чтобы DPI не смог его разобрать.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Движок&lt;br /&gt;
!Как встроен&lt;br /&gt;
!Особенности&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Zapret 2&#039;&#039;&#039; (&amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Исходящее соединение помечается меткой маршрутизации, nftables отправляет первые пакеты в NFQUEUE, где &amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt; искажает рукопожатие&lt;br /&gt;
|Работает с QUIC; трафик идёт напрямую и не шифруется&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ByeDPI&#039;&#039;&#039; (&amp;lt;code&amp;gt;ciadpi&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Локальный SOCKS5-прокси на &amp;lt;code&amp;gt;127.0.0.1:5335&amp;lt;/code&amp;gt;, подключается как обычный узел типа socks&lt;br /&gt;
|47 пресетов стратегий, не требует NFQUEUE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5.1. Установка ==&lt;br /&gt;
На вкладке &#039;&#039;&#039;Client Settings&#039;&#039;&#039; включите &#039;&#039;&#039;Enable Zapret&#039;&#039;&#039; или &#039;&#039;&#039;Enable ByeDPI&#039;&#039;&#039; — панель предложит установить недостающий пакет и сделает это сама. Пакеты небольшие.&lt;br /&gt;
&lt;br /&gt;
== 5.2. Обязательный шаг: назначить трафик ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — сама по себе галочка ничего не обходит.&#039;&#039;&#039; В этой панели &amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt; запускается &#039;&#039;&#039;без собственных хостлистов&#039;&#039;&#039; и обрабатывает только то, что ему явным образом направят. Какие соединения попадут в очередь, решает ядро — по правилам из &#039;&#039;&#039;Proxy Rules&#039;&#039;&#039;. Пока ни одно правило не указывает на &#039;&#039;&#039;Zapret&#039;&#039;&#039;, демон работает вхолостую, а счётчик его цепочки nftables остаётся нулевым.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Правильный порядок: &#039;&#039;&#039;Proxy Rules → Добавить&#039;&#039;&#039; → &#039;&#039;&#039;Source&#039;&#039;&#039; = нужный сервис (например, YouTube) → &#039;&#039;&#039;Node&#039;&#039;&#039; = &#039;&#039;&#039;Zapret&#039;&#039;&#039;. После этого — &#039;&#039;&#039;Применить&#039;&#039;&#039; и перезапуск службы.&lt;br /&gt;
&lt;br /&gt;
Проверить, что трафик действительно пошёл в обработку:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nft list chain inet fw4 homeproxy_zapret_queue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ненулевые счётчики пакетов в строках правил означают, что механизм работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание:&#039;&#039;&#039; В очередь попадают только первые 1–12 пакетов каждого соединения, то есть само рукопожатие, а не весь поток. Нагрузка на процессор роутера при этом минимальна.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5.3. Подбор стратегии ==&lt;br /&gt;
Стратегия по умолчанию рассчитана на типовые ограничения российских провайдеров и работает без настройки в большинстве случаев. Если сервис не открывается — на вкладке движка есть &#039;&#039;&#039;тестер стратегий&#039;&#039;&#039;: он прогоняет пресеты по нескольким сайтам и показывает, какие из них держатся у конкретного провайдера.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — что выбрать для голосовой связи.&#039;&#039;&#039; Голосовые звонки (Discord, мессенджеры) идут по UDP и плохо переносят искажение пакетов. Такие сервисы надёжнее направлять через туннель (&#039;&#039;&#039;Node&#039;&#039;&#039; = основной узел), а не через Zapret. Если всё же нужен Zapret — включите в настройках опцию проксирования голосовых портов, иначе звонки могут не устанавливаться.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
&lt;br /&gt;
== Как применяются изменения ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Что изменено&lt;br /&gt;
!Как применить&lt;br /&gt;
|-&lt;br /&gt;
|Настройки на вкладках (узел, режим, DNS, правила)&lt;br /&gt;
|кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Только&#039;&#039;&#039; списки доменов в Access Control&lt;br /&gt;
|&#039;&#039;&#039;Диагностика&#039;&#039;&#039; → &#039;&#039;&#039;Перезапустить службу&#039;&#039;&#039; (кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039; изменений не увидит)&lt;br /&gt;
|-&lt;br /&gt;
|Из командной строки&lt;br /&gt;
|&amp;lt;code&amp;gt;/etc/init.d/homeproxy reload&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Диагностика ==&lt;br /&gt;
Вкладка &#039;&#039;&#039;Диагностика&#039;&#039;&#039; показывает состояние ядра, версию, PID, запущенные движки обхода DPI, занятые порты и позволяет проверить доступность контрольных сайтов.&lt;br /&gt;
&lt;br /&gt;
Из командной строки — состояние службы:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Какие процессы запущены:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps w | grep -E &#039;sing-box|nfqws|ciadpi&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Журнал событий службы:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tail -30 /var/run/homeproxy/homeproxy.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Журнал самого ядра:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tail -30 /var/run/homeproxy/hiddify-c.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверка сгенерированной конфигурации:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/usr/bin/sing-box check -c /var/run/homeproxy/hiddify-c.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение по чтению журнала ядра.&#039;&#039;&#039; Обилие строк &amp;lt;code&amp;gt;connection refused&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;i/o timeout&amp;lt;/code&amp;gt; для соединений через &amp;lt;code&amp;gt;direct-out&amp;lt;/code&amp;gt; на портах &amp;lt;code&amp;gt;6881&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;51413&amp;lt;/code&amp;gt; и произвольных высоких портах — это, как правило, торрент-трафик: клиент перебирает пиров, часть которых недоступна. К работе туннеля такие записи отношения не имеют.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Как определить, каким маршрутом ушло соединение ===&lt;br /&gt;
Временно повысьте подробность журнала (в панели &#039;&#039;&#039;Log level&#039;&#039;&#039; → &amp;lt;code&amp;gt;info&amp;lt;/code&amp;gt;), выполните запрос с клиентского устройства и посмотрите записи вида:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
inbound/redirect[redirect-in]: inbound connection to 172.67.182.196:443&lt;br /&gt;
outbound/vless[main-out]: outbound connection to 172.67.182.196:443&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Тег в квадратных скобках показывает выбранный маршрут: &amp;lt;code&amp;gt;main-out&amp;lt;/code&amp;gt; — через основной узел, &amp;lt;code&amp;gt;direct-out&amp;lt;/code&amp;gt; — напрямую, &amp;lt;code&amp;gt;zapret-out&amp;lt;/code&amp;gt; — с обработкой DPI. После диагностики верните уровень &amp;lt;code&amp;gt;warn&amp;lt;/code&amp;gt;, иначе журнал быстро заполняется.&lt;br /&gt;
&lt;br /&gt;
== Проверка через клиентское устройство ==&lt;br /&gt;
Проверять маршрутизацию нужно &#039;&#039;&#039;с обычного устройства в сети&#039;&#039;&#039;, а не с самого роутера: собственный трафик роутера обрабатывается отдельной цепочкой правил и ведёт себя иначе.&lt;br /&gt;
&lt;br /&gt;
Какой адрес видит внешний мир (для непроксируемого ресурса это будет адрес провайдера):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Доступность конкретного сайта с кодом ответа и временем (подставьте нужный адрес):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s -o /dev/null -m 15 -w &#039;HTTP %{http_code} за %{time_total}s\n&#039; https://example.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение:&#039;&#039;&#039; в выводе &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; поле &amp;lt;code&amp;gt;%{remote_ip}&amp;lt;/code&amp;gt; — это адрес &#039;&#039;&#039;запрошенного сайта&#039;&#039;&#039;, а не собственный внешний адрес. Чтобы понять, ушло ли соединение через туннель, используйте сервис определения IP (&amp;lt;code&amp;gt;api.ipify.org&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ifconfig.me&amp;lt;/code&amp;gt;) либо смотрите тег маршрута в журнале ядра.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Типовые неполадки =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина и решение&lt;br /&gt;
|-&lt;br /&gt;
|Панель показывает &#039;&#039;&#039;NOT RUNNING&#039;&#039;&#039;, в журнале &amp;lt;code&amp;gt;No supported core found&amp;lt;/code&amp;gt;&lt;br /&gt;
|Ядро не установлено или лежит не по пути &amp;lt;code&amp;gt;/usr/bin/sing-box&amp;lt;/code&amp;gt; — см. Этап 2&lt;br /&gt;
|-&lt;br /&gt;
|Служба запущена, но статус &amp;lt;code&amp;gt;active with no instances&amp;lt;/code&amp;gt;&lt;br /&gt;
|Не задан &#039;&#039;&#039;Main node&#039;&#039;&#039;. Это ожидаемое поведение, а не ошибка&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается &#039;&#039;&#039;в браузере&#039;&#039;&#039;, но &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; работает&lt;br /&gt;
|ECH скрывает SNI. Добавить &amp;lt;code&amp;gt;cloudflare-ech.com&amp;lt;/code&amp;gt; в Proxy Domain List — см. раздел 4.3&lt;br /&gt;
|-&lt;br /&gt;
|Изменил список доменов — ничего не изменилось, панель пишет, что изменений нет&lt;br /&gt;
|Правка списка не перезапускает ядро. &#039;&#039;&#039;Диагностика → Перезапустить службу&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Мессенджер не подключается, хотя его сайт открывается&lt;br /&gt;
|Приложение ходит на серверы по IP. Нужен готовый список по сервису, содержащий IP-подсети — см. раздел 4.1&lt;br /&gt;
|-&lt;br /&gt;
|Zapret включён, но сервис по-прежнему замедлен&lt;br /&gt;
|Нет правила с назначением &#039;&#039;&#039;Zapret&#039;&#039;&#039;. Счётчик очереди нулевой — см. раздел 5.2&lt;br /&gt;
|-&lt;br /&gt;
|Пункт &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; отсутствует в списке типов узлов&lt;br /&gt;
|В OpenWrt-сборке ядра нет соответствующего флага. Установить musl-сборку — см. раздел 2.3&lt;br /&gt;
|-&lt;br /&gt;
|Интерфейс панели открывается по несколько секунд&lt;br /&gt;
|Ядро сжато UPX. Заменить на несжатый бинарник — см. предупреждение в разделе 2.3&lt;br /&gt;
|-&lt;br /&gt;
|Страницы «висят» по 30 секунд, затем открываются&lt;br /&gt;
|Браузер уходит в QUIC (UDP/443) мимо перехвата. Заблокировать UDP/443 действием &#039;&#039;&#039;REJECT&#039;&#039;&#039; (не DROP — при DROP браузер ждёт таймаута)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Удаление =&lt;br /&gt;
Остановить службу:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy stop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Убрать из автозапуска:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy disable&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удалить панель и локализацию:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg remove luci-app-re-homeproxy luci-i18n-homeproxy-ru&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удалить ядро, если оно ставилось пакетом:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg remove sing-box-extended&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удалить ядро, если бинарник копировался вручную:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rm -f /usr/bin/sing-box&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удалить конфигурацию и списки:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rm -rf /etc/homeproxy /etc/config/homeproxy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Правила nftables снимаются автоматически при остановке службы. Настройки межсетевого экрана, проброс портов и прочая конфигурация роутера не затрагиваются.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Ссылки =&lt;br /&gt;
* [https://github.com/1andrevich/homeproxy-hiddify Re:HomeProxy — исходный код и релизы]&lt;br /&gt;
* [https://github.com/shtorm-7/sing-box-extended sing-box-extended — ядро]&lt;br /&gt;
* [https://github.com/1andrevich/Re-filter-lists Re-filter — списки заблокированных ресурсов]&lt;br /&gt;
* [https://github.com/itdoginfo/allow-domains itdoginfo/allow-domains — списки по сервисам]&lt;br /&gt;
* [https://github.com/bol-van/zapret2 Zapret 2 (nfqws2)]&lt;br /&gt;
* [https://github.com/hufrea/byedpi ByeDPI]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Сеть]]&lt;br /&gt;
[[Категория:OpenWrt]]&lt;br /&gt;
[[Категория:Обход блокировок]]&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Re:HomeProxy_%D0%BD%D0%B0_OpenWrt:_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D0%BA_%D0%B8_%D0%B3%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&amp;diff=1037</id>
		<title>Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Re:HomeProxy_%D0%BD%D0%B0_OpenWrt:_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D0%BA_%D0%B8_%D0%B3%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&amp;diff=1037"/>
		<updated>2026-08-14T11:06:54Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Инструкция по установке Re:HomeProxy на OpenWrt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация =&lt;br /&gt;
Подробная пошаговая инструкция по развёртыванию &#039;&#039;&#039;Re:HomeProxy&#039;&#039;&#039; на чистом роутере с OpenWrt — от установки пакета до работающей выборочной маршрутизации. Рассчитана на пользователя, который умеет работать в терминале, но не знаком с внутренним устройством роутерных систем.&lt;br /&gt;
&lt;br /&gt;
Проверено на &#039;&#039;&#039;Xiaomi Redmi Router AX6000&#039;&#039;&#039; (&amp;lt;code&amp;gt;aarch64_cortex-a53&amp;lt;/code&amp;gt;, mediatek/filogic) с &#039;&#039;&#039;OpenWrt 24.10.0&#039;&#039;&#039;. Подходит для любого устройства с OpenWrt 24.10 (opkg) или 25.12+ (apk).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Одна панель в LuCI, которая управляет всем стеком обхода блокировок: туннелем, списками маршрутизации и обходом DPI без VPN.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Re:HomeProxy&#039;&#039;&#039; (&amp;lt;code&amp;gt;luci-app-re-homeproxy&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Веб-панель в LuCI. Форк ImmortalWrt HomeProxy. Управляет узлами, списками маршрутизации, правилами доступа и обходом DPI. Сама по себе трафик не обрабатывает — генерирует конфигурацию для ядра и правила nftables.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box-extended&#039;&#039;&#039;&lt;br /&gt;
|Ядро (движок). Форк sing-box с расширенным набором протоколов: VLESS (в т.ч. REALITY, XHTTP), Hysteria2, Trojan, Shadowsocks, ShadowTLS, AnyTLS, WireGuard, AmneziaWG, &#039;&#039;&#039;Mieru&#039;&#039;&#039;, &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039;, SSH и др.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Списки маршрутизации&#039;&#039;&#039;&lt;br /&gt;
|Готовые наборы доменов и IP (Re-filter, Russia Inside, точечные по сервисам). Скачиваются ядром автоматически и обновляются раз в сутки.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Zapret 2&#039;&#039;&#039; (&amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обход DPI без туннеля: искажает TLS-рукопожатие «на лету» через NFQUEUE. Подходит для замедленных, но не заблокированных сервисов — трафик идёт напрямую, минуя VPS.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ByeDPI&#039;&#039;&#039; (&amp;lt;code&amp;gt;ciadpi&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Альтернативный движок обхода DPI, работает как локальный SOCKS5-прокси. 47 пресетов стратегий и встроенный тестер.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Принцип работы ===&lt;br /&gt;
В основе — разделение на две независимые оси:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Списки&#039;&#039;&#039; решают, &#039;&#039;какой&#039;&#039; трафик особенный.&lt;br /&gt;
* &#039;&#039;&#039;Узел / Zapret / ByeDPI&#039;&#039;&#039; решают, &#039;&#039;через что&#039;&#039; его пустить.&lt;br /&gt;
&lt;br /&gt;
Они комбинируются: каждое правило — это пара «список → способ доставки». Например, заблокированные РКН сайты идут через туннель, а замедленный YouTube — напрямую с лечением DPI, не нагружая VPS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание:&#039;&#039;&#039; Весь трафик LAN перехватывается через nftables (&amp;lt;code&amp;gt;redirect&amp;lt;/code&amp;gt; для TCP, &amp;lt;code&amp;gt;tproxy&amp;lt;/code&amp;gt; для UDP) и передаётся ядру. Ядро определяет домен по SNI из TLS-рукопожатия либо по DNS-ответу, сверяет со списками и выбирает маршрут. Всё, что не попало ни в один список, идёт напрямую.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|OpenWrt / ImmortalWrt &#039;&#039;&#039;24.10&#039;&#039;&#039; и новее (менеджер пакетов &amp;lt;code&amp;gt;opkg&amp;lt;/code&amp;gt;) либо &#039;&#039;&#039;25.12&#039;&#039;&#039; и новее (&amp;lt;code&amp;gt;apk&amp;lt;/code&amp;gt;). Для 23.05 в разделе Releases есть legacy-сборка с суффиксом &amp;lt;code&amp;gt;_all-legacy.ipk&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Свободное место&#039;&#039;&#039;&lt;br /&gt;
|Минимум &#039;&#039;&#039;40 МБ&#039;&#039;&#039; на overlay при сжимающей файловой системе (ubifs/jffs2), либо &#039;&#039;&#039;90 МБ&#039;&#039;&#039; на несжимающей (ext4/f2fs). Основной объём — ядро.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оперативная память&#039;&#039;&#039;&lt;br /&gt;
|От &#039;&#039;&#039;256 МБ&#039;&#039;&#039;. Ядро — крупное Go-приложение.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Архитектура&#039;&#039;&#039;&lt;br /&gt;
|Любая, для которой собран пакет ядра: aarch64 (cortex-a53/a72/a76, generic), x86_64, arm, mips/mipsel.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ&#039;&#039;&#039;&lt;br /&gt;
|Веб-интерфейс LuCI и SSH-доступ к роутеру.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Узел (опционально)&#039;&#039;&#039;&lt;br /&gt;
|Сервер с VLESS / Hysteria2 / Trojan / Mieru / NaïveProxy и т.п. Нужен только для туннеля — обход DPI через Zapret и ByeDPI работает и без него.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — как правильно оценить свободное место.&#039;&#039;&#039; Команда &amp;lt;code&amp;gt;df -h&amp;lt;/code&amp;gt; показывает несколько разделов, и легко перепутать. Ориентироваться нужно &#039;&#039;&#039;только&#039;&#039;&#039; на строку с точкой монтирования &amp;lt;code&amp;gt;/overlay&amp;lt;/code&amp;gt; (или &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;). Раздел &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt;, смонтированный в &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; — это &#039;&#039;&#039;оперативная память&#039;&#039;&#039; (обычно половина от объёма ОЗУ), а не хранилище: он очищается при перезагрузке и для установки не годится, хотя его размер часто выглядит самым внушительным в выводе.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Проверка перед установкой ===&lt;br /&gt;
Подключитесь к роутеру по SSH и выполните:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Версия и архитектура&lt;br /&gt;
cat /etc/openwrt_release&lt;br /&gt;
grep OPENWRT_ARCH /etc/os-release&lt;br /&gt;
&lt;br /&gt;
# Свободное место (смотреть строку /overlay или /)&lt;br /&gt;
df -h&lt;br /&gt;
&lt;br /&gt;
# Тип файловой системы overlay: ubifs/jffs2 сжимают, ext4/f2fs — нет&lt;br /&gt;
mount | awk &#039;$3==&amp;quot;/overlay&amp;quot;{print $5}&#039;&lt;br /&gt;
&lt;br /&gt;
# Оперативная память&lt;br /&gt;
grep -E &#039;MemTotal|MemAvailable&#039; /proc/meminfo&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение про сжатие.&#039;&#039;&#039; На &#039;&#039;&#039;ubifs&#039;&#039;&#039; и &#039;&#039;&#039;jffs2&#039;&#039;&#039; файлы сжимаются прозрачно, и крупный бинарник ядра занимает примерно в 2–3 раза меньше своего номинального размера. Проверять реальный расход нужно по &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; &#039;&#039;&#039;до и после&#039;&#039;&#039; установки: команда &amp;lt;code&amp;gt;du&amp;lt;/code&amp;gt; на этих ФС показывает логический размер файла, а не занятое место.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка панели =&lt;br /&gt;
&lt;br /&gt;
== 1.1. Зависимости ==&lt;br /&gt;
Панели требуются четыре пакета: &amp;lt;code&amp;gt;libc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;firewall4&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kmod-nft-tproxy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ucode-mod-digest&amp;lt;/code&amp;gt;. В типовой сборке OpenWrt 24.10 первые три уже присутствуют.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ucode-mod-digest kmod-nft-tproxy kmod-tun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;kmod-tun&amp;lt;/code&amp;gt; нужен, если планируется режим TUN; для базового режима &amp;lt;code&amp;gt;Redirect + TProxy&amp;lt;/code&amp;gt; достаточно &amp;lt;code&amp;gt;kmod-nft-tproxy&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== 1.2. Установка пакета ==&lt;br /&gt;
Пакет ставится не из штатных репозиториев, а из релизов проекта.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Для OpenWrt 24.10 и ниже (opkg / ipk):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp&lt;br /&gt;
wget -O rhp.ipk &amp;quot;$(wget -qO- &#039;https://api.github.com/repos/1andrevich/homeproxy-hiddify/releases/latest&#039; \&lt;br /&gt;
  | grep -o &#039;https://github\.com/[^&amp;quot;]*luci-app-re-homeproxy[^&amp;quot;]*_all\.ipk&#039; | head -1)&amp;quot;&lt;br /&gt;
opkg install /tmp/rhp.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Русская локализация интерфейса:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp&lt;br /&gt;
wget -O rhp-ru.ipk &amp;quot;$(wget -qO- &#039;https://api.github.com/repos/1andrevich/homeproxy-hiddify/releases/latest&#039; \&lt;br /&gt;
  | grep -o &#039;https://github\.com/[^&amp;quot;]*luci-i18n-homeproxy-ru[^&amp;quot;]*_all\.ipk&#039; | head -1)&amp;quot;&lt;br /&gt;
opkg install /tmp/rhp-ru.ipk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Для OpenWrt 25.12+ (apk):&#039;&#039;&#039; в командах выше замените расширение &amp;lt;code&amp;gt;.ipk&amp;lt;/code&amp;gt; на &amp;lt;code&amp;gt;.apk&amp;lt;/code&amp;gt;, а &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt; — на &amp;lt;code&amp;gt;apk add --allow-untrusted&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сам пакет панели весит около &#039;&#039;&#039;650 КБ&#039;&#039;&#039; — основное место займёт ядро на следующем этапе.&lt;br /&gt;
&lt;br /&gt;
== 1.3. Проверка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg list-installed | grep re-homeproxy&lt;br /&gt;
ls /etc/config/homeproxy /etc/init.d/homeproxy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки в LuCI появится раздел &#039;&#039;&#039;Сервисы → Re:HomeProxy&#039;&#039;&#039;. Если пункт меню не виден — обновите страницу с очисткой кэша (&amp;lt;code&amp;gt;Ctrl+F5&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка ядра =&lt;br /&gt;
Панель без ядра не работает: на её странице будет висеть красная надпись &#039;&#039;&#039;NOT RUNNING&#039;&#039;&#039;, а в журнале — &amp;lt;code&amp;gt;No supported core found&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Выбор ядра ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Ядро&lt;br /&gt;
!Особенности&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;hiddify-core&#039;&#039;&#039;&lt;br /&gt;
|Вариант по умолчанию. Ставится в один клик, есть компактная (UPX) сборка для устройств с малым объёмом флеш-памяти.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box-extended&#039;&#039;&#039;&lt;br /&gt;
|Самый широкий набор протоколов: AmneziaWG / WARP, Mieru, NaïveProxy, TrustTunnel, MASQUE, Snell, MTProxy. &#039;&#039;&#039;Рекомендуется&#039;&#039;&#039;, если нужны эти протоколы.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 2.2. Установка в один клик ==&lt;br /&gt;
В панели: &#039;&#039;&#039;Core &amp;amp; Tools&#039;&#039;&#039; (в русской локализации — &#039;&#039;&#039;Ядро и службы&#039;&#039;&#039;) → выбрать ядро → &#039;&#039;&#039;Установить&#039;&#039;&#039;. Панель сама определит архитектуру, проверит свободное место, скачает и установит пакет.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — в OpenWrt-сборке sing-box-extended нет NaïveProxy.&#039;&#039;&#039; Пакет для OpenWrt собирается без флага &amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt;, поэтому пункт &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; в списке типов узлов не появится, а попытка использовать такой узел даст ошибку &amp;lt;code&amp;gt;naive outbound is not included in this build&amp;lt;/code&amp;gt;. Если NaïveProxy нужен — устанавливайте ядро вручную по инструкции ниже. Mieru в OpenWrt-сборке присутствует.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.3. Ручная установка ядра с поддержкой NaïveProxy ==&lt;br /&gt;
Вместо пакета для OpenWrt берётся ассет &amp;lt;code&amp;gt;linux-&amp;lt;арх&amp;gt;-musl&amp;lt;/code&amp;gt; из того же релиза. Он собран с флагами &amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;with_musl&amp;lt;/code&amp;gt;, библиотека cronet влинкована статически, поэтому дополнительных файлов не требуется. OpenWrt тоже построен на musl, и бинарник запускается как есть.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp&lt;br /&gt;
# Узнать актуальную версию и скачать musl-сборку (пример для arm64)&lt;br /&gt;
VER=$(wget -qO- &#039;https://api.github.com/repos/shtorm-7/sing-box-extended/releases/latest&#039; \&lt;br /&gt;
  | grep -o &#039;&amp;quot;tag_name&amp;quot;: &amp;quot;[^&amp;quot;]*&amp;quot;&#039; | cut -d&#039;&amp;quot;&#039; -f4)&lt;br /&gt;
wget -O sb.tar.gz &amp;quot;https://github.com/shtorm-7/sing-box-extended/releases/download/${VER}/sing-box-${VER#v}-linux-arm64-musl.tar.gz&amp;quot;&lt;br /&gt;
&lt;br /&gt;
mkdir -p sb &amp;amp;&amp;amp; tar xzf sb.tar.gz -C sb&lt;br /&gt;
BIN=$(find /tmp/sb -name &#039;sing-box&#039; -type f | head -1)&lt;br /&gt;
chmod +x &amp;quot;$BIN&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Убедиться, что нужные флаги на месте&lt;br /&gt;
&amp;quot;$BIN&amp;quot; version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В строке &amp;lt;code&amp;gt;Tags:&amp;lt;/code&amp;gt; должны присутствовать &amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt; (для NaïveProxy) и &amp;lt;code&amp;gt;with_quic&amp;lt;/code&amp;gt; (для Mieru и Hysteria2). Если флаги на месте — установить бинарник:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -h /                                   # запомнить свободное место&lt;br /&gt;
cp &amp;quot;$BIN&amp;quot; /usr/bin/sing-box&lt;br /&gt;
chmod +x /usr/bin/sing-box&lt;br /&gt;
sync&lt;br /&gt;
df -h /                                   # оценить реальный расход&lt;br /&gt;
/usr/bin/sing-box version                 # проверка запуска&lt;br /&gt;
rm -rf /tmp/sb /tmp/sb.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — путь строго &amp;lt;code&amp;gt;/usr/bin/sing-box&amp;lt;/code&amp;gt;.&#039;&#039;&#039; В конфигурации есть параметры &amp;lt;code&amp;gt;custom_core_path&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;custom_core_type&amp;lt;/code&amp;gt;, но стартовый скрипт &amp;lt;code&amp;gt;/etc/init.d/homeproxy&amp;lt;/code&amp;gt; их не читает — в нём жёстко задано &amp;lt;code&amp;gt;PROG_SINGBOX=&amp;quot;/usr/bin/sing-box&amp;quot;&amp;lt;/code&amp;gt;. Ядро, положенное в произвольный каталог, панель в статусе покажет, но сервис с ним не запустится.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — не сжимайте ядро упаковщиком UPX.&#039;&#039;&#039; Сжатый бинарник распаковывается в оперативную память &#039;&#039;&#039;при каждом запуске процесса&#039;&#039;&#039;, а панель вызывает &amp;lt;code&amp;gt;sing-box version&amp;lt;/code&amp;gt; при каждой отрисовке своих страниц (так она определяет доступные протоколы по флагам сборки). В результате интерфейс становится непригодным для работы: замер на aarch64 даёт &#039;&#039;&#039;3,1 с&#039;&#039;&#039; против &#039;&#039;&#039;0,05 с&#039;&#039;&#039; на каждый вызов — разница в шестьдесят раз. На скорость трафика это не влияет (ядро стартует один раз), страдает только отзывчивость панели. Экономия при этом невелика: на сжимающей файловой системе выигрыш составляет около 19 МБ.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — как проверить ядро, ничего не устанавливая.&#039;&#039;&#039; Скачайте архив в &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; (это ОЗУ), распакуйте и запускайте бинарник прямо оттуда: &amp;lt;code&amp;gt;./sing-box version&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;./sing-box check -c конфиг.json&amp;lt;/code&amp;gt;. Так проверяются и флаги сборки, и совместимость конфигурации, и живой трафик — при этом флеш-память роутера не затрагивается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.4. Проверка, что панель видит ядро ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ubus call luci.homeproxy singbox_get_features&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В ответе должны быть &amp;lt;code&amp;gt;&amp;quot;core_type&amp;quot;: &amp;quot;singbox&amp;quot;&amp;lt;/code&amp;gt;, версия и набор флагов. Ключевые:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Флаг&lt;br /&gt;
!Что даёт&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;with_naive_outbound&amp;lt;/code&amp;gt;&lt;br /&gt;
|Пункт &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; в списке типов узлов&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;with_quic&amp;lt;/code&amp;gt;&lt;br /&gt;
|&#039;&#039;&#039;Mieru&#039;&#039;&#039;, &#039;&#039;&#039;Hysteria&#039;&#039;&#039;, &#039;&#039;&#039;Hysteria2&#039;&#039;&#039;, &#039;&#039;&#039;TUIC&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hp_has_tproxy&amp;lt;/code&amp;gt;&lt;br /&gt;
|Режим &#039;&#039;&#039;TProxy&#039;&#039;&#039; для UDP&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hp_has_tun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Режим &#039;&#039;&#039;TUN&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Запуск и добавление узла =&lt;br /&gt;
&lt;br /&gt;
== 3.1. Запуск службы ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy enable&lt;br /&gt;
/etc/init.d/homeproxy start&lt;br /&gt;
/etc/init.d/homeproxy status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание:&#039;&#039;&#039; Статус &amp;lt;code&amp;gt;active with no instances&amp;lt;/code&amp;gt; и запись &amp;lt;code&amp;gt;no main_node configured, skipping config generation&amp;lt;/code&amp;gt; в журнале — это &#039;&#039;&#039;нормальное&#039;&#039;&#039; состояние до того, как задан основной узел. Ядро запускается только при выбранном узле. Пока узел не задан, трафик не перехватывается и сеть работает как обычно.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Добавление узла ==&lt;br /&gt;
&#039;&#039;&#039;Node Settings&#039;&#039;&#039; → &#039;&#039;&#039;Добавить&#039;&#039;&#039;. Поддерживаются два пути:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Импорт ссылки или подписки&#039;&#039;&#039; — вставить &amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;naive+https://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss://&amp;lt;/code&amp;gt; и т.п., либо URL подписки (sing-box JSON / Hiddify, base64, обычные share-ссылки, конфигурации Xray/V2Ray).&lt;br /&gt;
* &#039;&#039;&#039;Ручное заполнение&#039;&#039;&#039; — выбрать тип и заполнить поля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение по Mieru:&#039;&#039;&#039; диапазон портов в поле &#039;&#039;&#039;Port range&#039;&#039;&#039; записывается &#039;&#039;&#039;через дефис&#039;&#039;&#039; — &amp;lt;code&amp;gt;2012-2022&amp;lt;/code&amp;gt;. Форматы &amp;lt;code&amp;gt;2012:2022&amp;lt;/code&amp;gt; и одиночное значение в поле диапазона дают ошибку &amp;lt;code&amp;gt;invalid server_ports format&amp;lt;/code&amp;gt;. Одиночный порт без диапазона задаётся в обычном поле порта.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.3. Основные параметры ==&lt;br /&gt;
&#039;&#039;&#039;Client Settings → Routing Settings&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Рекомендация&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Main node&#039;&#039;&#039;&lt;br /&gt;
|Созданный узел. Значение &#039;&#039;&#039;Отключить&#039;&#039;&#039; оставляет всю маршрутизацию прямой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Routing mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Russia (Proxy Banned)&#039;&#039;&#039; — по умолчанию всё идёт напрямую, через туннель — только попавшее в списки.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Proxy mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Redirect TCP + TProxy UDP&#039;&#039;&#039; — базовый режим, не требует TUN.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Russia DNS server&#039;&#039;&#039;&lt;br /&gt;
|Быстрый российский резолвер для доменов, идущих напрямую.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Secure DNS server&#039;&#039;&#039;&lt;br /&gt;
|DoH/DoT-резолвер для проксируемых доменов — провайдер не видит, какие сайты запрашиваются.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Do not proxify torrents&#039;&#039;&#039;&lt;br /&gt;
|Включить: торренты пойдут напрямую и не будут нагружать туннель.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После изменений — &#039;&#039;&#039;Применить&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Списки маршрутизации =&lt;br /&gt;
Списки определяют, какой трафик считать особенным. Есть два механизма, и они дополняют друг друга.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Автообновляемые списки (основной способ) ==&lt;br /&gt;
&#039;&#039;&#039;Client Settings → Proxy Rules → Добавить&#039;&#039;&#039;. Каждое правило — строка из трёх полей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enable&#039;&#039;&#039;&lt;br /&gt;
|галочка&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Source ⤵️&#039;&#039;&#039;&lt;br /&gt;
|источник списка&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Node 🔗&#039;&#039;&#039;&lt;br /&gt;
|способ доставки: &#039;&#039;&#039;Same as main node&#039;&#039;&#039;, &#039;&#039;&#039;Separate URLTest&#039;&#039;&#039;, конкретный узел, &#039;&#039;&#039;ByeDPI&#039;&#039;&#039; или &#039;&#039;&#039;Zapret&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Доступные источники:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Источник&lt;br /&gt;
!Содержимое&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Re-filter&#039;&#039;&#039;&lt;br /&gt;
|Более 60000 доменов и 25000 IP — сводный перечень ресурсов, заблокированных в России&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Russia Inside&#039;&#039;&#039;&lt;br /&gt;
|Свыше 1000 записей: базовый набор сервисов, которым в России нужен туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Точечные&#039;&#039;&#039;&lt;br /&gt;
|YouTube, Discord, Telegram, Meta, TikTok, Twitter/X, Roblox, HDRezka, Google AI, Google Play, Cloudflare, CloudFront, Hetzner, OVH, DigitalOcean, аниме-стриминги, новостные сайты и др.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Такие списки ядро скачивает само и &#039;&#039;&#039;обновляет раз в сутки&#039;&#039;&#039; (&amp;lt;code&amp;gt;update_interval: 1d&amp;lt;/code&amp;gt;) — поддерживать их вручную не нужно.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание про приоритет:&#039;&#039;&#039; порядок правил задавать не нужно, панель расставляет их сама: сначала точечные списки по сервисам, затем Russia Inside, последним — самый широкий Re-filter. Благодаря этому частное правило всегда выигрывает у общего, и, например, YouTube можно отправить через Zapret, а всё остальное из Re-filter — через туннель.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение — почему списки по сервисам важнее ручных доменов.&#039;&#039;&#039; Многие приложения подключаются к своим серверам &#039;&#039;&#039;по IP-адресам, без доменного имени&#039;&#039;&#039; — так работают мессенджеры, игровые клиенты и часть мобильных приложений. Доменные правила на такие соединения не действуют в принципе: ядру нечего сопоставлять со списком. Готовые списки по сервисам содержат, помимо доменов, ещё и IP-подсети, поэтому закрывают оба случая. Показательный пример — Telegram: список включает домены (&amp;lt;code&amp;gt;t.me&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;telegra.ph&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;telesco.pe&amp;lt;/code&amp;gt;) и подсети дата-центров (&amp;lt;code&amp;gt;149.154.160.0/20&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;91.108.4.0/22&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;91.108.56.0/22&amp;lt;/code&amp;gt; и другие). Без подсетей веб-версия откроется, а сам мессенджер подключиться не сможет.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4.2. Ручной список доменов ==&lt;br /&gt;
&#039;&#039;&#039;Access Control → Proxy Domain List&#039;&#039;&#039; — произвольный перечень доменов, по одному в строке. Имеет смысл для того, чего нет в готовых списках.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — правка только списков не перезапускает ядро.&#039;&#039;&#039; Поле ручного списка не связано с конфигурацией UCI: оно записывает файл напрямую. LuCI отслеживает изменения именно в UCI, поэтому после правки одного лишь списка панель отвечает, что изменений нет, и ядро продолжает работать со старой конфигурацией. Применить изменения можно двумя способами: перейти на вкладку &#039;&#039;&#039;Диагностика&#039;&#039;&#039; и нажать &#039;&#039;&#039;Перезапустить службу&#039;&#039;&#039;, либо изменить список вместе с любой настройкой на других вкладках — тогда сработает обычная кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 4.3. Обязательный пункт: ECH ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — без этой записи проксирование не работает в браузерах.&#039;&#039;&#039; Добавьте в &#039;&#039;&#039;Proxy Domain List&#039;&#039;&#039; строку:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
cloudflare-ech.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Суть проблемы.&#039;&#039;&#039; Сайты за Cloudflare публикуют DNS-запись типа HTTPS (RR 65) с параметром &amp;lt;code&amp;gt;ech=&amp;lt;/code&amp;gt;. Получив её, современный браузер применяет &#039;&#039;&#039;ECH (Encrypted Client Hello)&#039;&#039;&#039; и &#039;&#039;&#039;шифрует имя сервера (SNI)&#039;&#039;&#039; в TLS-рукопожатии. Ядро определяет домен именно по SNI, поэтому распознать сайт не может, не находит его в списках и отправляет соединение напрямую — где его и обрывает DPI.&lt;br /&gt;
&lt;br /&gt;
Характерная картина: сайт есть в списке, но в браузере выдаёт ошибку установления защищённого соединения (в Firefox — &amp;lt;code&amp;gt;PR_END_OF_FILE_ERROR&amp;lt;/code&amp;gt;), тогда как &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; с того же устройства на тот же адрес работает и корректно уходит через туннель. Расхождение объясняется просто: &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; не поддерживает ECH и отправляет открытый SNI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Почему помогает одна строка.&#039;&#039;&#039; При ECH шифруется только внутреннее имя, а внешнее остаётся видимым — и для всех сайтов за Cloudflare оно равно &amp;lt;code&amp;gt;cloudflare-ech.com&amp;lt;/code&amp;gt;. Ядро видит его, находит в списке и отправляет соединение в туннель. Решение действует на &#039;&#039;&#039;любого клиента сети&#039;&#039;&#039; независимо от браузера и его настроек DNS — настраивать устройства пользователей не требуется.&lt;br /&gt;
&lt;br /&gt;
Проверить, использует ли конкретный домен ECH, можно с любой машины:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s &#039;https://cloudflare-dns.com/dns-query?name=example.com&amp;amp;type=HTTPS&#039; -H &#039;accept: application/dns-json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Наличие &amp;lt;code&amp;gt;ech=&amp;lt;/code&amp;gt; в ответе означает, что у этого домена SNI будет скрыт.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 5. Обход DPI без туннеля =&lt;br /&gt;
Для сервисов, которые не заблокированы, а &#039;&#039;&#039;замедлены&#039;&#039;&#039; (характерный пример — видеохостинги), туннель избыточен: он тратит трафик VPS и режет скорость. Достаточно исказить рукопожатие так, чтобы DPI не смог его разобрать.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Движок&lt;br /&gt;
!Как встроен&lt;br /&gt;
!Особенности&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Zapret 2&#039;&#039;&#039; (&amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Исходящее соединение помечается меткой маршрутизации, nftables отправляет первые пакеты в NFQUEUE, где &amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt; искажает рукопожатие&lt;br /&gt;
|Работает с QUIC; трафик идёт напрямую и не шифруется&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ByeDPI&#039;&#039;&#039; (&amp;lt;code&amp;gt;ciadpi&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Локальный SOCKS5-прокси на &amp;lt;code&amp;gt;127.0.0.1:5335&amp;lt;/code&amp;gt;, подключается как обычный узел типа socks&lt;br /&gt;
|47 пресетов стратегий, не требует NFQUEUE&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5.1. Установка ==&lt;br /&gt;
На вкладке &#039;&#039;&#039;Client Settings&#039;&#039;&#039; включите &#039;&#039;&#039;Enable Zapret&#039;&#039;&#039; или &#039;&#039;&#039;Enable ByeDPI&#039;&#039;&#039; — панель предложит установить недостающий пакет и сделает это сама. Пакеты небольшие.&lt;br /&gt;
&lt;br /&gt;
== 5.2. Обязательный шаг: назначить трафик ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — сама по себе галочка ничего не обходит.&#039;&#039;&#039; В этой панели &amp;lt;code&amp;gt;nfqws2&amp;lt;/code&amp;gt; запускается &#039;&#039;&#039;без собственных хостлистов&#039;&#039;&#039; и обрабатывает только то, что ему явным образом направят. Какие соединения попадут в очередь, решает ядро — по правилам из &#039;&#039;&#039;Proxy Rules&#039;&#039;&#039;. Пока ни одно правило не указывает на &#039;&#039;&#039;Zapret&#039;&#039;&#039;, демон работает вхолостую, а счётчик его цепочки nftables остаётся нулевым.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Правильный порядок: &#039;&#039;&#039;Proxy Rules → Добавить&#039;&#039;&#039; → &#039;&#039;&#039;Source&#039;&#039;&#039; = нужный сервис (например, YouTube) → &#039;&#039;&#039;Node&#039;&#039;&#039; = &#039;&#039;&#039;Zapret&#039;&#039;&#039;. После этого — &#039;&#039;&#039;Применить&#039;&#039;&#039; и перезапуск службы.&lt;br /&gt;
&lt;br /&gt;
Проверить, что трафик действительно пошёл в обработку:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nft list chain inet fw4 homeproxy_zapret_queue | grep counter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ненулевые счётчики пакетов означают, что механизм работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Примечание:&#039;&#039;&#039; В очередь попадают только первые 1–12 пакетов каждого соединения, то есть само рукопожатие, а не весь поток. Нагрузка на процессор роутера при этом минимальна.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 5.3. Подбор стратегии ==&lt;br /&gt;
Стратегия по умолчанию рассчитана на типовые ограничения российских провайдеров и работает без настройки в большинстве случаев. Если сервис не открывается — на вкладке движка есть &#039;&#039;&#039;тестер стратегий&#039;&#039;&#039;: он прогоняет пресеты по нескольким сайтам и показывает, какие из них держатся у конкретного провайдера.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — что выбрать для голосовой связи.&#039;&#039;&#039; Голосовые звонки (Discord, мессенджеры) идут по UDP и плохо переносят искажение пакетов. Такие сервисы надёжнее направлять через туннель (&#039;&#039;&#039;Node&#039;&#039;&#039; = основной узел), а не через Zapret. Если всё же нужен Zapret — включите в настройках опцию проксирования голосовых портов, иначе звонки могут не устанавливаться.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
&lt;br /&gt;
== Как применяются изменения ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Что изменено&lt;br /&gt;
!Как применить&lt;br /&gt;
|-&lt;br /&gt;
|Настройки на вкладках (узел, режим, DNS, правила)&lt;br /&gt;
|кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Только&#039;&#039;&#039; списки доменов в Access Control&lt;br /&gt;
|&#039;&#039;&#039;Диагностика&#039;&#039;&#039; → &#039;&#039;&#039;Перезапустить службу&#039;&#039;&#039; (кнопка &#039;&#039;&#039;Применить&#039;&#039;&#039; изменений не увидит)&lt;br /&gt;
|-&lt;br /&gt;
|Из командной строки&lt;br /&gt;
|&amp;lt;code&amp;gt;/etc/init.d/homeproxy reload&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Диагностика ==&lt;br /&gt;
Вкладка &#039;&#039;&#039;Диагностика&#039;&#039;&#039; показывает состояние ядра, версию, PID, запущенные движки обхода DPI, занятые порты и позволяет проверить доступность контрольных сайтов.&lt;br /&gt;
&lt;br /&gt;
Из командной строки:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Состояние службы и ядра&lt;br /&gt;
/etc/init.d/homeproxy status&lt;br /&gt;
ps w | grep -E &#039;sing-box|nfqws|ciadpi&#039; | grep -v grep&lt;br /&gt;
&lt;br /&gt;
# Журналы: события службы и лог ядра&lt;br /&gt;
tail -30 /var/run/homeproxy/homeproxy.log&lt;br /&gt;
tail -30 /var/run/homeproxy/hiddify-c.log&lt;br /&gt;
&lt;br /&gt;
# Проверка сгенерированной конфигурации&lt;br /&gt;
/usr/bin/sing-box check -c /var/run/homeproxy/hiddify-c.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение по чтению журнала ядра.&#039;&#039;&#039; Обилие строк &amp;lt;code&amp;gt;connection refused&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;i/o timeout&amp;lt;/code&amp;gt; для соединений через &amp;lt;code&amp;gt;direct-out&amp;lt;/code&amp;gt; на портах &amp;lt;code&amp;gt;6881&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;51413&amp;lt;/code&amp;gt; и произвольных высоких портах — это, как правило, торрент-трафик: клиент перебирает пиров, часть которых недоступна. К работе туннеля такие записи отношения не имеют.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Как определить, каким маршрутом ушло соединение ===&lt;br /&gt;
Временно повысьте подробность журнала (в панели &#039;&#039;&#039;Log level&#039;&#039;&#039; → &amp;lt;code&amp;gt;info&amp;lt;/code&amp;gt;), выполните запрос с клиентского устройства и посмотрите записи вида:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
inbound/redirect[redirect-in]: inbound connection to 172.67.182.196:443&lt;br /&gt;
outbound/vless[main-out]: outbound connection to 172.67.182.196:443&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Тег в квадратных скобках показывает выбранный маршрут: &amp;lt;code&amp;gt;main-out&amp;lt;/code&amp;gt; — через основной узел, &amp;lt;code&amp;gt;direct-out&amp;lt;/code&amp;gt; — напрямую, &amp;lt;code&amp;gt;zapret-out&amp;lt;/code&amp;gt; — с обработкой DPI. После диагностики верните уровень &amp;lt;code&amp;gt;warn&amp;lt;/code&amp;gt;, иначе журнал быстро заполняется.&lt;br /&gt;
&lt;br /&gt;
== Проверка через клиентское устройство ==&lt;br /&gt;
Проверять маршрутизацию нужно &#039;&#039;&#039;с обычного устройства в сети&#039;&#039;&#039;, а не с самого роутера: собственный трафик роутера обрабатывается отдельной цепочкой правил и ведёт себя иначе.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какой адрес видит внешний мир (для непроксируемого ресурса — адрес провайдера)&lt;br /&gt;
curl -s https://api.ipify.org; echo&lt;br /&gt;
&lt;br /&gt;
# Доступность конкретного сайта&lt;br /&gt;
curl -s -o /dev/null -m 15 -w &#039;HTTP %{http_code} за %{time_total}s\n&#039; https://example.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Уточнение:&#039;&#039;&#039; в выводе &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; поле &amp;lt;code&amp;gt;%{remote_ip}&amp;lt;/code&amp;gt; — это адрес &#039;&#039;&#039;запрошенного сайта&#039;&#039;&#039;, а не собственный внешний адрес. Чтобы понять, ушло ли соединение через туннель, используйте сервис определения IP (&amp;lt;code&amp;gt;api.ipify.org&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ifconfig.me&amp;lt;/code&amp;gt;) либо смотрите тег маршрута в журнале ядра.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Типовые неполадки =&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина и решение&lt;br /&gt;
|-&lt;br /&gt;
|Панель показывает &#039;&#039;&#039;NOT RUNNING&#039;&#039;&#039;, в журнале &amp;lt;code&amp;gt;No supported core found&amp;lt;/code&amp;gt;&lt;br /&gt;
|Ядро не установлено или лежит не по пути &amp;lt;code&amp;gt;/usr/bin/sing-box&amp;lt;/code&amp;gt; — см. Этап 2&lt;br /&gt;
|-&lt;br /&gt;
|Служба запущена, но статус &amp;lt;code&amp;gt;active with no instances&amp;lt;/code&amp;gt;&lt;br /&gt;
|Не задан &#039;&#039;&#039;Main node&#039;&#039;&#039;. Это ожидаемое поведение, а не ошибка&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается &#039;&#039;&#039;в браузере&#039;&#039;&#039;, но &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; работает&lt;br /&gt;
|ECH скрывает SNI. Добавить &amp;lt;code&amp;gt;cloudflare-ech.com&amp;lt;/code&amp;gt; в Proxy Domain List — см. раздел 4.3&lt;br /&gt;
|-&lt;br /&gt;
|Изменил список доменов — ничего не изменилось, панель пишет, что изменений нет&lt;br /&gt;
|Правка списка не перезапускает ядро. &#039;&#039;&#039;Диагностика → Перезапустить службу&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Мессенджер не подключается, хотя его сайт открывается&lt;br /&gt;
|Приложение ходит на серверы по IP. Нужен готовый список по сервису, содержащий IP-подсети — см. раздел 4.1&lt;br /&gt;
|-&lt;br /&gt;
|Zapret включён, но сервис по-прежнему замедлен&lt;br /&gt;
|Нет правила с назначением &#039;&#039;&#039;Zapret&#039;&#039;&#039;. Счётчик очереди нулевой — см. раздел 5.2&lt;br /&gt;
|-&lt;br /&gt;
|Пункт &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; отсутствует в списке типов узлов&lt;br /&gt;
|В OpenWrt-сборке ядра нет соответствующего флага. Установить musl-сборку — см. раздел 2.3&lt;br /&gt;
|-&lt;br /&gt;
|Интерфейс панели открывается по несколько секунд&lt;br /&gt;
|Ядро сжато UPX. Заменить на несжатый бинарник — см. предупреждение в разделе 2.3&lt;br /&gt;
|-&lt;br /&gt;
|Страницы «висят» по 30 секунд, затем открываются&lt;br /&gt;
|Браузер уходит в QUIC (UDP/443) мимо перехвата. Заблокировать UDP/443 действием &#039;&#039;&#039;REJECT&#039;&#039;&#039; (не DROP — при DROP браузер ждёт таймаута)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Удаление =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/homeproxy stop&lt;br /&gt;
/etc/init.d/homeproxy disable&lt;br /&gt;
opkg remove luci-app-re-homeproxy luci-i18n-homeproxy-ru&lt;br /&gt;
&lt;br /&gt;
# Ядро, если ставилось пакетом&lt;br /&gt;
opkg remove sing-box-extended&lt;br /&gt;
# Ядро, если бинарник копировался вручную&lt;br /&gt;
rm -f /usr/bin/sing-box&lt;br /&gt;
&lt;br /&gt;
# Остатки конфигурации и списков&lt;br /&gt;
rm -rf /etc/homeproxy /etc/config/homeproxy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Правила nftables снимаются автоматически при остановке службы. Настройки межсетевого экрана, проброс портов и прочая конфигурация роутера не затрагиваются.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Ссылки =&lt;br /&gt;
* [https://github.com/1andrevich/homeproxy-hiddify Re:HomeProxy — исходный код и релизы]&lt;br /&gt;
* [https://github.com/shtorm-7/sing-box-extended sing-box-extended — ядро]&lt;br /&gt;
* [https://github.com/1andrevich/Re-filter-lists Re-filter — списки заблокированных ресурсов]&lt;br /&gt;
* [https://github.com/itdoginfo/allow-domains itdoginfo/allow-domains — списки по сервисам]&lt;br /&gt;
* [https://github.com/bol-van/zapret2 Zapret 2 (nfqws2)]&lt;br /&gt;
* [https://github.com/hufrea/byedpi ByeDPI]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Сеть]]&lt;br /&gt;
[[Категория:OpenWrt]]&lt;br /&gt;
[[Категория:Обход блокировок]]&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A1%D1%83%D0%B2%D0%B5%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BD%D0%B5%D1%82&amp;diff=1036</id>
		<title>Суверенный интернет</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A1%D1%83%D0%B2%D0%B5%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BD%D0%B5%D1%82&amp;diff=1036"/>
		<updated>2026-08-06T10:49:45Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Эта страница посвящается всему что связано с ограничим доступа в интернет.&lt;br /&gt;
&lt;br /&gt;
На данный момент 20.04.26 мы имеем полную блокировку по белыми IP у операторов мобильной связи. И предпосылки к блокировкам на уровне провайдеров домашнего интернета.&lt;br /&gt;
&lt;br /&gt;
* [[Обход НКР|Установка и настройка связки Zapret + Podkop на OpenWRT]]&lt;br /&gt;
* [[Hysteria 2 каскад]]&lt;br /&gt;
* [[Whitelist-bypass: VK Creator (headless на сервере) + Joiner (Android)]]&lt;br /&gt;
* [[Установка MTProto Proxy (mtg) на Linux-сервере]]&lt;br /&gt;
* [[VK Turn Proxy + FreeTurn + VLESS]]&lt;br /&gt;
* [[NaïveProxy: установка полной каскадной цепочки]]&lt;br /&gt;
* [[OlcRTC: туннель через WebRTC-сервисы|olcRTC: туннель через легальные WebRTC-сервисы]]&lt;br /&gt;
* [[mieru: каскадный шифрованный SOCKS5-туннель]]&lt;br /&gt;
* [[Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)]]&lt;br /&gt;
* [[Mieru и NaïveProxy: каскадные туннели под управлением панели s-ui-x]]&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Mieru_%D0%B8_Na%C3%AFveProxy:_%D0%BA%D0%B0%D1%81%D0%BA%D0%B0%D0%B4%D0%BD%D1%8B%D0%B5_%D1%82%D1%83%D0%BD%D0%BD%D0%B5%D0%BB%D0%B8_%D0%BF%D0%BE%D0%B4_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D0%B0%D0%BD%D0%B5%D0%BB%D0%B8_s-ui-x&amp;diff=1035</id>
		<title>Mieru и NaïveProxy: каскадные туннели под управлением панели s-ui-x</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Mieru_%D0%B8_Na%C3%AFveProxy:_%D0%BA%D0%B0%D1%81%D0%BA%D0%B0%D0%B4%D0%BD%D1%8B%D0%B5_%D1%82%D1%83%D0%BD%D0%BD%D0%B5%D0%BB%D0%B8_%D0%BF%D0%BE%D0%B4_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5%D0%BC_%D0%BF%D0%B0%D0%BD%D0%B5%D0%BB%D0%B8_s-ui-x&amp;diff=1035"/>
		<updated>2026-08-06T07:10:29Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Объединение статей о mieru и NaïveProxy: актуальная схема с панелью s-ui-x, каскад, автопродление сертификатов&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= mieru и NaïveProxy: каскадные туннели под управлением панели s-ui-x =&lt;br /&gt;
&lt;br /&gt;
Эта статья объединяет два ранее раздельных руководства — по &#039;&#039;&#039;mieru&#039;&#039;&#039; и по &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; — в одно описание реально работающей инфраструктуры. Оба протокола развёрнуты параллельно, используют общее выходное плечо и управляются из &#039;&#039;&#039;одной панели&#039;&#039;&#039; на входном сервере.&lt;br /&gt;
&lt;br /&gt;
Статья описывает схему &#039;&#039;&#039;в том виде, в каком она работает сейчас&#039;&#039;&#039;. Прежний способ (голые Docker-контейнеры с правкой JSON и Caddyfile руками) не выброшен — он вынесен в [[#Приложение А. Ручная установка без панели|Приложение А]], поскольку остаётся актуальным для выходного плеча и как запасной путь.&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
&lt;br /&gt;
Двухзвенный каскад, в котором клиент никогда не видит адрес выходного сервера, а оба протокола управляются из общей веб-панели: пользователи, трафик, лимиты, сроки и подписки — в одном месте.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                    ВХОДНОЕ ПЛЕЧО (РФ)              ВЫХОДНОЕ ПЛЕЧО (EU)&lt;br /&gt;
                 +------------------------+      +----------------------+&lt;br /&gt;
 клиент --mieru-&amp;gt;| inbound mieru          |      |                      |&lt;br /&gt;
                 |      |                 |      |  mita  (mieru)       |&lt;br /&gt;
                 |      v  маршрутизация  |      |                      |&lt;br /&gt;
                 | outbound mieru --------+-----&amp;gt;|                      |&lt;br /&gt;
                 |                        |      |                      |&lt;br /&gt;
 клиент --naive-&amp;gt;| inbound naive          |      | Caddy + forwardproxy |&lt;br /&gt;
                 |      |                 |      |                      |&lt;br /&gt;
                 |      v                 |      |                      |&lt;br /&gt;
                 | outbound naive --------+-----&amp;gt;|                      |&lt;br /&gt;
                 |                        |      |          |           |&lt;br /&gt;
                 |  панель s-ui-x         |      |          v           |&lt;br /&gt;
                 |  подписки, учёт, лимиты|      |  egress (WARP/direct)|&lt;br /&gt;
                 +------------------------+      +----------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ключевая идея: &#039;&#039;&#039;входной сервер одновременно является и сервером для клиентов, и клиентом для выходного сервера&#039;&#039;&#039;. Оба протокола в панели заведены дважды — как inbound (принимает пользователей) и как outbound (уходит на выходное плечо), а связывают их правила маршрутизации. Это и есть каскад.&lt;br /&gt;
&lt;br /&gt;
=== Два протокола: чем отличаются и зачем нужны оба ===&lt;br /&gt;
&lt;br /&gt;
Смысл держать два канала одновременно в том, что они прячутся &#039;&#039;&#039;принципиально по-разному&#039;&#039;&#039; и ломаются от разных причин. Если один начнут давить — второй, скорее всего, устоит.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Признак&lt;br /&gt;
!mieru&lt;br /&gt;
!NaïveProxy&lt;br /&gt;
|-&lt;br /&gt;
|Транспорт&lt;br /&gt;
|TCP (или UDP), &#039;&#039;&#039;без TLS&#039;&#039;&#039;&lt;br /&gt;
|TCP, HTTPS + HTTP/2 CONNECT&lt;br /&gt;
|-&lt;br /&gt;
|Как прячется&lt;br /&gt;
|Поток выглядит как случайный шум либо текстоподобный трафик&lt;br /&gt;
|Неотличим от браузера Chrome, идущего на обычный сайт&lt;br /&gt;
|-&lt;br /&gt;
|Нужен домен и сертификат&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039; — только пара логин/пароль&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039; — валидный домен и TLS-сертификат&lt;br /&gt;
|-&lt;br /&gt;
|Реакция на активное зондирование&lt;br /&gt;
|Молчит — фингерпринтить нечего&lt;br /&gt;
|Отвечает как обычный сайт (&amp;lt;code&amp;gt;probe_resistance&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|Учёт трафика по пользователям&lt;br /&gt;
|Есть на уровне протокола&lt;br /&gt;
|Нет — считается панелью на входе&lt;br /&gt;
|-&lt;br /&gt;
|Клиенты&lt;br /&gt;
|Ставится штатно (mihomo, Karing, NekoBox)&lt;br /&gt;
|На Android нужен отдельный плагин (Cronet)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Практический вывод из эксплуатации: &#039;&#039;&#039;на мобильных основным каналом удобнее делать mieru&#039;&#039;&#039; — он импортируется без плясок. NaïveProxy держать на десктопе и как независимый резерв.&lt;br /&gt;
&lt;br /&gt;
=== Сравнение с другими методами туннелирования ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Метод&lt;br /&gt;
!Транспорт&lt;br /&gt;
!Маскировка&lt;br /&gt;
!Требует домен и TLS&lt;br /&gt;
!Устойчивость к ML-классификации&lt;br /&gt;
|-&lt;br /&gt;
|WireGuard&lt;br /&gt;
|UDP&lt;br /&gt;
|нет (явный WG)&lt;br /&gt;
|нет&lt;br /&gt;
|низкая&lt;br /&gt;
|-&lt;br /&gt;
|OpenVPN&lt;br /&gt;
|UDP/TCP&lt;br /&gt;
|нет&lt;br /&gt;
|опционально&lt;br /&gt;
|низкая&lt;br /&gt;
|-&lt;br /&gt;
|Shadowsocks-AEAD-2022&lt;br /&gt;
|TCP&lt;br /&gt;
|случайный шум&lt;br /&gt;
|нет&lt;br /&gt;
|средняя&lt;br /&gt;
|-&lt;br /&gt;
|VLESS + REALITY&lt;br /&gt;
|TCP (TLS)&lt;br /&gt;
|TLS-handshake чужого сайта&lt;br /&gt;
|да&lt;br /&gt;
|высокая (внутри пула TLS)&lt;br /&gt;
|-&lt;br /&gt;
|Hysteria2&lt;br /&gt;
|UDP (QUIC)&lt;br /&gt;
|QUIC с маскировкой под HTTPS&lt;br /&gt;
|да&lt;br /&gt;
|высокая (внутри пула QUIC)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;NaïveProxy&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;TCP (HTTP/2)&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;стек Chromium, неотличим от HTTPS&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;да&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;высокая (внутри пула HTTPS)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;mieru&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;TCP / UDP, без TLS&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;случайный шум либо текстоподобный поток&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;нет&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;высокая (подгонка энтропии и ASCII)&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Как работают протоколы ==&lt;br /&gt;
&lt;br /&gt;
=== mieru: шифрование без TLS ===&lt;br /&gt;
&lt;br /&gt;
Программный пакет состоит из двух самостоятельных бинарников:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Бинарник&lt;br /&gt;
!Роль&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt;&lt;br /&gt;
|Серверная часть. Принимает зашифрованные соединения, аутентифицирует пользователей, переправляет полезную нагрузку наружу&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;mieru&amp;lt;/code&amp;gt;&lt;br /&gt;
|Клиентская часть. Поднимает локальный SOCKS5-прокси; всё, что в него пришло, шифруется и уходит на &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
В отличие от большинства современных туннелей, mieru &#039;&#039;&#039;не использует TLS&#039;&#039;&#039; и &#039;&#039;&#039;не маскируется под легитимный сайт&#039;&#039;&#039;. Вместо этого он подаёт на провод поток, который при пассивном анализе выглядит как случайные байты или произвольный текстовый протокол — без узнаваемых заголовков, фиксированных длин и характерных handshake-паттернов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Mieru-single-flow.png|центр|685x685px|Поток данных одиночного mieru: клиент → SOCKS5 → mieru → шифр → mita → интернет]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Алгоритм&lt;br /&gt;
|XChaCha20-Poly1305 (AEAD) с 24-байтным nonce&lt;br /&gt;
|-&lt;br /&gt;
|Длина ключа&lt;br /&gt;
|32 байта&lt;br /&gt;
|-&lt;br /&gt;
|Выработка ключа&lt;br /&gt;
|PBKDF2-SHA256, 64 итерации, соль = SHA-256 от текущего времени, округлённого до 2 минут&lt;br /&gt;
|-&lt;br /&gt;
|Идентификатор пользователя&lt;br /&gt;
|Последние 4 байта nonce заменены на префикс SHA-256 от &amp;lt;code&amp;gt;username + nonce[0:16]&amp;lt;/code&amp;gt;; имя пользователя на провод &#039;&#039;&#039;не передаётся&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Защита от повтора&lt;br /&gt;
|Проверка nonce-кэша на сервере; повторный пакет отбрасывается&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ВСЕ&#039;&#039;&#039; структурные поля сегмента (тип, размер, sequence number, ID сессии) находятся внутри зашифрованного поля &amp;lt;code&amp;gt;enc-meta&amp;lt;/code&amp;gt;. Снаружи невозможно определить ни длину полезной нагрузки, ни фазу сессии — видны только зоны паддинга и шифр-блобы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Mieru-segment-format.png|центр|984x984px|Формат сегмента mieru: padding, nonce, шифр-метаданные, шифр-полезная нагрузка]]&lt;br /&gt;
&lt;br /&gt;
==== Требование к синхронизации часов ====&lt;br /&gt;
&lt;br /&gt;
Ключ шифрования &#039;&#039;&#039;не передаётся&#039;&#039;&#039; по каналу и нигде не хранится: он каждый раз вырабатывается из пары логин+пароль и &#039;&#039;&#039;текущего времени&#039;&#039;&#039;. Отсюда неочевидное требование:&lt;br /&gt;
&lt;br /&gt;
Расхождение системного времени клиента и сервера &#039;&#039;&#039;не должно превышать 4 минуты&#039;&#039;&#039;. При большем расхождении ключи не сойдутся, и подключения будут &#039;&#039;&#039;молча&#039;&#039;&#039; падать — без внятной ошибки в логах. На серверах и на клиентском устройстве обязан работать NTP.&lt;br /&gt;
&lt;br /&gt;
=== Traffic pattern: подгонка статистики под «обычный» трафик ===&lt;br /&gt;
&lt;br /&gt;
mieru умеет подменять статистические признаки шифр-потока:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Опция&lt;br /&gt;
!Что делает&lt;br /&gt;
!Накладные расходы&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tcpFragment&amp;lt;/code&amp;gt;&lt;br /&gt;
|Дробит TCP-пакеты на мелкие куски и вставляет случайную задержку. Ломает узнаваемость «один большой шифр-стрим»&lt;br /&gt;
|+латентность до &amp;lt;code&amp;gt;maxSleepMs&amp;lt;/code&amp;gt; мс на фрагмент&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nonce.NONCE_TYPE_PRINTABLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|Подменяет первые байты nonce печатными ASCII. Уводит классификатор из «высокая энтропия» в «это текстовый протокол»&lt;br /&gt;
|почти ноль&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nonce.NONCE_TYPE_FIXED&amp;lt;/code&amp;gt;&lt;br /&gt;
|Подменяет первые байты фиксированными hex-строками. Имитирует конкретный заголовок&lt;br /&gt;
|ноль&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Включать traffic pattern имеет смысл &#039;&#039;&#039;только&#039;&#039;&#039; на видимом анализатору плече (клиент → входной сервер). На внутреннем плече каскада это лишний overhead.&lt;br /&gt;
&lt;br /&gt;
=== NaïveProxy: маскировка под Chrome ===&lt;br /&gt;
&lt;br /&gt;
DPI «смотрит» не только на адреса, но и на характер соединения: сколько байт летит в каждую сторону, с какими интервалами, как выглядит TLS-рукопожатие, какой у соединения отпечаток. Обычный HTTPS-прокси выявляется именно потому, что программа-клиент общается &#039;&#039;&#039;иначе&#039;&#039;&#039;, чем настоящий браузер.&lt;br /&gt;
&lt;br /&gt;
NaïveProxy решает это принципиально: клиентская часть использует &#039;&#039;&#039;встроенный сетевой стек Chromium&#039;&#039;&#039; (Cronet), поэтому трафик неотличим от реального Chrome не только по содержимому, но и по поведению на уровне TCP/TLS — те же задержки, те же размеры пакетов, тот же TLS-fingerprint. Серверная часть притворяется обычным веб-сервером.&lt;br /&gt;
&lt;br /&gt;
Механика:&lt;br /&gt;
&lt;br /&gt;
# Клиент открывает HTTPS-соединение с сервером — точь-в-точь как Chrome.&lt;br /&gt;
# Внутри соединения по HTTP/2 CONNECT прокидывается туннель.&lt;br /&gt;
# Сервер обрабатывает обычные запросы как веб-сервер, а туннельные — переправляет дальше.&lt;br /&gt;
# Если кто-то «постучится» без верных учётных данных, сервер ответит как обычный сайт — это &#039;&#039;&#039;probe_resistance&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Почему каскад из двух серверов ===&lt;br /&gt;
&lt;br /&gt;
[[Файл:Mieru-cascade-flow.png|центр|1010x1010px|Поток данных каскадного mieru: входной сервер → выходной сервер → egress]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Адрес выходного сервера никогда не виден клиентскому ПО.&#039;&#039;&#039; Если входной сервер заблокируют — выходной остаётся неизвестным и переиспользуется со следующим входным.&lt;br /&gt;
* &#039;&#039;&#039;Входной сервер хранит только учётки клиентов&#039;&#039;&#039; и не имеет финального egress. Компрометация его конфигурации не раскрывает финальный IP.&lt;br /&gt;
* &#039;&#039;&#039;Двойное шифрование&#039;&#039;&#039; на каждом плече: сегмент дважды проходит AEAD с разными ключами.&lt;br /&gt;
* &#039;&#039;&#039;Подключение к близкому серверу менее заметно&#039;&#039;&#039;, чем прямое соединение с зарубежным.&lt;br /&gt;
----&lt;br /&gt;
== Требования ==&lt;br /&gt;
&lt;br /&gt;
=== Серверы ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!№&lt;br /&gt;
!Требование&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|Два Linux-сервера (Ubuntu 22.04+, Debian 12+ или сравнимый)&lt;br /&gt;
|Входное и выходное плечо каскада&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|SSH с правами &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; либо &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; без пароля&lt;br /&gt;
|Установка пакетов и управление сервисами&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|Docker Engine 24.0+ и Docker Compose v2.20+ &#039;&#039;&#039;на выходном сервере&#039;&#039;&#039;&lt;br /&gt;
|Контейнеры &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt; и Caddy. На входном сервере Docker не нужен — там всё нативно&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|NTP активен (&amp;lt;code&amp;gt;chrony&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;systemd-timesyncd&amp;lt;/code&amp;gt;) &#039;&#039;&#039;на обоих&#039;&#039;&#039;&lt;br /&gt;
|Синхронизация часов в пределах 4 минут (см. mieru)&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|Открытый наружу TCP-диапазон &amp;lt;code&amp;gt;2012-2022&amp;lt;/code&amp;gt; на обоих серверах&lt;br /&gt;
|Принимающие порты mieru&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|Открытые TCP &amp;lt;code&amp;gt;80&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;443&amp;lt;/code&amp;gt; на обоих серверах&lt;br /&gt;
|NaïveProxy и выпуск сертификатов&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DNS-записи ===&lt;br /&gt;
&lt;br /&gt;
Создать &#039;&#039;&#039;до&#039;&#039;&#039; начала установки — сертификаты выпускаются по ним:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Запись&lt;br /&gt;
!Указывает на&lt;br /&gt;
!Для чего&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_ENTRY_DOMAIN&amp;lt;/code&amp;gt; (например &amp;lt;code&amp;gt;np.example.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
|IP входного сервера&lt;br /&gt;
|NaïveProxy-вход для клиентов&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_PANEL_DOMAIN&amp;lt;/code&amp;gt; (например &amp;lt;code&amp;gt;panel.example.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
|IP входного сервера&lt;br /&gt;
|Панель и подписки по HTTPS&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_EXIT_DOMAIN&amp;lt;/code&amp;gt; (например &amp;lt;code&amp;gt;npeu.example.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
|IP выходного сервера&lt;br /&gt;
|NaïveProxy-приём от входного сервера&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Панель и NaïveProxy-вход &#039;&#039;&#039;могут&#039;&#039;&#039; жить на одном домене — они разведены по портам. Разные домены удобнее: вход выглядит как обычный сайт, а панель не светится рядом.&lt;br /&gt;
&lt;br /&gt;
=== Карта портов ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Порт&lt;br /&gt;
!Плечо&lt;br /&gt;
!Кто слушает&lt;br /&gt;
!Наружу&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;2012-2022/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
|оба&lt;br /&gt;
|mieru (inbound на входе, &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt; на выходе)&lt;br /&gt;
|да&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;443/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
|оба&lt;br /&gt;
|NaïveProxy (inbound на входе, Caddy на выходе)&lt;br /&gt;
|да&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;80/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
|оба&lt;br /&gt;
|ACME http-01 (выпуск сертификатов)&lt;br /&gt;
|да, обязательно свободен&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;2095/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
|вход&lt;br /&gt;
|веб-панель s-ui-x&lt;br /&gt;
|ограничить своим IP&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;2097/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
|вход&lt;br /&gt;
|подписки&lt;br /&gt;
|да (клиенты забирают конфиги)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Порт 80 держать свободным.&#039;&#039;&#039; Он нужен ACME для автопродления. Именно из-за занятого веб-сервером 80-го порта продление сертификатов может молча срываться — разбор этой ошибки в разделе [[#Диагностика|Диагностика]].&lt;br /&gt;
&lt;br /&gt;
== Параметры, которые нужно подготовить заранее ==&lt;br /&gt;
&lt;br /&gt;
Заполните таблицу своими значениями — дальше по тексту встречаются эти плейсхолдеры.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Плейсхолдер&lt;br /&gt;
!Что это&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_ENTRY_HOST&amp;lt;/code&amp;gt;&lt;br /&gt;
|Адрес входного сервера для SSH&lt;br /&gt;
|&amp;lt;code&amp;gt;198.51.100.10&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_EXIT_HOST&amp;lt;/code&amp;gt;&lt;br /&gt;
|Адрес выходного сервера для SSH&lt;br /&gt;
|&amp;lt;code&amp;gt;203.0.113.20&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_EXIT_IP&amp;lt;/code&amp;gt;&lt;br /&gt;
|Публичный IP выходного сервера — к нему подключается входной&lt;br /&gt;
|&amp;lt;code&amp;gt;203.0.113.20&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_ENTRY_DOMAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домен NaïveProxy-входа&lt;br /&gt;
|&amp;lt;code&amp;gt;np.example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_EXIT_DOMAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домен NaïveProxy-выхода&lt;br /&gt;
|&amp;lt;code&amp;gt;npeu.example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_PANEL_DOMAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домен панели&lt;br /&gt;
|&amp;lt;code&amp;gt;panel.example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_BRIDGE_PASSWORD&amp;lt;/code&amp;gt;&lt;br /&gt;
|Пароль служебной mieru-учётки, которой вход ходит на выход&lt;br /&gt;
|&amp;lt;code&amp;gt;openssl rand -base64 24&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_CHAIN_PASSWORD&amp;lt;/code&amp;gt;&lt;br /&gt;
|Пароль служебной naive-учётки (то же назначение)&lt;br /&gt;
|&amp;lt;code&amp;gt;openssl rand -hex 16&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_EGRESS_PORT&amp;lt;/code&amp;gt;&lt;br /&gt;
|Порт SOCKS5-egress на выходном сервере (если используется)&lt;br /&gt;
|&amp;lt;code&amp;gt;24366&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Служебные учётки (&amp;lt;code&amp;gt;bridge&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;chain&amp;lt;/code&amp;gt;) &#039;&#039;&#039;клиентам не раздаются никогда&#039;&#039;&#039; — это внутренние перемычки каскада.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Выходное плечо =&lt;br /&gt;
&lt;br /&gt;
Выходной сервер — «немой» конец каскада: он не знает о клиентах, у него всего &#039;&#039;&#039;две&#039;&#039;&#039; учётные записи, по одной на протокол. Управляется файлами, панель здесь не нужна.&lt;br /&gt;
&lt;br /&gt;
Все команды этого этапа выполняются на выходном сервере.&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Docker ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh root@YOUR_EXIT_HOST&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -fsSL https://get.docker.com | sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker --version &amp;amp;&amp;amp; docker compose version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. mieru: образ ==&lt;br /&gt;
&lt;br /&gt;
Рабочий каталог:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p /opt/mieru-exit &amp;amp;&amp;amp; cd /opt/mieru-exit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Официальных образов у mieru нет, поэтому собираем свой — бинарники берутся из релизов upstream:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-exit/Dockerfile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;docker&amp;quot;&amp;gt;&lt;br /&gt;
# Образ с mita и mieru, бинарники качаются из релизов upstream&lt;br /&gt;
# Версия задаётся через --build-arg MIERU_VERSION&lt;br /&gt;
ARG MIERU_VERSION=3.32.0&lt;br /&gt;
ARG TARGETARCH=amd64&lt;br /&gt;
&lt;br /&gt;
FROM alpine:3.19 AS downloader&lt;br /&gt;
ARG MIERU_VERSION&lt;br /&gt;
ARG TARGETARCH&lt;br /&gt;
RUN apk add --no-cache ca-certificates wget&lt;br /&gt;
WORKDIR /tmp/mita&lt;br /&gt;
RUN wget -qO- &amp;quot;https://github.com/enfein/mieru/releases/download/v${MIERU_VERSION}/mita_${MIERU_VERSION}_linux_${TARGETARCH}.tar.gz&amp;quot; | tar xz&lt;br /&gt;
WORKDIR /tmp/mieru&lt;br /&gt;
RUN wget -qO- &amp;quot;https://github.com/enfein/mieru/releases/download/v${MIERU_VERSION}/mieru_${MIERU_VERSION}_linux_${TARGETARCH}.tar.gz&amp;quot; | tar xz&lt;br /&gt;
&lt;br /&gt;
FROM alpine:3.19&lt;br /&gt;
RUN apk add --no-cache ca-certificates tini &amp;amp;&amp;amp; \&lt;br /&gt;
    adduser -H -D -g &amp;quot;&amp;quot; mita &amp;amp;&amp;amp; \&lt;br /&gt;
    adduser -H -D -g &amp;quot;&amp;quot; mieru&lt;br /&gt;
COPY --from=downloader /tmp/mita/mita /usr/local/bin/mita&lt;br /&gt;
COPY --from=downloader /tmp/mieru/mieru /usr/local/bin/mieru&lt;br /&gt;
RUN chmod +x /usr/local/bin/mita /usr/local/bin/mieru &amp;amp;&amp;amp; \&lt;br /&gt;
    mkdir -p /etc/mita /var/lib/mita /var/run/mita \&lt;br /&gt;
             /etc/mieru /var/lib/mieru /var/run/mieru /srv &amp;amp;&amp;amp; \&lt;br /&gt;
    chown -R mita:mita /etc/mita /var/lib/mita /var/run/mita &amp;amp;&amp;amp; \&lt;br /&gt;
    chown -R mieru:mieru /etc/mieru /var/lib/mieru /var/run/mieru&lt;br /&gt;
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh&lt;br /&gt;
RUN chmod +x /usr/local/bin/docker-entrypoint.sh&lt;br /&gt;
ENTRYPOINT [&amp;quot;/sbin/tini&amp;quot;,&amp;quot;--&amp;quot;,&amp;quot;/usr/local/bin/docker-entrypoint.sh&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Две строки &amp;lt;code&amp;gt;adduser&amp;lt;/code&amp;gt; здесь &#039;&#039;&#039;обязательны&#039;&#039;&#039;. Без системных пользователей &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;mieru&amp;lt;/code&amp;gt; не создаётся Unix-сокет для RPC, и контейнер падает с &amp;lt;code&amp;gt;FATAL: getUid(&amp;quot;mita&amp;quot;) failed&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== 1.3. mieru: entrypoint ==&lt;br /&gt;
&lt;br /&gt;
У &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt; неочевидная модель запуска: демон читает конфигурацию &#039;&#039;&#039;не из файла&#039;&#039;&#039;, а через RPC — то есть конфиг применяется к &#039;&#039;&#039;уже работающему&#039;&#039;&#039; демону. Получается замкнутый круг, который entrypoint разрывает: запускает демон на переднем плане, а в фоне ретраит применение конфигурации.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-exit/docker-entrypoint.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/sh&lt;br /&gt;
# Запускает mita или mieru в foreground, в фоне применяет JSON-конфиг&lt;br /&gt;
# через RPC (mita/mieru apply config). RPC становится доступен&lt;br /&gt;
# через 1-2 секунды после старта daemon — отсюда retry-цикл.&lt;br /&gt;
&lt;br /&gt;
set -eu&lt;br /&gt;
&lt;br /&gt;
MODE=&amp;quot;${MIERU_MODE:-mita}&amp;quot;&lt;br /&gt;
CONFIG_FILE=&amp;quot;${MIERU_CONFIG:-/srv/config.json}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
case &amp;quot;$MODE&amp;quot; in&lt;br /&gt;
    mita)  BIN=mita ;;&lt;br /&gt;
    mieru) BIN=mieru ;;&lt;br /&gt;
    *) echo &amp;quot;[entrypoint] FATAL: MIERU_MODE=&#039;$MODE&#039; (ожидается mita или mieru)&amp;quot;; exit 1 ;;&lt;br /&gt;
esac&lt;br /&gt;
&lt;br /&gt;
apply_config_and_start() {&lt;br /&gt;
    if [ ! -f &amp;quot;$CONFIG_FILE&amp;quot; ]; then&lt;br /&gt;
        echo &amp;quot;[entrypoint] WARN: $CONFIG_FILE не найден, конфиг не применяется&amp;quot;&lt;br /&gt;
        return 0&lt;br /&gt;
    fi&lt;br /&gt;
    i=0&lt;br /&gt;
    while [ &amp;quot;$i&amp;quot; -lt 30 ]; do&lt;br /&gt;
        i=$((i+1))&lt;br /&gt;
        sleep 1&lt;br /&gt;
        if &amp;quot;$BIN&amp;quot; apply config &amp;quot;$CONFIG_FILE&amp;quot; &amp;gt;/tmp/apply.out 2&amp;gt;&amp;amp;1; then&lt;br /&gt;
            echo &amp;quot;[entrypoint] config применён ($CONFIG_FILE)&amp;quot;&lt;br /&gt;
            # после apply config daemon в IDLE — нужен start, чтобы открыть сокеты&lt;br /&gt;
            if &amp;quot;$BIN&amp;quot; start &amp;gt;/tmp/start.out 2&amp;gt;&amp;amp;1; then&lt;br /&gt;
                echo &amp;quot;[entrypoint] $BIN start: OK&amp;quot;&lt;br /&gt;
            else&lt;br /&gt;
                echo &amp;quot;[entrypoint] WARN: $BIN start failed:&amp;quot;&lt;br /&gt;
                cat /tmp/start.out 2&amp;gt;/dev/null || true&lt;br /&gt;
            fi&lt;br /&gt;
            return 0&lt;br /&gt;
        fi&lt;br /&gt;
    done&lt;br /&gt;
    echo &amp;quot;[entrypoint] WARN: не удалось применить config за 30s, последний вывод:&amp;quot;&lt;br /&gt;
    cat /tmp/apply.out 2&amp;gt;/dev/null || true&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
apply_config_and_start &amp;amp;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[entrypoint] запуск $BIN run&amp;quot;&lt;br /&gt;
exec &amp;quot;$BIN&amp;quot; run&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на &amp;lt;code&amp;gt;&amp;quot;$BIN&amp;quot; start&amp;lt;/code&amp;gt; после &amp;lt;code&amp;gt;apply config&amp;lt;/code&amp;gt;. &#039;&#039;&#039;&amp;lt;code&amp;gt;mita run&amp;lt;/code&amp;gt; сам по себе не открывает прокси-порты&#039;&#039;&#039; — демон висит в состоянии &amp;lt;code&amp;gt;IDLE&amp;lt;/code&amp;gt; до явной команды &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;. Это одна из самых частых причин «сервис работает, но порты не слушаются».&lt;br /&gt;
&lt;br /&gt;
== 1.4. mieru: конфигурация выходного узла ==&lt;br /&gt;
&lt;br /&gt;
Сгенерируйте пароль служебной учётки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
openssl rand -base64 24&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-exit/server.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;portBindings&amp;quot;: [&lt;br /&gt;
        { &amp;quot;portRange&amp;quot;: &amp;quot;2012-2022&amp;quot;, &amp;quot;protocol&amp;quot;: &amp;quot;TCP&amp;quot; }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;users&amp;quot;: [&lt;br /&gt;
        { &amp;quot;name&amp;quot;: &amp;quot;ru-bridge&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;YOUR_BRIDGE_PASSWORD&amp;quot; }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;loggingLevel&amp;quot;: &amp;quot;INFO&amp;quot;,&lt;br /&gt;
    &amp;quot;mtu&amp;quot;: 1380,&lt;br /&gt;
    &amp;quot;egress&amp;quot;: {&lt;br /&gt;
        &amp;quot;proxies&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;warp-via-xui&amp;quot;,&lt;br /&gt;
                &amp;quot;protocol&amp;quot;: &amp;quot;SOCKS5_PROXY_PROTOCOL&amp;quot;,&lt;br /&gt;
                &amp;quot;host&amp;quot;: &amp;quot;172.17.0.1&amp;quot;,&lt;br /&gt;
                &amp;quot;port&amp;quot;: 24366&lt;br /&gt;
            }&lt;br /&gt;
        ],&lt;br /&gt;
        &amp;quot;rules&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;ipRanges&amp;quot;: [&amp;quot;*&amp;quot;],&lt;br /&gt;
                &amp;quot;domainNames&amp;quot;: [&amp;quot;*&amp;quot;],&lt;br /&gt;
                &amp;quot;action&amp;quot;: &amp;quot;PROXY&amp;quot;,&lt;br /&gt;
                &amp;quot;proxyNames&amp;quot;: [&amp;quot;warp-via-xui&amp;quot;]&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Учётка &#039;&#039;&#039;одна&#039;&#039;&#039; — та самая перемычка каскада. Клиентских учёток здесь нет и быть не должно: клиенты живут на входном сервере.&lt;br /&gt;
&lt;br /&gt;
Блок &amp;lt;code&amp;gt;egress&amp;lt;/code&amp;gt; нужен, если выходной трафик должен идти не напрямую, а через сторонний прокси (например, SOCKS5-inbound панели 3x-ui, ведущий на WARP). Адрес &amp;lt;code&amp;gt;172.17.0.1&amp;lt;/code&amp;gt; — это шлюз docker0: контейнер с &amp;lt;code&amp;gt;network_mode: host&amp;lt;/code&amp;gt; видит локальный SOCKS-inbound именно по этому адресу, а &#039;&#039;&#039;не&#039;&#039;&#039; по &amp;lt;code&amp;gt;127.0.0.1&amp;lt;/code&amp;gt;. Если egress не нужен — удалите блок целиком, тогда выход будет с публичного IP сервера.&lt;br /&gt;
&lt;br /&gt;
Проверить egress-цепочку (подставьте свой порт):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker run --rm curlimages/curl:latest --max-time 12 --socks5 172.17.0.1:24366 https://icanhazip.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ожидается IP вашего egress-узла. Если вернулся публичный IP самой машины — egress не работает, смотрите маршрутизацию в панели.&lt;br /&gt;
&lt;br /&gt;
== 1.5. mieru: запуск ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-exit/docker-compose.yml&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
services:&lt;br /&gt;
  mita-exit:&lt;br /&gt;
    build:&lt;br /&gt;
      context: .&lt;br /&gt;
      args:&lt;br /&gt;
        MIERU_VERSION: &amp;quot;3.32.0&amp;quot;&lt;br /&gt;
    image: mieru-bundle:3.32.0&lt;br /&gt;
    container_name: mita-exit&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
    network_mode: host&lt;br /&gt;
    environment:&lt;br /&gt;
      MIERU_MODE: mita&lt;br /&gt;
      MIERU_CONFIG: /srv/server.json&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./server.json:/srv/server.json:ro&lt;br /&gt;
      - mita-exit-state:/etc/mita&lt;br /&gt;
      - mita-exit-runtime:/var/run/mita&lt;br /&gt;
      - mita-exit-data:/var/lib/mita&lt;br /&gt;
    logging:&lt;br /&gt;
      driver: json-file&lt;br /&gt;
      options:&lt;br /&gt;
        max-size: &amp;quot;10m&amp;quot;&lt;br /&gt;
        max-file: &amp;quot;3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
volumes:&lt;br /&gt;
  mita-exit-state:&lt;br /&gt;
  mita-exit-runtime:&lt;br /&gt;
  mita-exit-data:&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /opt/mieru-exit &amp;amp;&amp;amp; docker compose up -d --build&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker exec mita-exit mita status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ожидается &amp;lt;code&amp;gt;mita server status is &amp;quot;RUNNING&amp;quot;&amp;lt;/code&amp;gt;. Если &amp;lt;code&amp;gt;IDLE&amp;lt;/code&amp;gt; — выполните &amp;lt;code&amp;gt;docker exec mita-exit mita start&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== 1.6. NaïveProxy: образ Caddy с forwardproxy ==&lt;br /&gt;
&lt;br /&gt;
Штатный Caddy не умеет NaïveProxy — нужен форк плагина от автора naiveproxy, вкомпилированный через &amp;lt;code&amp;gt;xcaddy&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p /opt/caddy-naive/www &amp;amp;&amp;amp; cd /opt/caddy-naive&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/caddy-naive/Dockerfile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;docker&amp;quot;&amp;gt;&lt;br /&gt;
FROM golang:1.25-alpine AS builder&lt;br /&gt;
&lt;br /&gt;
RUN apk add --no-cache git&lt;br /&gt;
&lt;br /&gt;
RUN go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest&lt;br /&gt;
&lt;br /&gt;
RUN xcaddy build \&lt;br /&gt;
    --with github.com/caddyserver/forwardproxy=github.com/klzgrad/forwardproxy@naive&lt;br /&gt;
&lt;br /&gt;
FROM debian:bookworm-slim&lt;br /&gt;
&lt;br /&gt;
RUN apt-get update &amp;amp;&amp;amp; \&lt;br /&gt;
    apt-get install -y ca-certificates &amp;amp;&amp;amp; \&lt;br /&gt;
    rm -rf /var/lib/apt/lists/*&lt;br /&gt;
&lt;br /&gt;
COPY --from=builder /go/caddy /usr/local/bin/caddy&lt;br /&gt;
&lt;br /&gt;
EXPOSE 80 443&lt;br /&gt;
&lt;br /&gt;
CMD [&amp;quot;caddy&amp;quot;, &amp;quot;run&amp;quot;, &amp;quot;--config&amp;quot;, &amp;quot;/etc/caddy/Caddyfile&amp;quot;, &amp;quot;--adapter&amp;quot;, &amp;quot;caddyfile&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ветка &amp;lt;code&amp;gt;@naive&amp;lt;/code&amp;gt; в строке &amp;lt;code&amp;gt;--with&amp;lt;/code&amp;gt; обязательна: это форк &amp;lt;code&amp;gt;klzgrad&amp;lt;/code&amp;gt;, а не оригинальный forwardproxy.&lt;br /&gt;
&lt;br /&gt;
== 1.7. NaïveProxy: Caddyfile ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/caddy-naive/Caddyfile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  order forward_proxy before file_server&lt;br /&gt;
}&lt;br /&gt;
:443, YOUR_EXIT_DOMAIN {&lt;br /&gt;
  tls your-email@example.com&lt;br /&gt;
  forward_proxy {&lt;br /&gt;
    basic_auth ru-chain YOUR_CHAIN_PASSWORD&lt;br /&gt;
    upstream socks5://127.0.0.1:24363&lt;br /&gt;
    hide_ip&lt;br /&gt;
    hide_via&lt;br /&gt;
    probe_resistance&lt;br /&gt;
  }&lt;br /&gt;
  file_server {&lt;br /&gt;
    root /var/www/html&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Разбор директив:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Директива&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tls your-email@example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|Caddy сам выпустит и будет продлевать сертификат Let&#039;s Encrypt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;basic_auth ru-chain ...&amp;lt;/code&amp;gt;&lt;br /&gt;
|Учётка перемычки. Строк &amp;lt;code&amp;gt;basic_auth&amp;lt;/code&amp;gt; может быть несколько — по одной на пользователя&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;upstream socks5://...&amp;lt;/code&amp;gt;&lt;br /&gt;
|Куда отдавать расшифрованный трафик (SOCKS5-inbound панели, WARP и т.п.)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hide_ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hide_via&amp;lt;/code&amp;gt;&lt;br /&gt;
|Вырезают заголовки, выдающие проксирование&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;probe_resistance&amp;lt;/code&amp;gt;&lt;br /&gt;
|При неверных учётных данных сервер ведёт себя как обычный сайт, а не как прокси&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;file_server&amp;lt;/code&amp;gt;&lt;br /&gt;
|Отдаёт статическую заглушку тем, кто зашёл «просто так»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Критично:&#039;&#039;&#039; плагин &amp;lt;code&amp;gt;klzgrad/forwardproxy&amp;lt;/code&amp;gt; &#039;&#039;&#039;не понимает bcrypt-хэши&#039;&#039;&#039; — только plaintext. Если вписать хэш, сервер не выдаст ошибку авторизации, а вернёт &amp;lt;code&amp;gt;308 Redirect&amp;lt;/code&amp;gt; (сработает &amp;lt;code&amp;gt;probe_resistance&amp;lt;/code&amp;gt;), и диагностика уведёт совсем не туда.&lt;br /&gt;
&lt;br /&gt;
Заглушка, чтобы сайт выглядел живым:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;&amp;lt;!doctype html&amp;gt;&amp;lt;title&amp;gt;It works&amp;lt;/title&amp;gt;&amp;lt;h1&amp;gt;It works&amp;lt;/h1&amp;gt;&#039; &amp;gt; /opt/caddy-naive/www/index.html&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.8. NaïveProxy: запуск ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/caddy-naive/docker-compose.yml&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
services:&lt;br /&gt;
  caddy-naive:&lt;br /&gt;
    build: .&lt;br /&gt;
    container_name: caddy-naive&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
    network_mode: host&lt;br /&gt;
    environment:&lt;br /&gt;
      - XDG_DATA_HOME=/data&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./Caddyfile:/etc/caddy/Caddyfile:ro&lt;br /&gt;
      - ./www:/var/www/html:ro&lt;br /&gt;
      - caddy_data:/data&lt;br /&gt;
      - caddy_logs:/var/log/caddy&lt;br /&gt;
&lt;br /&gt;
volumes:&lt;br /&gt;
  caddy_data:&lt;br /&gt;
  caddy_logs:&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /opt/caddy-naive &amp;amp;&amp;amp; docker compose up -d --build&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сборка занимает несколько минут — компилируется Caddy. Проверка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker logs caddy-naive --tail 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sI https://YOUR_EXIT_DOMAIN | head -3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Должна вернуться заглушка с валидным сертификатом. Если сертификат не выпустился — проверьте, что порт 80 доступен снаружи и DNS-запись уже указывает на этот сервер.&lt;br /&gt;
&lt;br /&gt;
== 1.9. Итог этапа ==&lt;br /&gt;
&lt;br /&gt;
На выходном сервере работают два независимых приёмника:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Служба&lt;br /&gt;
!Порт&lt;br /&gt;
!Учётка&lt;br /&gt;
!Куда отдаёт трафик&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;mita-exit&amp;lt;/code&amp;gt; (mieru)&lt;br /&gt;
|&amp;lt;code&amp;gt;2012-2022/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;ru-bridge&amp;lt;/code&amp;gt;&lt;br /&gt;
|egress SOCKS5 → WARP либо напрямую&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;caddy-naive&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;443/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;ru-chain&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;upstream socks5://…&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Оба ждут подключений &#039;&#039;&#039;только&#039;&#039;&#039; от входного сервера. Клиенты сюда не ходят.&lt;br /&gt;
----&lt;br /&gt;
= Этап 2. Входное плечо: панель s-ui-x =&lt;br /&gt;
&lt;br /&gt;
== 2.1. Почему панель, а не голые контейнеры ==&lt;br /&gt;
&lt;br /&gt;
Исторически оба протокола на входном сервере жили так же, как сейчас живут на выходном: &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt; в Docker с правкой &amp;lt;code&amp;gt;server.json&amp;lt;/code&amp;gt; и Caddy с учётками в &amp;lt;code&amp;gt;Caddyfile&amp;lt;/code&amp;gt;. Для двух служебных перемычек это нормально, но как только появляются &#039;&#039;&#039;живые пользователи&#039;&#039;&#039;, схема начинает мешать:&lt;br /&gt;
&lt;br /&gt;
* добавление клиента — правка JSON или Caddyfile плюс перезапуск контейнера;&lt;br /&gt;
* у NaïveProxy &#039;&#039;&#039;вообще нет&#039;&#039;&#039; учёта трафика: Caddy не считает, кто сколько выкачал;&lt;br /&gt;
* лимиты по объёму и срокам приходится изобретать самому;&lt;br /&gt;
* раздача конфигов — руками, никаких подписок;&lt;br /&gt;
* два протокола = два несвязанных набора учёток.&lt;br /&gt;
&lt;br /&gt;
Панель &#039;&#039;&#039;s-ui-x-extended&#039;&#039;&#039; закрывает это целиком. Она построена на форке ядра &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;, в котором mieru и NaïveProxy реализованы &#039;&#039;&#039;нативно&#039;&#039;&#039; — и как inbound (приём клиентов), и как outbound (уход на выходное плечо). Каскад собирается правилами маршрутизации внутри панели.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Роль&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;s-ui-x-extended&amp;lt;/code&amp;gt;&lt;br /&gt;
|Веб-панель: пользователи, трафик, лимиты, подписки, правила&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;sing-box-extended&amp;lt;/code&amp;gt;&lt;br /&gt;
|Ядро. &#039;&#039;&#039;Встроено в бинарь панели&#039;&#039;&#039; — отдельного процесса sing-box нет&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Важно для доверия к схеме: mieru-inbound в этом форке использует &#039;&#039;&#039;официальную библиотеку&#039;&#039;&#039; &amp;lt;code&amp;gt;enfein/mieru/v3/apis/server&amp;lt;/code&amp;gt; — ту же, на которой работает &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt;. Протокол идентичен, ранее розданные клиентские конфиги остаются валидными.&lt;br /&gt;
&lt;br /&gt;
Панель ставится &#039;&#039;&#039;рядом&#039;&#039;&#039; с уже существующими сервисами (например, 3x-ui с VLESS/Trojan/Hysteria2) и их не трогает — важно лишь развести порты.&lt;br /&gt;
&lt;br /&gt;
== 2.2. Установка ==&lt;br /&gt;
&lt;br /&gt;
Все команды этапа выполняются на входном сервере.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh root@YOUR_ENTRY_HOST&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Перед установкой освободите порты &amp;lt;code&amp;gt;2012-2022&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;443&amp;lt;/code&amp;gt;, если их занимали прежние сервисы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ss -tulpn | grep -E &amp;quot;:(443|80|201[2-9]|202[0-2]) &amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установщик:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
SUI_LANG=ru bash &amp;lt;(curl -Ls https://raw.githubusercontent.com/deposist/s-ui-x-extended/main/install.sh)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Он задаст несколько вопросов. Отвечать так:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вопрос&lt;br /&gt;
!Ответ&lt;br /&gt;
!Почему&lt;br /&gt;
|-&lt;br /&gt;
|Продолжить настройку?&lt;br /&gt;
|&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&lt;br /&gt;
|Иначе порты останутся дефолтными&lt;br /&gt;
|-&lt;br /&gt;
|Порт панели&lt;br /&gt;
|&amp;lt;code&amp;gt;2095&amp;lt;/code&amp;gt;&lt;br /&gt;
|Значение по умолчанию, обычно свободно&lt;br /&gt;
|-&lt;br /&gt;
|Путь панели&lt;br /&gt;
|оставить пустым&lt;br /&gt;
|Сгенерируется случайный — так лучше&lt;br /&gt;
|-&lt;br /&gt;
|Порт подписки&lt;br /&gt;
|&#039;&#039;&#039;&amp;lt;code&amp;gt;2097&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Не 2096!&#039;&#039;&#039; См. врезку ниже&lt;br /&gt;
|-&lt;br /&gt;
|Путь подписки&lt;br /&gt;
|оставить пустым&lt;br /&gt;
|—&lt;br /&gt;
|-&lt;br /&gt;
|Сменить логин/пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; и задать свои&lt;br /&gt;
|Иначе будут случайные&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Грабля, стоящая отдельного упоминания.&#039;&#039;&#039; По умолчанию панель занимает под подписки порт &amp;lt;code&amp;gt;2096&amp;lt;/code&amp;gt; — ровно тот, который использует &#039;&#039;&#039;3x-ui&#039;&#039;&#039; для своих подписок. Если 3x-ui уже стоит на этом сервере, панель уйдёт в бесконечный цикл перезапусков с ошибкой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
journalctl -u s-ui -n 20 --no-pager&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
web server run http on[::]:2095&lt;br /&gt;
listen tcp :2096: bind: address already in use&lt;br /&gt;
s-ui.service: Main process exited, code=exited, status=1/FAILURE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Лечится сменой порта:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl stop s-ui &amp;amp;&amp;amp; /usr/local/s-ui/sui setting -subPort 2097 &amp;amp;&amp;amp; systemctl start s-ui&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверка:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl is-active s-ui &amp;amp;&amp;amp; /usr/local/s-ui/sui setting -show&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.3. Учётные данные и первый вход ==&lt;br /&gt;
&lt;br /&gt;
Задать логин и пароль администратора можно в любой момент:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/usr/local/s-ui/sui admin -username YOUR_ADMIN -password YOUR_ADMIN_PASSWORD&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установщик оставляет файл со стартовым паролем — удалите его после первого входа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
rm -f /usr/local/s-ui/db/initial-admin.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Полезные команды управления:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
s-ui                      # интерактивное меню&lt;br /&gt;
systemctl status s-ui     # состояние сервиса&lt;br /&gt;
journalctl -u s-ui -f     # логи ядра в реальном времени&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.4. Сертификаты и автопродление ==&lt;br /&gt;
&lt;br /&gt;
Панель и подписки &#039;&#039;&#039;обязаны&#039;&#039;&#039; работать по HTTPS: через них ходят пароли администратора и клиентские конфигурации. NaïveProxy-инбаунду сертификат нужен по определению.&lt;br /&gt;
&lt;br /&gt;
Используем &amp;lt;code&amp;gt;acme.sh&amp;lt;/code&amp;gt; в режиме standalone — он поднимает собственный HTTP-сервер на порту 80 на время проверки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl https://get.acme.sh | sh -s email=your-email@example.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сертификат для NaïveProxy-входа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --issue -d YOUR_ENTRY_DOMAIN --standalone --keylength ec-256 --server letsencrypt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Установка с хуком, который перезапустит панель после каждого продления:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --install-cert -d YOUR_ENTRY_DOMAIN --ecc \&lt;br /&gt;
  --fullchain-file /root/cert/YOUR_ENTRY_DOMAIN/fullchain.pem \&lt;br /&gt;
  --key-file /root/cert/YOUR_ENTRY_DOMAIN/privkey.pem \&lt;br /&gt;
  --reloadcmd &amp;quot;systemctl restart s-ui&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же самое для домена панели:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --issue -d YOUR_PANEL_DOMAIN --standalone --keylength ec-256 --server letsencrypt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --install-cert -d YOUR_PANEL_DOMAIN --ecc \&lt;br /&gt;
  --fullchain-file /root/cert/YOUR_PANEL_DOMAIN/fullchain.pem \&lt;br /&gt;
  --key-file /root/cert/YOUR_PANEL_DOMAIN/privkey.pem \&lt;br /&gt;
  --reloadcmd &amp;quot;systemctl restart s-ui&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить, что автопродление зарегистрировано:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --list&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
crontab -l | grep acme&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Про порт 80 — не пропускайте.&#039;&#039;&#039; В standalone-режиме (&amp;lt;code&amp;gt;Le_Webroot=&#039;no&#039;&amp;lt;/code&amp;gt; в конфигурации домена) acme.sh поднимает свой сервер на 80-м порту. Если порт занят — например, тем же Caddy — продление &#039;&#039;&#039;молча провалится&#039;&#039;&#039;, и вы узнаете об этом, когда сертификат истечёт. Реальный случай из эксплуатации разобран в разделе [[#Диагностика|Диагностика]].&lt;br /&gt;
&lt;br /&gt;
Если сертификат уже выпускался другим инструментом и вам нужно лишь дописать перезапуск панели в существующий хук, правьте поле &amp;lt;code&amp;gt;Le_ReloadCmd&amp;lt;/code&amp;gt; прямо в конфигурации домена:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /root/.acme.sh/YOUR_DOMAIN_ecc/YOUR_DOMAIN.conf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Значение хранится в base64 внутри обёртки &amp;lt;code&amp;gt;__ACME_BASE64__START_…__ACME_BASE64__END_&amp;lt;/code&amp;gt;. Такой способ хорош тем, что не запускает reload немедленно — в отличие от повторного &amp;lt;code&amp;gt;--install-cert&amp;lt;/code&amp;gt;, который тут же дёрнет перезапуск и разорвёт активные соединения.&lt;br /&gt;
&lt;br /&gt;
== 2.5. Включение HTTPS для панели и подписок ==&lt;br /&gt;
&lt;br /&gt;
Через интерфейс: &#039;&#039;&#039;Settings&#039;&#039;&#039; → поля сертификата для панели и для подписки. Через API (см. [[#2.9. Настройка через API|2.9]]) — объект &amp;lt;code&amp;gt;settings&amp;lt;/code&amp;gt; с полями:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;webCertFile&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;webKeyFile&amp;lt;/code&amp;gt;&lt;br /&gt;
|Пути к сертификату панели&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;webDomain&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_PANEL_DOMAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;subCertFile&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;subKeyFile&amp;lt;/code&amp;gt;&lt;br /&gt;
|Пути к сертификату подписок&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;subURI&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;https://YOUR_PANEL_DOMAIN:2097/sub/&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После перезапуска панель доступна только по HTTPS:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl restart s-ui&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s -o /dev/null -w &amp;quot;%{http_code}\n&amp;quot; https://YOUR_PANEL_DOMAIN:2095/app/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответ &amp;lt;code&amp;gt;307&amp;lt;/code&amp;gt; (редирект на форму входа) означает, что всё поднялось.&lt;br /&gt;
&lt;br /&gt;
== 2.6. Outbound&#039;ы: плечи каскада ==&lt;br /&gt;
&lt;br /&gt;
Первое, что заводим, — &#039;&#039;&#039;исходящие&#039;&#039;&#039; подключения к выходному серверу. Именно они превращают схему в каскад.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Outbound mieru&#039;&#039;&#039; (раздел &#039;&#039;Outbounds&#039;&#039; → добавить, тип &amp;lt;code&amp;gt;mieru&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tag&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;to-exit-mieru&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;server&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_EXIT_IP&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;server_ports&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;2012-2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;transport&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;ru-bridge&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;YOUR_BRIDGE_PASSWORD&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;multiplexing&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;MULTIPLEXING_HIGH&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Outbound naive&#039;&#039;&#039; (тип &amp;lt;code&amp;gt;naive&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tag&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;to-exit-naive&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;server&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;server_port&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_EXIT_DOMAIN&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;443&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;ru-chain&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;YOUR_CHAIN_PASSWORD&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tls&amp;lt;/code&amp;gt;&lt;br /&gt;
|включён, &amp;lt;code&amp;gt;server_name&amp;lt;/code&amp;gt; = &amp;lt;code&amp;gt;YOUR_EXIT_DOMAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После сохранения в логе должно появиться:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
journalctl -u s-ui -n 20 --no-pager | grep -E &amp;quot;mieru|naive&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
outbound/mieru[to-exit-mieru]mieru client is started&lt;br /&gt;
outbound/naive[to-exit-naive]NaiveProxy started, version: 148.0.7778.96&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Строка с версией NaiveProxy подтверждает, что ядро подняло &#039;&#039;&#039;настоящий Cronet&#039;&#039;&#039; — сетевой стек Chromium, а не самодельную реализацию HTTP/2.&lt;br /&gt;
&lt;br /&gt;
== 2.7. TLS-запись и инбаунды ==&lt;br /&gt;
&lt;br /&gt;
NaïveProxy-инбаунду нужен сертификат. В разделе &#039;&#039;TLS&#039;&#039; создаётся запись:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_ENTRY_DOMAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;server.certificate_path&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;/root/cert/YOUR_ENTRY_DOMAIN/fullchain.pem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;server.key_path&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;/root/cert/YOUR_ENTRY_DOMAIN/privkey.pem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;server.server_name&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;YOUR_ENTRY_DOMAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Теперь &#039;&#039;&#039;инбаунды&#039;&#039;&#039; — точки входа для клиентов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Inbound mieru:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tag&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;mieru-in&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;listen&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;::&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;listen_ports&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;2012-2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;transport&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;TCP&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;traffic_pattern&amp;lt;/code&amp;gt;&lt;br /&gt;
|см. [[#2.8. Traffic pattern для mieru|2.8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Inbound naive:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tag&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;naive-in&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;listen&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;listen_port&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;::&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;443&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tls_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|id созданной TLS-записи&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ядро не запустится, пока нет ни одного пользователя.&#039;&#039;&#039; Сразу после создания mieru-инбаунда в логе появится:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
start sing-box err: initialize inbound[0] mieru-in failed to build mieru server config:&lt;br /&gt;
failed to validate mieru options: users is empty&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это &#039;&#039;&#039;нормально&#039;&#039;&#039;. Пользователи в этой панели хранятся отдельно от инбаундов, поэтому ядро поднимется только после создания первого клиента — см. [[#Этап 3. Пользователи и раздача|Этап 3]]. Пугаться и переделывать инбаунд не нужно.&lt;br /&gt;
&lt;br /&gt;
== 2.8. Traffic pattern для mieru ==&lt;br /&gt;
&lt;br /&gt;
Плечо «клиент → входной сервер» — единственное, которое видит ТСПУ, поэтому здесь имеет смысл включить подгонку статистики (см. теорию выше). В панели &amp;lt;code&amp;gt;traffic_pattern&amp;lt;/code&amp;gt; задаётся &#039;&#039;&#039;строкой base64&#039;&#039;&#039;, а не JSON.&lt;br /&gt;
&lt;br /&gt;
Готовое значение для комбинации «дробление TCP + печатаемый nonce»:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EAAaBAgBEAoiBggBGAYgCA==&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оно соответствует такой конфигурации:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;unlockAll&amp;quot;: false,&lt;br /&gt;
    &amp;quot;tcpFragment&amp;quot;: { &amp;quot;enable&amp;quot;: true, &amp;quot;maxSleepMs&amp;quot;: 10 },&lt;br /&gt;
    &amp;quot;nonce&amp;quot;: { &amp;quot;type&amp;quot;: &amp;quot;NONCE_TYPE_PRINTABLE&amp;quot;, &amp;quot;minLen&amp;quot;: 6, &amp;quot;maxLen&amp;quot;: 8 }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Собственный вариант получается так: поднимите временный контейнер &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt; с нужным &amp;lt;code&amp;gt;trafficPattern&amp;lt;/code&amp;gt; в &amp;lt;code&amp;gt;server.json&amp;lt;/code&amp;gt; и выполните экспорт:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker exec mita-exit mita export traffic-pattern&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить, что строка означает именно то, что задумано:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker exec mita-exit mita explain traffic-pattern &amp;quot;EAAaBAgBEAoiBggBGAYgCA==&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Клиенту знать этот паттерн не нужно — режим диктует сервер.&lt;br /&gt;
&lt;br /&gt;
== 2.9. Правила маршрутизации — собственно каскад ==&lt;br /&gt;
&lt;br /&gt;
Без правил трафик клиентов уйдёт &#039;&#039;&#039;напрямую&#039;&#039;&#039; с входного сервера, и весь смысл каскада потеряется. Нужно связать каждый inbound со своим outbound. В разделе &#039;&#039;Config&#039;&#039; → &amp;lt;code&amp;gt;route.rules&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;route&amp;quot;: {&lt;br /&gt;
    &amp;quot;rules&amp;quot;: [&lt;br /&gt;
      { &amp;quot;action&amp;quot;: &amp;quot;sniff&amp;quot; },&lt;br /&gt;
      { &amp;quot;protocol&amp;quot;: [&amp;quot;dns&amp;quot;], &amp;quot;action&amp;quot;: &amp;quot;hijack-dns&amp;quot; },&lt;br /&gt;
      { &amp;quot;inbound&amp;quot;: [&amp;quot;mieru-in&amp;quot;], &amp;quot;action&amp;quot;: &amp;quot;route&amp;quot;, &amp;quot;outbound&amp;quot;: &amp;quot;to-exit-mieru&amp;quot; },&lt;br /&gt;
      { &amp;quot;inbound&amp;quot;: [&amp;quot;naive-in&amp;quot;], &amp;quot;action&amp;quot;: &amp;quot;route&amp;quot;, &amp;quot;outbound&amp;quot;: &amp;quot;to-exit-naive&amp;quot; }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два последних правила и есть каскад: всё, что пришло на mieru-вход, уходит mieru-плечом; всё, что пришло на naive-вход, — naive-плечом. Протоколы не смешиваются, каждый идёт своей цепочкой до конца.&lt;br /&gt;
&lt;br /&gt;
== 2.10. Настройка через API ==&lt;br /&gt;
&lt;br /&gt;
Всё вышеописанное делается и без веб-интерфейса — полезно для автоматизации и повторного развёртывания.&lt;br /&gt;
&lt;br /&gt;
Авторизация принимает &#039;&#039;&#039;только&#039;&#039;&#039; form-encoded поля &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;pass&amp;lt;/code&amp;gt; (JSON и пары &amp;lt;code&amp;gt;username/password&amp;lt;/code&amp;gt; не работают):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sk -c /tmp/sui.jar -X POST &amp;quot;https://YOUR_PANEL_DOMAIN:2095/app/api/login&amp;quot; \&lt;br /&gt;
     -d &amp;quot;user=YOUR_ADMIN&amp;amp;pass=YOUR_ADMIN_PASSWORD&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Любой изменяющий запрос требует CSRF-токен:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
CSRF=$(curl -sk -b /tmp/sui.jar -c /tmp/sui.jar &amp;quot;https://YOUR_PANEL_DOMAIN:2095/app/api/csrf&amp;quot; \&lt;br /&gt;
       | sed -E &#039;s/.*&amp;quot;token&amp;quot;:&amp;quot;([^&amp;quot;]+)&amp;quot;.*/\1/&#039;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранение объектов — единый эндпоинт &amp;lt;code&amp;gt;/app/api/save&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sk -b /tmp/sui.jar -X POST &amp;quot;https://YOUR_PANEL_DOMAIN:2095/app/api/save&amp;quot; \&lt;br /&gt;
  -H &amp;quot;X-CSRF-Token: $CSRF&amp;quot; \&lt;br /&gt;
  --data-urlencode &amp;quot;object=outbounds&amp;quot; \&lt;br /&gt;
  --data-urlencode &amp;quot;action=new&amp;quot; \&lt;br /&gt;
  --data-urlencode &#039;data={&amp;quot;type&amp;quot;:&amp;quot;mieru&amp;quot;,&amp;quot;tag&amp;quot;:&amp;quot;to-exit-mieru&amp;quot;,&amp;quot;server&amp;quot;:&amp;quot;YOUR_EXIT_IP&amp;quot;,&amp;quot;server_ports&amp;quot;:[&amp;quot;2012-2022&amp;quot;],&amp;quot;transport&amp;quot;:&amp;quot;TCP&amp;quot;,&amp;quot;username&amp;quot;:&amp;quot;ru-bridge&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;YOUR_BRIDGE_PASSWORD&amp;quot;,&amp;quot;multiplexing&amp;quot;:&amp;quot;MULTIPLEXING_HIGH&amp;quot;}&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значения&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;object&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;inbounds&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;outbounds&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;clients&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;settings&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;action&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;new&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;edit&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;del&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
|JSON объекта; при &amp;lt;code&amp;gt;edit&amp;lt;/code&amp;gt; обязательно поле &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Чтение — GET-эндпоинты &amp;lt;code&amp;gt;/app/api/{load,inbounds,outbounds,clients,tls,config,settings,stats,onlines}&amp;lt;/code&amp;gt;.&lt;br /&gt;
----&lt;br /&gt;
= Этап 3. Пользователи и раздача =&lt;br /&gt;
&lt;br /&gt;
== 3.1. Модель: один клиент — оба протокола ==&lt;br /&gt;
&lt;br /&gt;
В панели &#039;&#039;&#039;пользователь и инбаунд разделены&#039;&#039;&#039;. Клиент — это самостоятельная запись с набором учётных данных по протоколам, привязанная к одному или нескольким инбаундам. Отсюда удобное свойство: одна запись выдаёт человеку &#039;&#039;&#039;сразу оба канала&#039;&#039;&#039; — mieru и NaïveProxy — с общим лимитом трафика, общим сроком и одной подпиской.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле клиента&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя (оно же логин mieru)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;config.mieru&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;{&amp;quot;name&amp;quot;: …, &amp;quot;password&amp;quot;: …}&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;config.naive&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;{&amp;quot;username&amp;quot;: …, &amp;quot;password&amp;quot;: …}&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;inbounds&amp;lt;/code&amp;gt;&lt;br /&gt;
|Список id инбаундов, к которым привязан клиент&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt;&lt;br /&gt;
|Лимит трафика в байтах, &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; — без лимита&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;expiry&amp;lt;/code&amp;gt;&lt;br /&gt;
|Срок действия (unix-время), &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; — бессрочно&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;subSecret&amp;lt;/code&amp;gt;&lt;br /&gt;
|UUID подписки, генерируется автоматически&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Mieru-multi-user.png|центр|958x958px|Многопользовательский режим: один сервер обслуживает несколько учёток одновременно]]&lt;br /&gt;
&lt;br /&gt;
Один сервер обслуживает неограниченное число учёток — отдельный контейнер на каждого пользователя, как в схемах «один сервер — один клиент», здесь не нужен.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание на разные имена полей: у mieru — &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, у naive — &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;. Если ошибиться, пользователь просто не попадёт в конфигурацию соответствующего инбаунда: панель пропустит запись без нужного поля, а протокол останется без учёток.&lt;br /&gt;
&lt;br /&gt;
== 3.2. Создание клиента ==&lt;br /&gt;
&lt;br /&gt;
В интерфейсе: &#039;&#039;Clients&#039;&#039; → добавить, задать имя, отметить оба инбаунда, при необходимости выставить лимит и срок.&lt;br /&gt;
&lt;br /&gt;
Через API:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sk -b /tmp/sui.jar -X POST &amp;quot;https://YOUR_PANEL_DOMAIN:2095/app/api/save&amp;quot; \&lt;br /&gt;
  -H &amp;quot;X-CSRF-Token: $CSRF&amp;quot; \&lt;br /&gt;
  --data-urlencode &amp;quot;object=clients&amp;quot; \&lt;br /&gt;
  --data-urlencode &amp;quot;action=new&amp;quot; \&lt;br /&gt;
  --data-urlencode &#039;data={&amp;quot;enable&amp;quot;:true,&amp;quot;name&amp;quot;:&amp;quot;user01&amp;quot;,&amp;quot;config&amp;quot;:{&amp;quot;mieru&amp;quot;:{&amp;quot;name&amp;quot;:&amp;quot;user01&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;MIERU_PASSWORD&amp;quot;},&amp;quot;naive&amp;quot;:{&amp;quot;username&amp;quot;:&amp;quot;user01&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;NAIVE_PASSWORD&amp;quot;}},&amp;quot;inbounds&amp;quot;:[1,2],&amp;quot;volume&amp;quot;:0,&amp;quot;expiry&amp;quot;:0,&amp;quot;desc&amp;quot;:&amp;quot;первый клиент&amp;quot;}&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сразу после создания первого клиента ядро наконец поднимется полностью:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
journalctl -u s-ui -n 15 --no-pager | grep started&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
inbound/mieru[mieru-in]mieru server is started&lt;br /&gt;
inbound/naive[naive-in]tcp server started at [::]:443&lt;br /&gt;
inbound/naive[naive-in]udp server started at [::]:443&lt;br /&gt;
sing-box started&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить занятые порты:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ss -tlnp | grep -E &amp;quot;:(443|201[2-9]|202[0-2]) &amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.3. Подписка ==&lt;br /&gt;
&lt;br /&gt;
У каждого клиента свой адрес подписки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://YOUR_PANEL_DOMAIN:2097/sub/&amp;lt;subSecret&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Форматы отдачи:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Запрос&lt;br /&gt;
!Что отдаёт&lt;br /&gt;
!Кому годится&lt;br /&gt;
|-&lt;br /&gt;
|без параметров&lt;br /&gt;
|base64 со списком ссылок (&amp;lt;code&amp;gt;mierus://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;http2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|NekoBox, Karing и подобные&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;?format=json&amp;lt;/code&amp;gt;&lt;br /&gt;
|Готовый конфиг sing-box с обоими outbound&#039;ами, группами &amp;lt;code&amp;gt;auto&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;proxy&amp;lt;/code&amp;gt;&lt;br /&gt;
|Клиенты на sing-box&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;?format=clash&amp;lt;/code&amp;gt;&lt;br /&gt;
|&#039;&#039;&#039;Бесполезен&#039;&#039;&#039;&lt;br /&gt;
|—&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Про Clash-формат.&#039;&#039;&#039; Он отдаёт пустой &amp;lt;code&amp;gt;proxies: []&amp;lt;/code&amp;gt;, потому что Clash и Mihomo не знают ни mieru, ни naive в том виде, в каком их отдаёт панель. Не тратьте на него время — раздавайте base64-ссылки или JSON.&lt;br /&gt;
&lt;br /&gt;
Проверка, что подписка жива:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s -o /dev/null -w &amp;quot;%{http_code}\n&amp;quot; &amp;quot;https://YOUR_PANEL_DOMAIN:2097/sub/SUBSECRET&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответ &amp;lt;code&amp;gt;200&amp;lt;/code&amp;gt; — всё в порядке.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Отдельно про кнопку проверки в интерфейсе.&#039;&#039;&#039; В окне выдачи конфигов («Delivery») кнопка проверки подписки может показывать &#039;&#039;Subscription URL test failed&#039;&#039;, хотя подписка полностью работоспособна. Причина — политика браузера: панель живёт на порту 2095, подписки на 2097, для браузера это &#039;&#039;&#039;разные источники&#039;&#039;&#039;, а сервер подписок не отдаёт заголовок &amp;lt;code&amp;gt;Access-Control-Allow-Origin&amp;lt;/code&amp;gt;. Браузер отправляет запрос, но не даёт скрипту прочитать ответ — панель честно пишет об этом мелким шрифтом («HTTP success cannot be confirmed»). Клиентов это не касается: они ходят за подпиской напрямую, без ограничений CORS. Проверять доступность надо командой &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; выше, а не кнопкой.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Какие ссылки отдавать клиенту ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mieru&#039;&#039;&#039; — из подписки берётся штатно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mierus://user01:MIERU_PASSWORD@YOUR_ENTRY_DOMAIN?port=2012-2022&amp;amp;profile=mieru-in&amp;amp;protocol=TCP&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; — здесь есть нюанс. Панель генерирует ссылку в схеме &amp;lt;code&amp;gt;http2://&amp;lt;/code&amp;gt; с base64-частью, и &#039;&#039;&#039;NekoBox её не импортирует&#039;&#039;&#039; (схема жёстко зашита в генераторе, настройки нет). Рабочий формат для NekoBox и NekoRay нужно собрать вручную:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
naive+https://user01:NAIVE_PASSWORD@YOUR_ENTRY_DOMAIN:443#naive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сама панель такую ссылку понимает — можно проверить её конвертером:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sk -b /tmp/sui.jar -X POST &amp;quot;https://YOUR_PANEL_DOMAIN:2095/app/api/linkConvert&amp;quot; \&lt;br /&gt;
  -H &amp;quot;X-CSRF-Token: $CSRF&amp;quot; \&lt;br /&gt;
  --data-urlencode &amp;quot;link=naive+https://user01:NAIVE_PASSWORD@YOUR_ENTRY_DOMAIN:443#naive&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответ — валидный naive-outbound с полями &amp;lt;code&amp;gt;server&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tls&amp;lt;/code&amp;gt;.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Клиентские приложения =&lt;br /&gt;
&lt;br /&gt;
== 4.1. Десктоп ==&lt;br /&gt;
&lt;br /&gt;
Для &#039;&#039;&#039;mieru&#039;&#039;&#039; — Clash Verge Rev, Mihomo Party, NekoBox. Профиль дополняется блоком:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
proxies:&lt;br /&gt;
  - name: mieru-entry&lt;br /&gt;
    type: mieru&lt;br /&gt;
    server: YOUR_ENTRY_DOMAIN&lt;br /&gt;
    port: 2022&lt;br /&gt;
    username: user01&lt;br /&gt;
    password: MIERU_PASSWORD&lt;br /&gt;
    multiplexing: MULTIPLEXING_HIGH&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;port: 2022&amp;lt;/code&amp;gt; — &#039;&#039;&#039;одно&#039;&#039;&#039; число из диапазона &amp;lt;code&amp;gt;2012-2022&amp;lt;/code&amp;gt;, не диапазон: сервер слушает все 11 портов параллельно, клиент занимает один. Многие сборки mihomo не разбирают диапазон и молча ломаются.&lt;br /&gt;
&lt;br /&gt;
Для &#039;&#039;&#039;NaïveProxy&#039;&#039;&#039; — официальный бинарь &amp;lt;code&amp;gt;naive&amp;lt;/code&amp;gt;. Конфигурация:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;listen&amp;quot;: &amp;quot;socks://127.0.0.1:1080&amp;quot;,&lt;br /&gt;
  &amp;quot;proxy&amp;quot;: &amp;quot;https://user01:NAIVE_PASSWORD@YOUR_ENTRY_DOMAIN&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
naive ./config.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После запуска на &amp;lt;code&amp;gt;127.0.0.1:1080&amp;lt;/code&amp;gt; появляется SOCKS5, который и указывается в браузере или системе.&lt;br /&gt;
&lt;br /&gt;
== 4.2. Android ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Протокол&lt;br /&gt;
!Как подключается&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;mieru&#039;&#039;&#039;&lt;br /&gt;
|Импортируется штатно из подписки или ссылкой &amp;lt;code&amp;gt;mierus://&amp;lt;/code&amp;gt;. Работает в NekoBox, Karing, husi&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;NaïveProxy&#039;&#039;&#039;&lt;br /&gt;
|Требует &#039;&#039;&#039;отдельный плагин&#039;&#039;&#039; (naive-plugin): протоколу нужен Cronet — часть Chromium, которую не кладут в основной APK&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Если плагин не установлен, ссылка не импортируется вовсе — и это не проблема конфигурации сервера. Порядок: сначала плагин, затем импорт ссылки в формате &amp;lt;code&amp;gt;naive+https://&amp;lt;/code&amp;gt; (не &amp;lt;code&amp;gt;http2://&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Практический вывод: &#039;&#039;&#039;на телефонах основным каналом делайте mieru&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.3. OpenWrt-роутер ==&lt;br /&gt;
&lt;br /&gt;
Смысл: роутер держит постоянное подключение, а трафик-менеджер (например, podkop) заворачивает в него нужные домены — тогда туннель работает для всей домашней сети.&lt;br /&gt;
&lt;br /&gt;
=== Вариант с mieru ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh root@YOUR_ROUTER_HOST&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp &amp;amp;&amp;amp; wget -q https://github.com/enfein/mieru/releases/download/v3.32.0/mieru_3.32.0_linux_arm64.tar.gz -O mieru.tar.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tar xzf mieru.tar.gz &amp;amp;&amp;amp; mv mieru /usr/bin/mieru &amp;amp;&amp;amp; chmod +x /usr/bin/mieru &amp;amp;&amp;amp; rm -f /tmp/mieru.tar.gz /tmp/LICENSE /tmp/README.md&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Архитектура большинства современных моделей — &amp;lt;code&amp;gt;aarch64&amp;lt;/code&amp;gt;; для других (&amp;lt;code&amp;gt;mipsel&amp;lt;/code&amp;gt; и т.п.) возьмите соответствующий артефакт из релизов.&lt;br /&gt;
&lt;br /&gt;
Конфигурация &amp;lt;code&amp;gt;/etc/mieru_client_config.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;profiles&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;profileName&amp;quot;: &amp;quot;default&amp;quot;,&lt;br /&gt;
            &amp;quot;user&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;user01&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;MIERU_PASSWORD&amp;quot; },&lt;br /&gt;
            &amp;quot;servers&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;ipAddress&amp;quot;: &amp;quot;YOUR_ENTRY_IP&amp;quot;,&lt;br /&gt;
                    &amp;quot;portBindings&amp;quot;: [ { &amp;quot;portRange&amp;quot;: &amp;quot;2012-2022&amp;quot;, &amp;quot;protocol&amp;quot;: &amp;quot;TCP&amp;quot; } ]&lt;br /&gt;
                }&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;mtu&amp;quot;: 1380,&lt;br /&gt;
            &amp;quot;multiplexing&amp;quot;: { &amp;quot;level&amp;quot;: &amp;quot;MULTIPLEXING_HIGH&amp;quot; }&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;activeProfile&amp;quot;: &amp;quot;default&amp;quot;,&lt;br /&gt;
    &amp;quot;rpcPort&amp;quot;: 8964,&lt;br /&gt;
    &amp;quot;socks5Port&amp;quot;: 1090,&lt;br /&gt;
    &amp;quot;loggingLevel&amp;quot;: &amp;quot;INFO&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;На роутере не используйте &amp;lt;code&amp;gt;mieru apply config&amp;lt;/code&amp;gt;.&#039;&#039;&#039; Клиент читает JSON напрямую через переменную &amp;lt;code&amp;gt;MIERU_CONFIG_JSON_FILE&amp;lt;/code&amp;gt;, выставленную в init-скрипте. Если применить конфигурацию через RPC, состояние разъедется между JSON-файлом и persistent-хранилищем.&lt;br /&gt;
&lt;br /&gt;
=== Вариант с NaïveProxy ===&lt;br /&gt;
&lt;br /&gt;
Конфигурация &amp;lt;code&amp;gt;/etc/naive/config.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;listen&amp;quot;: &amp;quot;socks://127.0.0.1:1080&amp;quot;,&lt;br /&gt;
  &amp;quot;proxy&amp;quot;: &amp;quot;https://user01:NAIVE_PASSWORD@YOUR_ENTRY_DOMAIN&amp;quot;,&lt;br /&gt;
  &amp;quot;log&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Init-скрипт procd &amp;lt;code&amp;gt;/etc/init.d/naive&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/sh /etc/rc.common&lt;br /&gt;
&lt;br /&gt;
START=95&lt;br /&gt;
STOP=01&lt;br /&gt;
USE_PROCD=1&lt;br /&gt;
&lt;br /&gt;
start_service() {&lt;br /&gt;
    procd_open_instance&lt;br /&gt;
    procd_set_param command /usr/bin/naive /etc/naive/config.json&lt;br /&gt;
    procd_set_param respawn&lt;br /&gt;
    procd_set_param stdout 1&lt;br /&gt;
    procd_set_param stderr 1&lt;br /&gt;
    procd_close_instance&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod +x /etc/init.d/naive &amp;amp;&amp;amp; /etc/init.d/naive enable &amp;amp;&amp;amp; /etc/init.d/naive start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;START=95&amp;lt;/code&amp;gt; — запуск после поднятия сети.&lt;br /&gt;
&lt;br /&gt;
=== Подключение podkop ===&lt;br /&gt;
&lt;br /&gt;
podkop &#039;&#039;&#039;не принимает&#039;&#039;&#039; ни &amp;lt;code&amp;gt;mierus://&amp;lt;/code&amp;gt;, ни &amp;lt;code&amp;gt;naive+https://&amp;lt;/code&amp;gt; напрямую — он понимает только vless/ss/trojan/socks/hy2. Поэтому на роутере поднимается standalone-клиент с локальным SOCKS5, а в podkop указывается уже он:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
socks5://127.0.0.1:1090     — для mieru&lt;br /&gt;
socks5://127.0.0.1:1080     — для naive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверка с роутера:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s --max-time 20 -x socks5h://127.0.0.1:1090 https://ifconfig.me&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Должен вернуться IP &#039;&#039;&#039;выходного&#039;&#039;&#039; сервера (или его egress), а не роутера и не входного сервера.&lt;br /&gt;
----&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  КЛИЕНТЫ                ВХОДНОЙ СЕРВЕР                     ВЫХОДНОЙ СЕРВЕР&lt;br /&gt;
                    (панель s-ui-x, нативно)              (Docker, без панели)&lt;br /&gt;
&lt;br /&gt;
  mieru ──2012-2022──▶ inbound mieru ──┐&lt;br /&gt;
  (mierus://)          traffic_pattern │&lt;br /&gt;
                                       ├─▶ outbound mieru ──2012-2022──▶ mita&lt;br /&gt;
                                       │   (ru-bridge)                    │&lt;br /&gt;
  naive ─── :443 ────▶ inbound naive ──┤                                  ▼&lt;br /&gt;
  (naive+https://)     TLS + padding   │                            egress SOCKS5&lt;br /&gt;
                                       └─▶ outbound naive ─── :443 ──▶ Caddy&lt;br /&gt;
                                           (ru-chain)                     │&lt;br /&gt;
                       панель  :2095                                      ▼&lt;br /&gt;
                       подписки :2097                              WARP / прямой выход&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Та же цепочка для NaïveProxy в виде схемы:&lt;br /&gt;
&lt;br /&gt;
[[Файл:Generated image NaïveProxy Полная схема прохождения трафика.png|безрамки|739x739пкс]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Участок&lt;br /&gt;
!Протокол&lt;br /&gt;
!Что видит наблюдатель&lt;br /&gt;
|-&lt;br /&gt;
|Клиент → входной сервер (mieru)&lt;br /&gt;
|mieru поверх TCP, без TLS&lt;br /&gt;
|Случайный либо текстоподобный поток без опознаваемых признаков&lt;br /&gt;
|-&lt;br /&gt;
|Клиент → входной сервер (naive)&lt;br /&gt;
|HTTPS + HTTP/2 CONNECT&lt;br /&gt;
|Chrome, открывающий обычный сайт&lt;br /&gt;
|-&lt;br /&gt;
|Входной → выходной (mieru)&lt;br /&gt;
|mieru поверх TCP&lt;br /&gt;
|Трафик между двумя серверами&lt;br /&gt;
|-&lt;br /&gt;
|Входной → выходной (naive)&lt;br /&gt;
|HTTPS + HTTP/2 CONNECT&lt;br /&gt;
|То же&lt;br /&gt;
|-&lt;br /&gt;
|Выходной → интернет&lt;br /&gt;
|через egress (WARP) либо напрямую&lt;br /&gt;
|IP выходного узла или Cloudflare&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Клиентское ПО &#039;&#039;&#039;никогда&#039;&#039;&#039; не видит адрес выходного сервера: в конфигурации клиента фигурирует только входной.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
&lt;br /&gt;
== Добавление пользователя ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Clients&#039;&#039; → добавить, отметить оба инбаунда, выдать ссылку подписки. Перезапускать ничего не нужно — панель применяет изменения к работающему ядру.&lt;br /&gt;
&lt;br /&gt;
== Отзыв доступа ==&lt;br /&gt;
&lt;br /&gt;
Снять галочку &amp;lt;code&amp;gt;enable&amp;lt;/code&amp;gt; или удалить клиента. Учётка исчезает из конфигурации инбаундов, активные сессии рвутся при следующем применении.&lt;br /&gt;
&lt;br /&gt;
== Лимиты и сроки ==&lt;br /&gt;
&lt;br /&gt;
Поля &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; (байты) и &amp;lt;code&amp;gt;expiry&amp;lt;/code&amp;gt; (unix-время) у клиента. По исчерпании панель сама отключает доступ. Учёт ведётся &#039;&#039;&#039;на входе&#039;&#039;&#039;, поэтому работает и для NaïveProxy, который сам по себе трафик не считает.&lt;br /&gt;
&lt;br /&gt;
== Просмотр трафика ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sk -b /tmp/sui.jar &amp;quot;https://YOUR_PANEL_DOMAIN:2095/app/api/clients&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поля &amp;lt;code&amp;gt;up&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;down&amp;lt;/code&amp;gt; — байты. В интерфейсе то же самое видно в списке клиентов.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
s-ui update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На выходном сервере (смена версии mieru — в &amp;lt;code&amp;gt;docker-compose.yml&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /opt/mieru-exit &amp;amp;&amp;amp; docker compose up -d --build&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /opt/caddy-naive &amp;amp;&amp;amp; docker compose up -d --build&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Логи ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
journalctl -u s-ui -f                      # входной сервер: панель и ядро&lt;br /&gt;
docker logs mita-exit --tail 50            # выходной: mieru&lt;br /&gt;
docker logs caddy-naive --tail 50          # выходной: naive&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Резервная копия ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tar czf /root/sui-backup-$(date +%F).tar.gz /usr/local/s-ui/db /root/cert&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
База панели — SQLite в &amp;lt;code&amp;gt;/usr/local/s-ui/db/&amp;lt;/code&amp;gt;. В ней клиенты, инбаунды, правила; вместе с сертификатами этого достаточно для восстановления.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
&lt;br /&gt;
== Проверка цепочек ==&lt;br /&gt;
&lt;br /&gt;
Единственная надёжная проверка — &#039;&#039;&#039;пропустить реальный трафик&#039;&#039;&#039; и посмотреть, какой IP видит внешний сервис. Ожидается IP выходного сервера либо его egress.&lt;br /&gt;
&lt;br /&gt;
NaïveProxy (запустить локальный клиент, затем):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s --max-time 25 -x socks5h://127.0.0.1:1080 https://ifconfig.me&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mieru:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s --max-time 30 -x socks5h://127.0.0.1:1090 https://ifconfig.me&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
&lt;br /&gt;
=== Панель циклически перезапускается: &amp;lt;code&amp;gt;bind: address already in use&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Порт подписок (по умолчанию 2096) занят другим сервисом — чаще всего подписками 3x-ui. Сменить:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl stop s-ui &amp;amp;&amp;amp; /usr/local/s-ui/sui setting -subPort 2097 &amp;amp;&amp;amp; systemctl start s-ui&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ядро не стартует: &amp;lt;code&amp;gt;failed to validate mieru options: users is empty&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
У mieru-инбаунда нет ни одного пользователя. Пользователи в панели хранятся отдельно, поэтому &#039;&#039;&#039;создайте клиента&#039;&#039;&#039; и привяжите его к инбаунду — ядро поднимется само. Инбаунд переделывать не нужно.&lt;br /&gt;
&lt;br /&gt;
=== NaïveProxy: обрыв соединения, в логе &amp;lt;code&amp;gt;missing naive padding&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Так и должно быть. Инбаунд требует &#039;&#039;&#039;настоящий&#039;&#039;&#039; протокол NaïveProxy с padding-заголовком, поэтому обычная проверка вида&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -x https://user:pass@YOUR_ENTRY_DOMAIN:443 https://ifconfig.me&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
будет отброшена: curl шлёт обычный HTTP/1.1 CONNECT. Это &#039;&#039;&#039;не поломка&#039;&#039;&#039;, а строгое соответствие протоколу (Caddy в такой ситуации был мягче и принимал обычный CONNECT). Проверять только настоящим клиентом &amp;lt;code&amp;gt;naive&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Клиент mieru подключается, но трафика нет ===&lt;br /&gt;
&lt;br /&gt;
Проверьте &#039;&#039;&#039;время&#039;&#039;&#039; на клиенте:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
date -u&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Расхождение более 4 минут с сервером ломает выработку ключа, и соединения падают молча. Настройте NTP.&lt;br /&gt;
&lt;br /&gt;
=== Тест mieru показывает пустой ответ ===&lt;br /&gt;
&lt;br /&gt;
mieru-клиенту нужно около &#039;&#039;&#039;15 секунд&#039;&#039;&#039; на старт. Если проверять раньше, соединение ещё не установлено и curl вернёт пустоту — легко принять за поломку. Дайте клиенту подняться и повторите; заодно убедитесь, что SOCKS слушает:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ss -tlnp | grep 1090&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Трафик уходит, но выходной IP — входного сервера ===&lt;br /&gt;
&lt;br /&gt;
Не работают правила маршрутизации: трафик уходит &amp;lt;code&amp;gt;direct&amp;lt;/code&amp;gt; вместо каскадного outbound. Проверьте секцию &amp;lt;code&amp;gt;route.rules&amp;lt;/code&amp;gt; — правила &amp;lt;code&amp;gt;inbound → outbound&amp;lt;/code&amp;gt; должны существовать и стоять &#039;&#039;&#039;после&#039;&#039;&#039; &amp;lt;code&amp;gt;sniff&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;hijack-dns&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== NaïveProxy: &amp;lt;code&amp;gt;308 Redirect&amp;lt;/code&amp;gt; вместо авторизации ===&lt;br /&gt;
&lt;br /&gt;
Пароль не совпал, а &amp;lt;code&amp;gt;probe_resistance&amp;lt;/code&amp;gt; маскирует это под обычный редирект. Частая причина — &#039;&#039;&#039;bcrypt-хэш&#039;&#039;&#039; в &amp;lt;code&amp;gt;basic_auth&amp;lt;/code&amp;gt; на выходном сервере: плагин &amp;lt;code&amp;gt;klzgrad/forwardproxy&amp;lt;/code&amp;gt; понимает только plaintext и сравнивает строку буквально.&lt;br /&gt;
&lt;br /&gt;
=== mita в состоянии &amp;lt;code&amp;gt;IDLE&amp;lt;/code&amp;gt;, порты не слушаются ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;mita run&amp;lt;/code&amp;gt; не открывает сокеты сам по себе:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker exec mita-exit mita start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;FATAL: getUid(&amp;quot;mita&amp;quot;) failed&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
В образе нет системных пользователей. Проверьте наличие строк &amp;lt;code&amp;gt;adduser -H -D -g &amp;quot;&amp;quot; mita&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;adduser -H -D -g &amp;quot;&amp;quot; mieru&amp;lt;/code&amp;gt; в Dockerfile и пересоберите образ.&lt;br /&gt;
&lt;br /&gt;
=== Сертификат не продлился, хотя acme.sh в cron ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Самая коварная из проблем&#039;&#039;&#039; — она молчаливая. В standalone-режиме acme.sh поднимает собственный сервер на порту 80; если порт занят (веб-сервером, Caddy, чем угодно), проверка не проходит, и продление тихо срывается. Обнаруживается уже по истёкшему сертификату.&lt;br /&gt;
&lt;br /&gt;
Проверить режим:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
grep Le_Webroot /root/.acme.sh/YOUR_DOMAIN_ecc/YOUR_DOMAIN.conf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Le_Webroot=&#039;no&#039;&amp;lt;/code&amp;gt; означает standalone. Убедитесь, что порт свободен:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ss -tlnp | grep &amp;quot;:80 &amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ручной прогон, показывающий реальную причину:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --cron --home /root/.acme.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Даты и расписание:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --list&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Кнопка проверки подписки показывает ошибку ===&lt;br /&gt;
&lt;br /&gt;
Ограничение CORS в браузере, а не поломка. Подробности — в разделе [[#3.3. Подписка|3.3]]. Проверяйте &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;-ом.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка =&lt;br /&gt;
&lt;br /&gt;
== Входной сервер ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status s-ui                    # состояние панели и ядра&lt;br /&gt;
systemctl restart s-ui                   # перезапуск&lt;br /&gt;
journalctl -u s-ui -f                    # логи&lt;br /&gt;
s-ui                                     # интерактивное меню&lt;br /&gt;
/usr/local/s-ui/sui setting -show        # порты и пути&lt;br /&gt;
/usr/local/s-ui/sui admin -username U -password P   # смена учётных данных&lt;br /&gt;
ss -tlnp | grep -E &amp;quot;:(443|209[57]|201[2-9]|202[0-2]) &amp;quot;   # занятые порты&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Выходной сервер ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker ps                                        # что запущено&lt;br /&gt;
docker exec mita-exit mita status                # состояние mieru&lt;br /&gt;
docker exec mita-exit mita start                 # поднять из IDLE&lt;br /&gt;
docker exec mita-exit mita get users             # пользователи и трафик&lt;br /&gt;
docker logs caddy-naive --tail 50                # логи naive&lt;br /&gt;
cd /opt/mieru-exit &amp;amp;&amp;amp; docker compose up -d --build&lt;br /&gt;
cd /opt/caddy-naive &amp;amp;&amp;amp; docker compose up -d --build&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сертификаты ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/root/.acme.sh/acme.sh --list                    # что ведётся и когда продление&lt;br /&gt;
/root/.acme.sh/acme.sh --cron --home /root/.acme.sh   # ручной прогон&lt;br /&gt;
openssl x509 -in /root/cert/DOMAIN/fullchain.pem -noout -dates&lt;br /&gt;
ss -tlnp | grep &amp;quot;:80 &amp;quot;                           # порт 80 обязан быть свободен&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка цепочек ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s --max-time 30 -x socks5h://127.0.0.1:1090 https://ifconfig.me   # mieru&lt;br /&gt;
curl -s --max-time 25 -x socks5h://127.0.0.1:1080 https://ifconfig.me   # naive&lt;br /&gt;
curl -s -o /dev/null -w &amp;quot;%{http_code}\n&amp;quot; &amp;quot;https://YOUR_PANEL_DOMAIN:2097/sub/SUBSECRET&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== API панели ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# вход (только form-encoded user/pass!)&lt;br /&gt;
curl -sk -c /tmp/sui.jar -X POST &amp;quot;https://PANEL:2095/app/api/login&amp;quot; -d &amp;quot;user=U&amp;amp;pass=P&amp;quot;&lt;br /&gt;
# CSRF-токен для любых изменений&lt;br /&gt;
CSRF=$(curl -sk -b /tmp/sui.jar -c /tmp/sui.jar &amp;quot;https://PANEL:2095/app/api/csrf&amp;quot; \&lt;br /&gt;
       | sed -E &#039;s/.*&amp;quot;token&amp;quot;:&amp;quot;([^&amp;quot;]+)&amp;quot;.*/\1/&#039;)&lt;br /&gt;
# чтение&lt;br /&gt;
curl -sk -b /tmp/sui.jar &amp;quot;https://PANEL:2095/app/api/load&amp;quot;&lt;br /&gt;
curl -sk -b /tmp/sui.jar &amp;quot;https://PANEL:2095/app/api/clients&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
= Приложение А. Ручная установка без панели =&lt;br /&gt;
&lt;br /&gt;
Ниже — прежний способ развернуть &#039;&#039;&#039;входное&#039;&#039;&#039; плечо: mieru и NaïveProxy отдельными контейнерами, с правкой конфигураций руками. Материал перенесён из исходных руководств без изменений и остаётся полезным в трёх случаях:&lt;br /&gt;
&lt;br /&gt;
* нужно поднять входное плечо &#039;&#039;&#039;без панели&#039;&#039;&#039; (минимум зависимостей, всё в файлах);&lt;br /&gt;
* нужно понять, что именно панель делает под капотом;&lt;br /&gt;
* панель по каким-то причинам недоступна, а канал нужен здесь и сейчас.&lt;br /&gt;
&lt;br /&gt;
Терминология исходных текстов: &#039;&#039;&#039;frontend&#039;&#039;&#039; — входной сервер (в основной части статьи), &#039;&#039;&#039;backend&#039;&#039;&#039; — выходной. Учётки клиентов в этом варианте живут в &amp;lt;code&amp;gt;server.json&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;Caddyfile&amp;lt;/code&amp;gt;, учёта трафика у NaïveProxy нет, подписок нет — всё это как раз и появляется вместе с панелью.&lt;br /&gt;
&lt;br /&gt;
== А.1. mieru: ручная установка входного узла ==&lt;br /&gt;
&lt;br /&gt;
=== Установка frontend-сервера (точка входа) ===&lt;br /&gt;
Все команды этого раздела выполняются на frontend-сервере по SSH.&lt;br /&gt;
&lt;br /&gt;
==== Шаг 1. Подключение и подготовка ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh root@YOUR_FRONTEND_HOST&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p /opt/mieru-frontend &amp;amp;&amp;amp; cd /opt/mieru-frontend&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Шаг 2. Dockerfile и entrypoint (одинаковые с backend) ====&lt;br /&gt;
Создайте оба файла так же, как на backend-сервере (см. шаги 4–5 раздела «Установка backend-сервера»). Содержимое идентично — образ универсальный, режим работы выбирается переменной окружения.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-frontend/Dockerfile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;(вставьте тот же Dockerfile, что на backend)&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-frontend/docker-entrypoint.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;(вставьте тот же entrypoint)&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod +x /opt/mieru-frontend/docker-entrypoint.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Шаг 3. Генерация пароля первой клиентской учётки ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
openssl rand -base64 24 | tr -d &#039;=/+&#039; | head -c 31&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Скопируйте результат и впишите в таблицу как &amp;lt;code&amp;gt;YOUR_CLIENT_PASSWORD&amp;lt;/code&amp;gt;. Это пароль учётки &amp;lt;code&amp;gt;client01&amp;lt;/code&amp;gt;, который вы потом отдадите своему первому клиентскому устройству.&lt;br /&gt;
&lt;br /&gt;
==== Шаг 4. Серверный конфиг (mita-frontend) ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-frontend/server.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Замените &amp;lt;code&amp;gt;YOUR_CLIENT_PASSWORD&amp;lt;/code&amp;gt; на значение из шага 3.&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;portBindings&amp;quot;: [&lt;br /&gt;
        {&amp;quot;portRange&amp;quot;: &amp;quot;2012-2022&amp;quot;, &amp;quot;protocol&amp;quot;: &amp;quot;TCP&amp;quot;}&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;users&amp;quot;: [&lt;br /&gt;
        {&amp;quot;name&amp;quot;: &amp;quot;client01&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;YOUR_CLIENT_PASSWORD&amp;quot;}&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;loggingLevel&amp;quot;: &amp;quot;INFO&amp;quot;,&lt;br /&gt;
    &amp;quot;mtu&amp;quot;: 1380,&lt;br /&gt;
    &amp;quot;trafficPattern&amp;quot;: {&lt;br /&gt;
        &amp;quot;unlockAll&amp;quot;: false,&lt;br /&gt;
        &amp;quot;tcpFragment&amp;quot;: {&amp;quot;enable&amp;quot;: true, &amp;quot;maxSleepMs&amp;quot;: 10},&lt;br /&gt;
        &amp;quot;nonce&amp;quot;: {&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;NONCE_TYPE_PRINTABLE&amp;quot;,&lt;br /&gt;
            &amp;quot;minLen&amp;quot;: 6,&lt;br /&gt;
            &amp;quot;maxLen&amp;quot;: 8&lt;br /&gt;
        }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;egress&amp;quot;: {&lt;br /&gt;
        &amp;quot;proxies&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;to-backend&amp;quot;,&lt;br /&gt;
                &amp;quot;protocol&amp;quot;: &amp;quot;SOCKS5_PROXY_PROTOCOL&amp;quot;,&lt;br /&gt;
                &amp;quot;host&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;br /&gt;
                &amp;quot;port&amp;quot;: 1080&lt;br /&gt;
            }&lt;br /&gt;
        ],&lt;br /&gt;
        &amp;quot;rules&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;ipRanges&amp;quot;: [&amp;quot;*&amp;quot;],&lt;br /&gt;
                &amp;quot;domainNames&amp;quot;: [&amp;quot;*&amp;quot;],&lt;br /&gt;
                &amp;quot;action&amp;quot;: &amp;quot;PROXY&amp;quot;,&lt;br /&gt;
                &amp;quot;proxyNames&amp;quot;: [&amp;quot;to-backend&amp;quot;]&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Сохраните и защитите файл.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 /opt/mieru-frontend/server.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Шаг 5. Клиентский конфиг (mieru-client) ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-frontend/client.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Замените &amp;lt;code&amp;gt;YOUR_BACKEND_PUBLIC_IP&amp;lt;/code&amp;gt; на публичный IP backend-сервера, &amp;lt;code&amp;gt;YOUR_BRIDGE_PASSWORD&amp;lt;/code&amp;gt; — на пароль bridge-учётки, который вы создавали на backend в шаге 6.&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;profiles&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;profileName&amp;quot;: &amp;quot;default&amp;quot;,&lt;br /&gt;
            &amp;quot;user&amp;quot;: {&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;frontend-bridge&amp;quot;,&lt;br /&gt;
                &amp;quot;password&amp;quot;: &amp;quot;YOUR_BRIDGE_PASSWORD&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;servers&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;ipAddress&amp;quot;: &amp;quot;YOUR_BACKEND_PUBLIC_IP&amp;quot;,&lt;br /&gt;
                    &amp;quot;portBindings&amp;quot;: [&lt;br /&gt;
                        {&amp;quot;portRange&amp;quot;: &amp;quot;2012-2022&amp;quot;, &amp;quot;protocol&amp;quot;: &amp;quot;TCP&amp;quot;}&lt;br /&gt;
                    ]&lt;br /&gt;
                }&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;mtu&amp;quot;: 1380,&lt;br /&gt;
            &amp;quot;multiplexing&amp;quot;: {&amp;quot;level&amp;quot;: &amp;quot;MULTIPLEXING_HIGH&amp;quot;},&lt;br /&gt;
            &amp;quot;handshakeMode&amp;quot;: &amp;quot;HANDSHAKE_NO_WAIT&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;activeProfile&amp;quot;: &amp;quot;default&amp;quot;,&lt;br /&gt;
    &amp;quot;rpcPort&amp;quot;: 8964,&lt;br /&gt;
    &amp;quot;socks5Port&amp;quot;: 1080,&lt;br /&gt;
    &amp;quot;loggingLevel&amp;quot;: &amp;quot;INFO&amp;quot;,&lt;br /&gt;
    &amp;quot;socks5ListenLAN&amp;quot;: false,&lt;br /&gt;
    &amp;quot;httpProxyListenLAN&amp;quot;: false&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Сохраните и защитите файл.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 /opt/mieru-frontend/client.json&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Шаг 6. docker-compose.yml на frontend ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/mieru-frontend/docker-compose.yml&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Вставьте.&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
services:&lt;br /&gt;
  mieru-client:&lt;br /&gt;
    build:&lt;br /&gt;
      context: .&lt;br /&gt;
      args:&lt;br /&gt;
        MIERU_VERSION: &amp;quot;3.32.0&amp;quot;&lt;br /&gt;
    image: mieru-bundle:3.32.0&lt;br /&gt;
    container_name: mieru-client&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
    network_mode: host&lt;br /&gt;
    environment:&lt;br /&gt;
      MIERU_MODE: mieru&lt;br /&gt;
      MIERU_CONFIG: /srv/client.json&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./client.json:/srv/client.json:ro&lt;br /&gt;
      - mieru-client-state:/etc/mieru&lt;br /&gt;
      - mieru-client-runtime:/var/run/mieru&lt;br /&gt;
      - mieru-client-data:/var/lib/mieru&lt;br /&gt;
    logging:&lt;br /&gt;
      driver: json-file&lt;br /&gt;
      options:&lt;br /&gt;
        max-size: &amp;quot;10m&amp;quot;&lt;br /&gt;
        max-file: &amp;quot;3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  mita:&lt;br /&gt;
    image: mieru-bundle:3.32.0&lt;br /&gt;
    container_name: mita-frontend&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
    network_mode: host&lt;br /&gt;
    depends_on:&lt;br /&gt;
      - mieru-client&lt;br /&gt;
    environment:&lt;br /&gt;
      MIERU_MODE: mita&lt;br /&gt;
      MIERU_CONFIG: /srv/server.json&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./server.json:/srv/server.json:ro&lt;br /&gt;
      - mita-state:/etc/mita&lt;br /&gt;
      - mita-runtime:/var/run/mita&lt;br /&gt;
      - mita-data:/var/lib/mita&lt;br /&gt;
    logging:&lt;br /&gt;
      driver: json-file&lt;br /&gt;
      options:&lt;br /&gt;
        max-size: &amp;quot;10m&amp;quot;&lt;br /&gt;
        max-file: &amp;quot;3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
volumes:&lt;br /&gt;
  mieru-client-state:&lt;br /&gt;
  mieru-client-runtime:&lt;br /&gt;
  mieru-client-data:&lt;br /&gt;
  mita-state:&lt;br /&gt;
  mita-runtime:&lt;br /&gt;
  mita-data:&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Сохраните.&lt;br /&gt;
&lt;br /&gt;
==== Шаг 7. Сборка и запуск ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker compose -f /opt/mieru-frontend/docker-compose.yml build&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker compose -f /opt/mieru-frontend/docker-compose.yml up -d&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 12&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Шаг 8. Проверка ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker logs mieru-client --tail 10&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Должна быть строка &amp;lt;code&amp;gt;mieru: config применён&amp;lt;/code&amp;gt; и затем &amp;lt;code&amp;gt;запуск mieru run&amp;lt;/code&amp;gt;.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker logs mita-frontend --tail 10&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Должно быть &amp;lt;code&amp;gt;RUNNING&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;mita start: OK&amp;lt;/code&amp;gt;.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker exec mieru-client mieru status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ожидание: &amp;lt;code&amp;gt;mieru client is running&amp;lt;/code&amp;gt;.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker exec mita-frontend mita status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ожидание: &amp;lt;code&amp;gt;mita server status is &amp;quot;RUNNING&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Шаг 9. Сквозной тест каскада ====&lt;br /&gt;
С frontend-сервера обратитесь во внешний интернет через локальный SOCKS5 mieru-client. Это эквивалент того, как пойдёт реальный клиентский запрос.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sS --max-time 30 --proxy socks5h://127.0.0.1:1080 https://icanhazip.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ожидание: IP вашего egress-узла. Например, IP Cloudflare WARP, если на backend настроен такой outbound. Команда &#039;&#039;&#039;не должна&#039;&#039;&#039; возвращать публичный IP frontend-сервера.&lt;br /&gt;
&lt;br /&gt;
Контрольный замер прямого IP frontend для сравнения.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -sS --max-time 5 https://icanhazip.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Если первый IP отличается от второго — каскад работает.&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== А.2. NaïveProxy: ручная установка входного узла ==&lt;br /&gt;
&lt;br /&gt;
Здесь входной сервер описан как «RU сервер (Entry Node)»: Caddy принимает клиентов, а отдельный демон &amp;lt;code&amp;gt;naive&amp;lt;/code&amp;gt; держит второе плечо каскада до выходного сервера.&lt;br /&gt;
&lt;br /&gt;
=== Часть 2: Настройка RU сервера (Entry Node) ===&lt;br /&gt;
&lt;br /&gt;
Цель: на RU сервере поднять два компонента:&lt;br /&gt;
# &#039;&#039;&#039;Caddy&#039;&#039;&#039; (Docker) — принимает соединения от клиентов (роутеров), пробрасывает через локальный naive daemon&lt;br /&gt;
# &#039;&#039;&#039;naive daemon&#039;&#039;&#039; (systemd) — клиент, который соединяется с EU Caddy по протоколу NaïveProxy&lt;br /&gt;
&lt;br /&gt;
==== 2.1 Установка Docker ====&lt;br /&gt;
&lt;br /&gt;
Те же команды, что и для EU сервера:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
apt-get update &amp;amp;&amp;amp; apt-get upgrade -y&lt;br /&gt;
apt-get install -y ca-certificates curl gnupg lsb-release&lt;br /&gt;
&lt;br /&gt;
install -m 0755 -d /etc/apt/keyrings&lt;br /&gt;
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \&lt;br /&gt;
  -o /etc/apt/keyrings/docker.asc&lt;br /&gt;
chmod a+r /etc/apt/keyrings/docker.asc&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;deb [arch=$(dpkg --print-architecture) \&lt;br /&gt;
  signed-by=/etc/apt/keyrings/docker.asc] \&lt;br /&gt;
  https://download.docker.com/linux/ubuntu \&lt;br /&gt;
  $(. /etc/os-release &amp;amp;&amp;amp; echo &amp;quot;$VERSION_CODENAME&amp;quot;) stable&amp;quot; \&lt;br /&gt;
  &amp;gt; /etc/apt/sources.list.d/docker.list&lt;br /&gt;
&lt;br /&gt;
apt-get update&lt;br /&gt;
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin&lt;br /&gt;
&lt;br /&gt;
docker --version &amp;amp;&amp;amp; docker compose version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 2.2 Структура каталогов и файлы Docker ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p /opt/caddy-naive/www&lt;br /&gt;
cd /opt/caddy-naive&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dockerfile идентичен EU серверу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/caddy-naive/Dockerfile &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
FROM golang:1.25-alpine AS builder&lt;br /&gt;
RUN apk add --no-cache git&lt;br /&gt;
RUN go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest&lt;br /&gt;
RUN xcaddy build \&lt;br /&gt;
    --with github.com/caddyserver/forwardproxy=github.com/klzgrad/forwardproxy@naive&lt;br /&gt;
&lt;br /&gt;
FROM debian:bookworm-slim&lt;br /&gt;
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y ca-certificates &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;br /&gt;
COPY --from=builder /go/caddy /usr/local/bin/caddy&lt;br /&gt;
EXPOSE 80 443&lt;br /&gt;
CMD [&amp;quot;caddy&amp;quot;, &amp;quot;run&amp;quot;, &amp;quot;--config&amp;quot;, &amp;quot;/etc/caddy/Caddyfile&amp;quot;, &amp;quot;--adapter&amp;quot;, &amp;quot;caddyfile&amp;quot;]&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/caddy-naive/docker-compose.yml &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
services:&lt;br /&gt;
  caddy-naive:&lt;br /&gt;
    build: .&lt;br /&gt;
    container_name: caddy-naive&lt;br /&gt;
    restart: unless-stopped&lt;br /&gt;
    network_mode: host&lt;br /&gt;
    environment:&lt;br /&gt;
      - XDG_DATA_HOME=/data&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./Caddyfile:/etc/caddy/Caddyfile:ro&lt;br /&gt;
      - ./www:/var/www/html:ro&lt;br /&gt;
      - caddy_data:/data&lt;br /&gt;
      - caddy_logs:/var/log/caddy&lt;br /&gt;
&lt;br /&gt;
volumes:&lt;br /&gt;
  caddy_data:&lt;br /&gt;
  caddy_logs:&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/caddy-naive/www/index.html &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Welcome&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;Welcome&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Nothing to see here.&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 2.3 Создание Caddyfile RU сервера ====&lt;br /&gt;
&lt;br /&gt;
Ключевое отличие от EU: директива &amp;lt;code&amp;gt;upstream socks5://127.0.0.1:10808&amp;lt;/code&amp;gt;, которая перенаправляет входящие CONNECT-запросы в локальный naive daemon.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/caddy-naive/Caddyfile &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
{&lt;br /&gt;
  order forward_proxy before file_server&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
:443, your-naive-ru-domain.example.com {&lt;br /&gt;
  tls YOUR_EMAIL@example.com&lt;br /&gt;
  forward_proxy {&lt;br /&gt;
    basic_auth RU_PROXY_USER RU_PROXY_PASSWORD&lt;br /&gt;
    upstream socks5://127.0.0.1:10808&lt;br /&gt;
    hide_ip&lt;br /&gt;
    hide_via&lt;br /&gt;
    probe_resistance&lt;br /&gt;
  }&lt;br /&gt;
  file_server {&lt;br /&gt;
    root /var/www/html&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Почему upstream socks5, а не https?&lt;br /&gt;
: Вариант &amp;lt;code&amp;gt;upstream https://EU_CADDY&amp;lt;/code&amp;gt; не работает в данной схеме — Caddy при таком подходе &amp;quot;протекает&amp;quot; заголовки HTTP-ответа в TLS-туннель, что ломает рукопожатие. Правильное решение — цепочка через SOCKS5 к бинарнику naive, который уже корректно реализует протокол NaïveProxy.&lt;br /&gt;
&lt;br /&gt;
==== 2.4 Сборка и запуск Caddy ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /opt/caddy-naive&lt;br /&gt;
docker compose build&lt;br /&gt;
docker compose up -d&lt;br /&gt;
docker compose logs -f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ожидаемый вывод:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&amp;quot;level&amp;quot;:&amp;quot;info&amp;quot;,&amp;quot;msg&amp;quot;:&amp;quot;certificate obtained successfully&amp;quot;,&amp;quot;identifier&amp;quot;:&amp;quot;your-naive-ru-domain.example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 2.5 Установка naive daemon (клиент для EU) ====&lt;br /&gt;
&lt;br /&gt;
===== 2.5.1 Определение архитектуры сервера =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
uname -m&lt;br /&gt;
# x86_64 → нужен пакет для linux-x86_64-static&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 2.5.2 Скачивание бинарника naive =====&lt;br /&gt;
&lt;br /&gt;
Перейди на страницу релизов: https://github.com/klzgrad/naiveproxy/releases&lt;br /&gt;
&lt;br /&gt;
Найди архив для своей архитектуры. Для Ubuntu x86_64:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
naiveproxy-vXXX-linux-x86_64.tar.xz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Скачать (замени URL на актуальный из Releases)&lt;br /&gt;
cd /tmp&lt;br /&gt;
wget https://github.com/klzgrad/naiveproxy/releases/download/vXXX/naiveproxy-vXXX-linux-x86_64.tar.xz&lt;br /&gt;
&lt;br /&gt;
# Распаковать (xzcat нужен если tar не поддерживает .xz напрямую)&lt;br /&gt;
tar -xf naiveproxy-vXXX-linux-x86_64.tar.xz&lt;br /&gt;
# или&lt;br /&gt;
xzcat naiveproxy-vXXX-linux-x86_64.tar.xz | tar -xf -&lt;br /&gt;
&lt;br /&gt;
# Установить бинарник&lt;br /&gt;
cp naiveproxy-vXXX-linux-x86_64/naive /usr/bin/naive&lt;br /&gt;
chmod +x /usr/bin/naive&lt;br /&gt;
&lt;br /&gt;
# Проверить&lt;br /&gt;
naive --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 2.5.3 Создание конфигурации naive daemon =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p /etc/naive&lt;br /&gt;
&lt;br /&gt;
cat &amp;gt; /etc/naive/config.json &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;listen&amp;quot;: &amp;quot;socks://127.0.0.1:10808&amp;quot;,&lt;br /&gt;
  &amp;quot;proxy&amp;quot;: &amp;quot;https://EU_PROXY_USER:EU_PROXY_PASSWORD@your-naive-eu-domain.example.com&amp;quot;,&lt;br /&gt;
  &amp;quot;log&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Что происходит:&lt;br /&gt;
:* &amp;lt;code&amp;gt;listen&amp;lt;/code&amp;gt; — naive слушает SOCKS5 на localhost:10808 (именно сюда смотрит upstream в Caddyfile RU)&lt;br /&gt;
:* &amp;lt;code&amp;gt;proxy&amp;lt;/code&amp;gt; — подключается к EU Caddy по протоколу NaïveProxy (HTTP/2 CONNECT over TLS, трафик неотличим от обычного Chrome HTTPS)&lt;br /&gt;
&lt;br /&gt;
===== 2.5.4 Создание systemd-сервиса =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /etc/systemd/system/naive.service &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=NaïveProxy client daemon&lt;br /&gt;
After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
ExecStart=/usr/bin/naive /etc/naive/config.json&lt;br /&gt;
Restart=always&lt;br /&gt;
RestartSec=5&lt;br /&gt;
User=nobody&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Активировать и запустить&lt;br /&gt;
systemctl daemon-reload&lt;br /&gt;
systemctl enable naive&lt;br /&gt;
systemctl start naive&lt;br /&gt;
&lt;br /&gt;
# Проверить статус&lt;br /&gt;
systemctl status naive&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ожидаемый вывод:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
● naive.service - NaïveProxy client daemon&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/naive.service; enabled)&lt;br /&gt;
     Active: active (running)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 2.6 Проверка цепочки с RU сервера ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Тест HTTP через naive daemon → EU → WARP&lt;br /&gt;
curl -s -x socks5h://127.0.0.1:10808 http://ifconfig.me&lt;br /&gt;
# Должен вернуть IP EU сервера или Cloudflare WARP IP&lt;br /&gt;
&lt;br /&gt;
# Тест HTTPS&lt;br /&gt;
curl -s -x socks5h://127.0.0.1:10808 https://ifconfig.me&lt;br /&gt;
# Должен вернуть тот же результат&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Приложение Б. Скрипты управления пользователями =&lt;br /&gt;
&lt;br /&gt;
Скрипты из прежней схемы, когда учётки правились в файлах. С панелью они &#039;&#039;&#039;не нужны&#039;&#039;&#039; — управление пользователями, лимитами и подписками делается через интерфейс или API. Оставлены для варианта из [[#Приложение А. Ручная установка без панели|Приложения А]] и для тех, кто автоматизирует выходное плечо.&lt;br /&gt;
&lt;br /&gt;
== Б.1. mieru-users.sh ==&lt;br /&gt;
&lt;br /&gt;
Скрипт добавляет, удаляет и показывает учётки на &amp;lt;code&amp;gt;mita&amp;lt;/code&amp;gt;, попутно генерируя клиентские конфигурации и QR-коды.&lt;br /&gt;
&lt;br /&gt;
Важная деталь реализации: при удалении вызывается &amp;lt;code&amp;gt;mita delete user&amp;lt;/code&amp;gt;, а не только правка JSON. Если убрать пользователя лишь из файла, его &#039;&#039;&#039;активные сессии продолжат работать&#039;&#039;&#039; до истечения тайм-аута.&lt;br /&gt;
&lt;br /&gt;
=== Исходный код &amp;lt;code&amp;gt;mieru-users.sh&amp;lt;/code&amp;gt; ===&lt;br /&gt;
Полный текст скрипта приведён ниже. Скопируйте его целиком в &amp;lt;code&amp;gt;/usr/local/bin/mieru-users.sh&amp;lt;/code&amp;gt; на frontend-сервере по инструкции выше.&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# mieru-users — управление пользователями mita&lt;br /&gt;
# Источник истины: /opt/mieru-frontend/server.json&lt;br /&gt;
# Применение через mita apply config (RPC к работающему daemon).&lt;br /&gt;
&lt;br /&gt;
CONFIG_FILE=&amp;quot;/opt/mieru-frontend/server.json&amp;quot;&lt;br /&gt;
COMPOSE_DIR=&amp;quot;/opt/mieru-frontend&amp;quot;&lt;br /&gt;
COMPOSE_SVC=&amp;quot;mita&amp;quot;&lt;br /&gt;
BACKUP_DIR=&amp;quot;/opt/mieru-frontend/backups&amp;quot;&lt;br /&gt;
BACKUP_KEEP=10&lt;br /&gt;
SERVER_IP=&amp;quot;YOUR_FRONTEND_PUBLIC_IP&amp;quot;&lt;br /&gt;
&lt;br /&gt;
RED=&#039;\033[0;31m&#039;; GREEN=&#039;\033[0;32m&#039;; YELLOW=&#039;\033[1;33m&#039;&lt;br /&gt;
CYAN=&#039;\033[0;36m&#039;; BOLD=&#039;\033[1m&#039;; NC=&#039;\033[0m&#039;&lt;br /&gt;
&lt;br /&gt;
check_root() {&lt;br /&gt;
    if [[ $EUID -ne 0 ]]; then&lt;br /&gt;
        echo -e &amp;quot;${RED}Запусти от root: sudo bash mieru-users.sh${NC}&amp;quot;; exit 1&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
check_deps() {&lt;br /&gt;
    for c in jq docker openssl; do&lt;br /&gt;
        if ! command -v &amp;quot;$c&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then&lt;br /&gt;
            echo -e &amp;quot;${RED}Не найдено: $c${NC}&amp;quot;; exit 1&lt;br /&gt;
        fi&lt;br /&gt;
    done&lt;br /&gt;
    if [[ ! -f &amp;quot;$CONFIG_FILE&amp;quot; ]]; then&lt;br /&gt;
        echo -e &amp;quot;${RED}Не найден $CONFIG_FILE${NC}&amp;quot;; exit 1&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
mita_cli() {&lt;br /&gt;
    docker compose -f &amp;quot;$COMPOSE_DIR/docker-compose.yml&amp;quot; exec -T &amp;quot;$COMPOSE_SVC&amp;quot; mita &amp;quot;$@&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
backup_config() {&lt;br /&gt;
    mkdir -p &amp;quot;$BACKUP_DIR&amp;quot;&lt;br /&gt;
    local stamp; stamp=$(date +%Y%m%d-%H%M%S)&lt;br /&gt;
    cp &amp;quot;$CONFIG_FILE&amp;quot; &amp;quot;$BACKUP_DIR/server-$stamp.json&amp;quot;&lt;br /&gt;
    ls -1t &amp;quot;$BACKUP_DIR&amp;quot;/server-*.json 2&amp;gt;/dev/null | tail -n +$((BACKUP_KEEP+1)) | xargs -r rm -f&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
apply_config() {&lt;br /&gt;
    if ! mita_cli apply config /srv/server.json 2&amp;gt;&amp;amp;1; then&lt;br /&gt;
        echo -e &amp;quot;${RED}mita apply config упал. Откатываюсь к последнему бэкапу.${NC}&amp;quot;&lt;br /&gt;
        local last; last=$(ls -1t &amp;quot;$BACKUP_DIR&amp;quot;/server-*.json 2&amp;gt;/dev/null | head -1)&lt;br /&gt;
        if [[ -n &amp;quot;$last&amp;quot; ]]; then&lt;br /&gt;
            cp &amp;quot;$last&amp;quot; &amp;quot;$CONFIG_FILE&amp;quot;&lt;br /&gt;
            mita_cli apply config /srv/server.json &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || true&lt;br /&gt;
        fi&lt;br /&gt;
        return 1&lt;br /&gt;
    fi&lt;br /&gt;
    return 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
press_enter() { echo &amp;quot;&amp;quot;; read -rp &amp;quot;Enter для возврата...&amp;quot;; }&lt;br /&gt;
&lt;br /&gt;
generate_password() {&lt;br /&gt;
    openssl rand -base64 24 | tr -d &#039;=/+&#039; | head -c 31&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
generate_uri() {&lt;br /&gt;
    echo &amp;quot;mierus://${1}:${2}@${SERVER_IP}?port=2022&amp;amp;protocol=TCP&amp;amp;mtu=1380&amp;amp;multiplexing=MULTIPLEXING_HIGH&amp;amp;handshake-mode=HANDSHAKE_NO_WAIT&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
generate_yaml() {&lt;br /&gt;
    cat &amp;lt;&amp;lt;EOF&lt;br /&gt;
proxies:&lt;br /&gt;
  - name: mieru-${1}&lt;br /&gt;
    type: mieru&lt;br /&gt;
    server: ${SERVER_IP}&lt;br /&gt;
    port: 2022&lt;br /&gt;
    username: ${1}&lt;br /&gt;
    password: ${2}&lt;br /&gt;
    multiplexing: MULTIPLEXING_HIGH&lt;br /&gt;
EOF&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
print_uri_for() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${CYAN}=== Вариант 1: Clash / Mihomo YAML ===${NC}&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}$(generate_yaml &amp;quot;$1&amp;quot; &amp;quot;$2&amp;quot;)${NC}&amp;quot;&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${CYAN}=== Вариант 2: mierus:// URI ===${NC}&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}  $(generate_uri &amp;quot;$1&amp;quot; &amp;quot;$2&amp;quot;)${NC}&amp;quot;&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${CYAN}user=${BOLD}${1}${NC}, pwd=${BOLD}${2}${NC}, host=${BOLD}${SERVER_IP}${NC}, port=${BOLD}2022${NC}&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
list_users() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    local n; n=$(jq &#039;.users | length&#039; &amp;quot;$CONFIG_FILE&amp;quot;)&lt;br /&gt;
    echo -e &amp;quot;${BOLD}${CYAN}=== Пользователей: $n ===${NC}&amp;quot;&lt;br /&gt;
    jq -r &#039;.users[] | &amp;quot;  \(.name)\t\(.password[0:6])…&amp;quot;&#039; &amp;quot;$CONFIG_FILE&amp;quot; | nl -w2 -s&#039;. &#039;&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}${CYAN}=== Активные сессии ===${NC}&amp;quot;&lt;br /&gt;
    mita_cli get connections 2&amp;gt;&amp;amp;1 | head -20&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
add_user() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;; read -rp &amp;quot;Имя пользователя: &amp;quot; name&lt;br /&gt;
    name=$(echo &amp;quot;$name&amp;quot; | tr -d &#039; &#039;)&lt;br /&gt;
    [[ -z &amp;quot;$name&amp;quot; ]] &amp;amp;&amp;amp; { echo &amp;quot;Пусто.&amp;quot;; press_enter; return; }&lt;br /&gt;
    if jq -e --arg n &amp;quot;$name&amp;quot; &#039;.users[] | select(.name == $n)&#039; &amp;quot;$CONFIG_FILE&amp;quot; &amp;gt;/dev/null; then&lt;br /&gt;
        echo &amp;quot;Уже есть.&amp;quot;; press_enter; return&lt;br /&gt;
    fi&lt;br /&gt;
    read -rp &amp;quot;Пароль (Enter — сгенерировать): &amp;quot; pwd&lt;br /&gt;
    [[ -z &amp;quot;$pwd&amp;quot; ]] &amp;amp;&amp;amp; pwd=$(generate_password)&lt;br /&gt;
&lt;br /&gt;
    backup_config&lt;br /&gt;
    local tmp; tmp=$(mktemp)&lt;br /&gt;
    jq --arg n &amp;quot;$name&amp;quot; --arg p &amp;quot;$pwd&amp;quot; &#039;.users += [{&amp;quot;name&amp;quot;:$n,&amp;quot;password&amp;quot;:$p}]&#039; &amp;quot;$CONFIG_FILE&amp;quot; &amp;gt; &amp;quot;$tmp&amp;quot; &amp;amp;&amp;amp; mv &amp;quot;$tmp&amp;quot; &amp;quot;$CONFIG_FILE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if apply_config; then&lt;br /&gt;
        echo -e &amp;quot;${GREEN}Пользователь $name добавлен.${NC}&amp;quot;&lt;br /&gt;
        print_uri_for &amp;quot;$name&amp;quot; &amp;quot;$pwd&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
delete_user() {&lt;br /&gt;
    mapfile -t users &amp;lt; &amp;lt;(jq -r &#039;.users[].name&#039; &amp;quot;$CONFIG_FILE&amp;quot;)&lt;br /&gt;
    [[ ${#users[@]} -eq 0 ]] &amp;amp;&amp;amp; { echo &amp;quot;Пусто.&amp;quot;; press_enter; return; }&lt;br /&gt;
    for i in &amp;quot;${!users[@]}&amp;quot;; do echo &amp;quot;  $((i+1))) ${users[$i]}&amp;quot;; done&lt;br /&gt;
    echo &amp;quot;  0) Отмена&amp;quot;; read -rp &amp;quot;Номер: &amp;quot; n&lt;br /&gt;
    [[ &amp;quot;$n&amp;quot; == &amp;quot;0&amp;quot; || -z &amp;quot;$n&amp;quot; ]] &amp;amp;&amp;amp; return&lt;br /&gt;
    local target=&amp;quot;${users[$((n-1))]}&amp;quot;&lt;br /&gt;
    read -rp &amp;quot;Удалить &#039;$target&#039;? (y/N): &amp;quot; yn&lt;br /&gt;
    [[ ! &amp;quot;$yn&amp;quot; =~ ^[Yy]$ ]] &amp;amp;&amp;amp; return&lt;br /&gt;
&lt;br /&gt;
    mita_cli delete user &amp;quot;$target&amp;quot; 2&amp;gt;&amp;amp;1&lt;br /&gt;
    backup_config&lt;br /&gt;
    local tmp; tmp=$(mktemp)&lt;br /&gt;
    jq --arg n &amp;quot;$target&amp;quot; &#039;del(.users[] | select(.name == $n))&#039; &amp;quot;$CONFIG_FILE&amp;quot; &amp;gt; &amp;quot;$tmp&amp;quot; &amp;amp;&amp;amp; mv &amp;quot;$tmp&amp;quot; &amp;quot;$CONFIG_FILE&amp;quot;&lt;br /&gt;
    apply_config &amp;amp;&amp;amp; echo -e &amp;quot;${GREEN}Удалён.${NC}&amp;quot;&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
show_uri() {&lt;br /&gt;
    mapfile -t users &amp;lt; &amp;lt;(jq -r &#039;.users[].name&#039; &amp;quot;$CONFIG_FILE&amp;quot;)&lt;br /&gt;
    [[ ${#users[@]} -eq 0 ]] &amp;amp;&amp;amp; { echo &amp;quot;Пусто.&amp;quot;; press_enter; return; }&lt;br /&gt;
    for i in &amp;quot;${!users[@]}&amp;quot;; do echo &amp;quot;  $((i+1))) ${users[$i]}&amp;quot;; done&lt;br /&gt;
    read -rp &amp;quot;Номер: &amp;quot; n&lt;br /&gt;
    [[ -z &amp;quot;$n&amp;quot; ]] &amp;amp;&amp;amp; return&lt;br /&gt;
    local user=&amp;quot;${users[$((n-1))]}&amp;quot;&lt;br /&gt;
    local pwd; pwd=$(jq -r --arg n &amp;quot;$user&amp;quot; &#039;.users[] | select(.name == $n) | .password&#039; &amp;quot;$CONFIG_FILE&amp;quot;)&lt;br /&gt;
    print_uri_for &amp;quot;$user&amp;quot; &amp;quot;$pwd&amp;quot;&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
server_status() {&lt;br /&gt;
    mita_cli status 2&amp;gt;&amp;amp;1&lt;br /&gt;
    echo&lt;br /&gt;
    mita_cli get connections 2&amp;gt;&amp;amp;1&lt;br /&gt;
    echo&lt;br /&gt;
    mita_cli get metrics 2&amp;gt;&amp;amp;1 | head -30&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
main_menu() {&lt;br /&gt;
    check_root; check_deps&lt;br /&gt;
    while true; do&lt;br /&gt;
        clear&lt;br /&gt;
        echo &amp;quot;╔══════════════════════════════════════╗&amp;quot;&lt;br /&gt;
        echo &amp;quot;║   mieru User Manager                 ║&amp;quot;&lt;br /&gt;
        echo &amp;quot;║   IP: $SERVER_IP&amp;quot;&lt;br /&gt;
        echo &amp;quot;╚══════════════════════════════════════╝&amp;quot;&lt;br /&gt;
        echo&lt;br /&gt;
        echo &amp;quot;  1) Список пользователей&amp;quot;&lt;br /&gt;
        echo &amp;quot;  2) Добавить&amp;quot;&lt;br /&gt;
        echo &amp;quot;  3) Удалить&amp;quot;&lt;br /&gt;
        echo &amp;quot;  4) URI клиента&amp;quot;&lt;br /&gt;
        echo &amp;quot;  5) Статус сервера&amp;quot;&lt;br /&gt;
        echo &amp;quot;  0) Выход&amp;quot;&lt;br /&gt;
        read -rp &amp;quot;Выбор: &amp;quot; opt&lt;br /&gt;
        case &amp;quot;$opt&amp;quot; in&lt;br /&gt;
            1) list_users ;; 2) add_user ;;&lt;br /&gt;
            3) delete_user ;; 4) show_uri ;;&lt;br /&gt;
            5) server_status ;; 0) exit 0 ;;&lt;br /&gt;
        esac&lt;br /&gt;
    done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
main_menu&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Б.2. naive-users ==&lt;br /&gt;
&lt;br /&gt;
Аналог для NaïveProxy: правит &amp;lt;code&amp;gt;basic_auth&amp;lt;/code&amp;gt;-строки в &amp;lt;code&amp;gt;Caddyfile&amp;lt;/code&amp;gt; и перезапускает контейнер. Учёта трафика здесь нет и быть не может — Caddy его не ведёт.&lt;br /&gt;
&lt;br /&gt;
==== 4.5 Исходный код ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;padding:4px 0&amp;quot;&amp;gt;▶ naive-users.sh — показать исходный код&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# =============================================================&lt;br /&gt;
# naive-users — управление пользователями NaïveProxy на RU сервере&lt;br /&gt;
# Caddyfile: /opt/caddy-naive/Caddyfile&lt;br /&gt;
# =============================================================&lt;br /&gt;
&lt;br /&gt;
CADDYFILE=&amp;quot;/opt/caddy-naive/Caddyfile&amp;quot;&lt;br /&gt;
CADDY_DIR=&amp;quot;/opt/caddy-naive&amp;quot;&lt;br /&gt;
# Подставьте свой домен (не коммитьте реальные значения в публичный репозиторий)&lt;br /&gt;
DOMAIN=&amp;quot;your-naive-ru-domain.example.com&amp;quot;&lt;br /&gt;
&lt;br /&gt;
RED=&#039;\033[0;31m&#039;&lt;br /&gt;
GREEN=&#039;\033[0;32m&#039;&lt;br /&gt;
YELLOW=&#039;\033[1;33m&#039;&lt;br /&gt;
CYAN=&#039;\033[0;36m&#039;&lt;br /&gt;
BOLD=&#039;\033[1m&#039;&lt;br /&gt;
NC=&#039;\033[0m&#039;&lt;br /&gt;
&lt;br /&gt;
# --- Вспомогательные функции ---&lt;br /&gt;
&lt;br /&gt;
check_root() {&lt;br /&gt;
    if [[ $EUID -ne 0 ]]; then&lt;br /&gt;
        echo -e &amp;quot;${RED}Ошибка: запусти скрипт от root (sudo bash naive-users.sh)${NC}&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
check_caddyfile() {&lt;br /&gt;
    if [[ ! -f &amp;quot;$CADDYFILE&amp;quot; ]]; then&lt;br /&gt;
        echo -e &amp;quot;${RED}Ошибка: Caddyfile не найден: $CADDYFILE${NC}&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Возвращает массив пользователей из Caddyfile&lt;br /&gt;
get_users() {&lt;br /&gt;
    grep -oP &#039;(?&amp;lt;=basic_auth )\S+&#039; &amp;quot;$CADDYFILE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Возвращает пароль пользователя&lt;br /&gt;
get_password() {&lt;br /&gt;
    local user=&amp;quot;$1&amp;quot;&lt;br /&gt;
    grep &amp;quot;basic_auth $user &amp;quot; &amp;quot;$CADDYFILE&amp;quot; | awk &#039;{print $3}&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
restart_caddy() {&lt;br /&gt;
    echo -e &amp;quot;${YELLOW}Перезапуск Caddy...${NC}&amp;quot;&lt;br /&gt;
    cd &amp;quot;$CADDY_DIR&amp;quot; &amp;amp;&amp;amp; docker compose restart caddy-naive &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;br /&gt;
    if [[ $? -eq 0 ]]; then&lt;br /&gt;
        echo -e &amp;quot;${GREEN}Caddy перезапущен успешно.${NC}&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        echo -e &amp;quot;${RED}Ошибка при перезапуске Caddy. Проверь: cd $CADDY_DIR &amp;amp;&amp;amp; docker compose logs${NC}&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
press_enter() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    read -rp &amp;quot;Нажми Enter для возврата в меню...&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- Функции меню ---&lt;br /&gt;
&lt;br /&gt;
list_users() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}${CYAN}=== Список клиентов ===${NC}&amp;quot;&lt;br /&gt;
    mapfile -t users &amp;lt; &amp;lt;(get_users)&lt;br /&gt;
    if [[ ${#users[@]} -eq 0 ]]; then&lt;br /&gt;
        echo -e &amp;quot;${YELLOW}Клиентов нет.${NC}&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        for i in &amp;quot;${!users[@]}&amp;quot;; do&lt;br /&gt;
            echo -e &amp;quot;  ${BOLD}$((i+1)).${NC} ${users[$i]}&amp;quot;&lt;br /&gt;
        done&lt;br /&gt;
        echo &amp;quot;&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;  Всего: ${#users[@]} клиент(ов)&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
add_user() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}${CYAN}=== Добавить клиента ===${NC}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    read -rp &amp;quot;Имя пользователя: &amp;quot; username&lt;br /&gt;
    username=$(echo &amp;quot;$username&amp;quot; | tr -d &#039; &#039;)&lt;br /&gt;
&lt;br /&gt;
    if [[ -z &amp;quot;$username&amp;quot; ]]; then&lt;br /&gt;
        echo -e &amp;quot;${RED}Имя не может быть пустым.${NC}&amp;quot;&lt;br /&gt;
        press_enter&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    # Проверка — уже существует?&lt;br /&gt;
    if grep -q &amp;quot;basic_auth $username &amp;quot; &amp;quot;$CADDYFILE&amp;quot;; then&lt;br /&gt;
        echo -e &amp;quot;${RED}Пользователь &#039;$username&#039; уже существует.${NC}&amp;quot;&lt;br /&gt;
        press_enter&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    read -rp &amp;quot;Пароль (Enter = сгенерировать автоматически): &amp;quot; password&lt;br /&gt;
    if [[ -z &amp;quot;$password&amp;quot; ]]; then&lt;br /&gt;
        password=$(openssl rand -base64 16 | tr -d &#039;=/+&#039; | head -c 20)&lt;br /&gt;
        echo -e &amp;quot;  Сгенерирован пароль: ${BOLD}${password}${NC}&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    # Вставить строку basic_auth перед первой строкой upstream или probe_resistance&lt;br /&gt;
    sed -i &amp;quot;/probe_resistance/i\\    basic_auth $username $password&amp;quot; &amp;quot;$CADDYFILE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    echo -e &amp;quot;${GREEN}Пользователь &#039;${username}&#039; добавлен.${NC}&amp;quot;&lt;br /&gt;
    restart_caddy&lt;br /&gt;
&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}Конфиг для клиента:${NC}&amp;quot;&lt;br /&gt;
    print_config_for &amp;quot;$username&amp;quot; &amp;quot;$password&amp;quot;&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
delete_user() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}${CYAN}=== Удалить клиента ===${NC}&amp;quot;&lt;br /&gt;
    mapfile -t users &amp;lt; &amp;lt;(get_users)&lt;br /&gt;
&lt;br /&gt;
    if [[ ${#users[@]} -eq 0 ]]; then&lt;br /&gt;
        echo -e &amp;quot;${YELLOW}Нет клиентов для удаления.${NC}&amp;quot;&lt;br /&gt;
        press_enter&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    for i in &amp;quot;${!users[@]}&amp;quot;; do&lt;br /&gt;
        echo -e &amp;quot;  ${BOLD}$((i+1)).${NC} ${users[$i]}&amp;quot;&lt;br /&gt;
    done&lt;br /&gt;
    echo &amp;quot;  0. Отмена&amp;quot;&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    read -rp &amp;quot;Выбери номер для удаления: &amp;quot; choice&lt;br /&gt;
&lt;br /&gt;
    if [[ &amp;quot;$choice&amp;quot; == &amp;quot;0&amp;quot; || -z &amp;quot;$choice&amp;quot; ]]; then&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    if ! [[ &amp;quot;$choice&amp;quot; =~ ^[0-9]+$ ]] || (( choice &amp;lt; 1 || choice &amp;gt; ${#users[@]} )); then&lt;br /&gt;
        echo -e &amp;quot;${RED}Неверный номер.${NC}&amp;quot;&lt;br /&gt;
        press_enter&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    target=&amp;quot;${users[$((choice-1))]}&amp;quot;&lt;br /&gt;
    read -rp &amp;quot;Удалить пользователя &#039;${target}&#039;? (y/N): &amp;quot; confirm&lt;br /&gt;
    if [[ &amp;quot;$confirm&amp;quot; =~ ^[Yy]$ ]]; then&lt;br /&gt;
        sed -i &amp;quot;/basic_auth $target /d&amp;quot; &amp;quot;$CADDYFILE&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;${GREEN}Пользователь &#039;${target}&#039; удалён.${NC}&amp;quot;&lt;br /&gt;
        restart_caddy&lt;br /&gt;
    else&lt;br /&gt;
        echo &amp;quot;Отменено.&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
print_config_for() {&lt;br /&gt;
    local user=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local pass=&amp;quot;$2&amp;quot;&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}┌─────────────────────────────────────────────┐${NC}&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}│  config.json для naive (ПК / роутер)        │${NC}&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}└─────────────────────────────────────────────┘${NC}&amp;quot;&lt;br /&gt;
    echo &#039;{&lt;br /&gt;
  &amp;quot;listen&amp;quot;: &amp;quot;socks://127.0.0.1:1080&amp;quot;,&lt;br /&gt;
  &amp;quot;proxy&amp;quot;: &amp;quot;https://&#039;&amp;quot;$user&amp;quot;&#039;:&#039;&amp;quot;$pass&amp;quot;&#039;@&#039;&amp;quot;$DOMAIN&amp;quot;&#039;&amp;quot;,&lt;br /&gt;
  &amp;quot;log&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
}&#039;&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${CYAN}Путь на роутере OpenWrt:${NC}  /etc/naive/config.json&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${CYAN}Путь на Windows ПК:${NC}       рядом с naive.exe&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${CYAN}SOCKS5 адрес для podkop:${NC}  socks5://127.0.0.1:1080&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
show_config() {&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    echo -e &amp;quot;${BOLD}${CYAN}=== Показать конфиг клиента ===${NC}&amp;quot;&lt;br /&gt;
    mapfile -t users &amp;lt; &amp;lt;(get_users)&lt;br /&gt;
&lt;br /&gt;
    if [[ ${#users[@]} -eq 0 ]]; then&lt;br /&gt;
        echo -e &amp;quot;${YELLOW}Нет клиентов.${NC}&amp;quot;&lt;br /&gt;
        press_enter&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    for i in &amp;quot;${!users[@]}&amp;quot;; do&lt;br /&gt;
        echo -e &amp;quot;  ${BOLD}$((i+1)).${NC} ${users[$i]}&amp;quot;&lt;br /&gt;
    done&lt;br /&gt;
    echo &amp;quot;  0. Отмена&amp;quot;&lt;br /&gt;
    echo &amp;quot;&amp;quot;&lt;br /&gt;
    read -rp &amp;quot;Выбери номер клиента: &amp;quot; choice&lt;br /&gt;
&lt;br /&gt;
    if [[ &amp;quot;$choice&amp;quot; == &amp;quot;0&amp;quot; || -z &amp;quot;$choice&amp;quot; ]]; then&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    if ! [[ &amp;quot;$choice&amp;quot; =~ ^[0-9]+$ ]] || (( choice &amp;lt; 1 || choice &amp;gt; ${#users[@]} )); then&lt;br /&gt;
        echo -e &amp;quot;${RED}Неверный номер.${NC}&amp;quot;&lt;br /&gt;
        press_enter&lt;br /&gt;
        return&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    local user=&amp;quot;${users[$((choice-1))]}&amp;quot;&lt;br /&gt;
    local pass&lt;br /&gt;
    pass=$(get_password &amp;quot;$user&amp;quot;)&lt;br /&gt;
    echo -e &amp;quot;Клиент: ${BOLD}${user}${NC}&amp;quot;&lt;br /&gt;
    print_config_for &amp;quot;$user&amp;quot; &amp;quot;$pass&amp;quot;&lt;br /&gt;
    press_enter&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- Главное меню ---&lt;br /&gt;
&lt;br /&gt;
main_menu() {&lt;br /&gt;
    check_root&lt;br /&gt;
    check_caddyfile&lt;br /&gt;
&lt;br /&gt;
    while true; do&lt;br /&gt;
        clear&lt;br /&gt;
        echo -e &amp;quot;${BOLD}${CYAN}&amp;quot;&lt;br /&gt;
        echo &amp;quot;╔══════════════════════════════════════╗&amp;quot;&lt;br /&gt;
        echo &amp;quot;║   NaïveProxy User Manager (RU)       ║&amp;quot;&lt;br /&gt;
        echo &amp;quot;║   Домен: $DOMAIN   ║&amp;quot;&lt;br /&gt;
        echo &amp;quot;╚══════════════════════════════════════╝&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;${NC}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        mapfile -t users &amp;lt; &amp;lt;(get_users)&lt;br /&gt;
        echo -e &amp;quot;  Активных клиентов: ${BOLD}${#users[@]}${NC}&amp;quot;&lt;br /&gt;
        echo &amp;quot;&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;  ${BOLD}1.${NC} Список клиентов&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;  ${BOLD}2.${NC} Добавить клиента&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;  ${BOLD}3.${NC} Удалить клиента&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;  ${BOLD}4.${NC} Показать конфиг клиента&amp;quot;&lt;br /&gt;
        echo -e &amp;quot;  ${BOLD}0.${NC} Выход&amp;quot;&lt;br /&gt;
        echo &amp;quot;&amp;quot;&lt;br /&gt;
        read -rp &amp;quot;Выбор: &amp;quot; option&lt;br /&gt;
&lt;br /&gt;
        case &amp;quot;$option&amp;quot; in&lt;br /&gt;
            1) list_users ;;&lt;br /&gt;
            2) add_user ;;&lt;br /&gt;
            3) delete_user ;;&lt;br /&gt;
            4) show_config ;;&lt;br /&gt;
            0) echo &amp;quot;Выход.&amp;quot;; exit 0 ;;&lt;br /&gt;
            *) echo -e &amp;quot;${RED}Неверный выбор.${NC}&amp;quot;; sleep 1 ;;&lt;br /&gt;
        esac&lt;br /&gt;
    done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
main_menu&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/enfein/mieru Официальный репозиторий mieru]&lt;br /&gt;
* [https://github.com/enfein/mieru/blob/master/docs/protocol.md Спецификация протокола mieru]&lt;br /&gt;
* [https://github.com/enfein/mieru/blob/master/docs/traffic-pattern.md Документация по traffic pattern]&lt;br /&gt;
* [https://github.com/enfein/mieru/blob/master/docs/security.md Руководство по безопасности mieru]&lt;br /&gt;
* [https://github.com/klzgrad/naiveproxy Официальный репозиторий NaïveProxy]&lt;br /&gt;
* [https://github.com/klzgrad/forwardproxy Форк forwardproxy для Caddy]&lt;br /&gt;
* [https://github.com/deposist/s-ui-x-extended Панель s-ui-x-extended]&lt;br /&gt;
* [https://github.com/shtorm-7/sing-box-extended Ядро sing-box-extended]&lt;br /&gt;
* [https://github.com/acmesh-official/acme.sh acme.sh]&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%9E%D0%B1%D1%85%D0%BE%D0%B4_%D0%9D%D0%9A%D0%A0&amp;diff=1034</id>
		<title>Обход НКР</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%9E%D0%B1%D1%85%D0%BE%D0%B4_%D0%9D%D0%9A%D0%A0&amp;diff=1034"/>
		<updated>2026-07-02T11:21:57Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* Исходный код скрипта */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Установка и настройка связки Zapret + Podkop на OpenWRT =&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Данная инструкция описывает установку и настройку связки двух сервисов обхода блокировок на роутере OpenWRT:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Zapret&#039;&#039;&#039; — утилита DPI-обхода (anti-DPI), работает без VPN&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Podkop&#039;&#039;&#039; — прокси-менеджер на базе sing-box с поддержкой VPN&lt;br /&gt;
&lt;br /&gt;
=== Суть связки ===&lt;br /&gt;
Комбинация Zapret + Podkop позволяет реализовать оптимальную стратегию обхода блокировок:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Zapret&#039;&#039;&#039; обрабатывает трафик &#039;&#039;&#039;первым&#039;&#039;&#039; (приоритет firewall -150/-100) и применяет DPI-обход для популярных сервисов (YouTube, Discord, Steam и др.) &#039;&#039;&#039;без использования VPN&#039;&#039;&#039;. Это обеспечивает:&lt;br /&gt;
&lt;br /&gt;
#* Высокую скорость (нет шифрования VPN)&lt;br /&gt;
&lt;br /&gt;
#* Сохранение геолокации РФ (важно для YouTube, Twitch и других региональных сервисов)&lt;br /&gt;
&lt;br /&gt;
#* Минимальную нагрузку на роутер&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Podkop&#039;&#039;&#039; обрабатывает трафик &#039;&#039;&#039;вторым&#039;&#039;&#039; (приоритет firewall 0) и направляет через VPN/прокси &#039;&#039;&#039;только те домены&#039;&#039;&#039;, которые Zapret не может обойти или требуют смены региона&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-message-box mw-message-box-notice&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Важно.&#039;&#039;&#039; Эта архитектура позволяет &#039;&#039;&#039;экономить VPN-трафик&#039;&#039;&#039; и &#039;&#039;&#039;сохранять региональный контент&#039;&#039;&#039; (например, русскоязычные каналы YouTube), при этом имея полноценный VPN для заблокированных ресурсов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
&lt;br /&gt;
* OpenWRT версии 24 или выше&lt;br /&gt;
&lt;br /&gt;
* Минимум 25 МБ свободного места в памяти роутера&lt;br /&gt;
&lt;br /&gt;
* Доступ к интернету для загрузки пакетов&lt;br /&gt;
&lt;br /&gt;
* Базовые навыки работы с SSH/терминалом&lt;br /&gt;
&lt;br /&gt;
== Установка Zapret ==&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1: Установка вспомогательных пакетов ===&lt;br /&gt;
Через веб-интерфейс OpenWRT перейдите в &#039;&#039;&#039;System → Software&#039;&#039;&#039; и установите:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; — для скачивания установщика&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;code&amp;gt;luci-app-ttyd&amp;lt;/code&amp;gt; — для доступа к терминалу через веб-интерфейс&lt;br /&gt;
&lt;br /&gt;
После установки &amp;lt;code&amp;gt;luci-app-ttyd&amp;lt;/code&amp;gt; в меню появится раздел &#039;&#039;&#039;Services → Terminal&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2: Запуск установщика Zapret ===&lt;br /&gt;
Откройте терминал (&#039;&#039;&#039;Services → Terminal&#039;&#039;&#039;) и выполните команду:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
curl -fsSL https://raw.githubusercontent.com/remittor/zapret-openwrt/zap1/zapret/update-pkg.sh -o /tmp/zap.sh &amp;amp;&amp;amp; sh /tmp/zap.sh -u 1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Дождитесь завершения установки.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3: Перезагрузка интерфейса ===&lt;br /&gt;
Выйдите из веб-интерфейса (&#039;&#039;&#039;Log Out&#039;&#039;&#039;) и войдите снова. В меню &#039;&#039;&#039;Services&#039;&#039;&#039; появится раздел &#039;&#039;&#039;Zapret&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Установка Podkop ==&lt;br /&gt;
Выполните команду в терминале:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sh &amp;lt;(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;После установки &#039;&#039;&#039;обязательно очистите кэш браузера&#039;&#039;&#039; и перезайдите в LuCI. В меню &#039;&#039;&#039;Services&#039;&#039;&#039; появится раздел &#039;&#039;&#039;Podkop&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Настройка Zapret ==&lt;br /&gt;
&lt;br /&gt;
После установки пакет &#039;&#039;&#039;уже содержит&#039;&#039;&#039; нужные домены и параметры — &#039;&#039;&#039;ручную настройку проходить не нужно&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Откройте &#039;&#039;&#039;Services → Zapret → Service&#039;&#039;&#039; и проверьте только следующее:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Service daemons control&#039;&#039;&#039; — включён (сервис Zapret активен).&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Service autorun control&#039;&#039;&#039; — включён (Zapret поднимается после перезагрузки роутера).&lt;br /&gt;
&lt;br /&gt;
При необходимости можно добавить свои домены в поле &#039;&#039;&#039;User hostname entries&#039;&#039;&#039; в веб-интерфейсе (как правило — &#039;&#039;&#039;по одному домену на строку&#039;&#039;&#039;); для типовой установки это &#039;&#039;&#039;не обязательно&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Настройка Podkop ==&lt;br /&gt;
&lt;br /&gt;
=== Важное предупреждение о списках сообщества ===&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-message-box mw-message-box-warning&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ПРЕДУПРЕЖДЕНИЕ:&#039;&#039;&#039; Не используйте готовые списки сообщества &#039;&#039;&#039;Russian inside&#039;&#039;&#039; в базовой конфигурации!&lt;br /&gt;
&amp;lt;/div&amp;gt;&#039;&#039;&#039;Почему нельзя использовать готовые списки:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Готовые списки сообщества (например, Russian inside) включают домены YouTube, Google, Discord, которые &#039;&#039;&#039;уже обрабатываются Zapret&#039;&#039;&#039;. Если добавить их в Podkop, весь трафик этих сервисов будет принудительно направлен через VPN, что приведёт к:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Увеличению расхода VPN-трафика в десятки раз&#039;&#039;&#039; (YouTube потребляет гигабайты данных)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Смене региона&#039;&#039;&#039; на регион VPN-сервера (потеряете русскоязычный контент YouTube, региональные рекомендации)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Конфликту с Zapret&#039;&#039;&#039; (один и тот же домен будет обрабатываться дважды)&lt;br /&gt;
&lt;br /&gt;
=== Использование готового списка доменов ===&lt;br /&gt;
Для удобства мы подготовили &#039;&#039;&#039;готовый список доменов&#039;&#039;&#039; для Podkop, который:&lt;br /&gt;
&lt;br /&gt;
* Основан на актуальном списке &#039;&#039;&#039;Russian inside&#039;&#039;&#039; (&amp;lt;code&amp;gt;inside-raw.lst&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Исключает&#039;&#039;&#039; все домены из &amp;lt;code&amp;gt;zapret-hosts-google.txt&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;zapret-hosts-user.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Дополнен&#039;&#039;&#039; специализированными доменами (торрент-трекеры, облачные сервисы, AI-инструменты и др.)&lt;br /&gt;
&lt;br /&gt;
==== Шаг 1: Скопируйте список доменов ====&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%; overflow:auto;&amp;quot;&amp;gt;&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Список доменов для Podkop (нажмите, чтобы развернуть)&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
.ua&lt;br /&gt;
10minutemail.com&lt;br /&gt;
1337x.to&lt;br /&gt;
1fichier.com&lt;br /&gt;
24.kg&lt;br /&gt;
4freerussia.org&lt;br /&gt;
4pda.to&lt;br /&gt;
4pda.ws&lt;br /&gt;
4pna.com&lt;br /&gt;
529055.xyz&lt;br /&gt;
5sim.net&lt;br /&gt;
7dniv.rv.ua&lt;br /&gt;
7tv.app&lt;br /&gt;
7tv.io&lt;br /&gt;
9tv.co.il&lt;br /&gt;
a-13.1fichier.com&lt;br /&gt;
a-vrv.akamaized.net&lt;br /&gt;
abercrombie.com&lt;br /&gt;
abook-club.ru&lt;br /&gt;
academy.terrasoft.ua&lt;br /&gt;
activatica.org&lt;br /&gt;
adguard.com&lt;br /&gt;
adidas.com&lt;br /&gt;
adminforge.de&lt;br /&gt;
adobe.com&lt;br /&gt;
ads-twitter.com&lt;br /&gt;
adultmult.tv&lt;br /&gt;
agents.media&lt;br /&gt;
ahrefs.com&lt;br /&gt;
ai-chat.bsg.brave.com&lt;br /&gt;
ai.com&lt;br /&gt;
aircanada.com&lt;br /&gt;
akc.org&lt;br /&gt;
allegro.pl&lt;br /&gt;
alphacoders.com&lt;br /&gt;
alza.hu&lt;br /&gt;
amazfitwatchfaces.com&lt;br /&gt;
amdm.ru&lt;br /&gt;
amedia.site&lt;br /&gt;
amnezia.org&lt;br /&gt;
amplitude.com&lt;br /&gt;
amx.com&lt;br /&gt;
analog.com&lt;br /&gt;
andrevi.ch&lt;br /&gt;
anidub.com&lt;br /&gt;
anilib.me&lt;br /&gt;
anilibria.org&lt;br /&gt;
anilibria.top&lt;br /&gt;
anilibria.tv&lt;br /&gt;
anilibria.uno&lt;br /&gt;
anilibria.wtf&lt;br /&gt;
animaunt.org&lt;br /&gt;
anime-portal.su&lt;br /&gt;
anime1.best&lt;br /&gt;
animebest.org&lt;br /&gt;
animedia.tv&lt;br /&gt;
animego.org&lt;br /&gt;
animespirit.ru&lt;br /&gt;
anistar.org&lt;br /&gt;
anistars.ru&lt;br /&gt;
annas-archive.org&lt;br /&gt;
ansys.com&lt;br /&gt;
anthropic.com&lt;br /&gt;
any.do&lt;br /&gt;
aol.com&lt;br /&gt;
api.jetbrains.ai&lt;br /&gt;
api.service-kp.com&lt;br /&gt;
api.theins.info&lt;br /&gt;
api.themoviedb.org&lt;br /&gt;
apkmirror.com&lt;br /&gt;
app.zerossl.com&lt;br /&gt;
arbat.media&lt;br /&gt;
arc.net&lt;br /&gt;
archive.midnightchannel.net&lt;br /&gt;
archive.ph&lt;br /&gt;
archiveofourown.org&lt;br /&gt;
arduino.cc&lt;br /&gt;
as6723.net&lt;br /&gt;
assets.heroku.com&lt;br /&gt;
atlassian.com&lt;br /&gt;
atn.ua&lt;br /&gt;
att.com&lt;br /&gt;
attachments.f95zone.to&lt;br /&gt;
atv4.dnskp.cc&lt;br /&gt;
audiobookbay.lu&lt;br /&gt;
augmentcode.com&lt;br /&gt;
autodesk.com&lt;br /&gt;
avira.com&lt;br /&gt;
azathabar.com&lt;br /&gt;
azattyq.org&lt;br /&gt;
babook.org&lt;br /&gt;
baginya.org&lt;br /&gt;
baikal-journal.ru&lt;br /&gt;
bato.to&lt;br /&gt;
bbc.co.uk&lt;br /&gt;
bbc.com&lt;br /&gt;
bbci.co.uk&lt;br /&gt;
bcbits.com&lt;br /&gt;
bell-sw.com&lt;br /&gt;
bellingcat.com&lt;br /&gt;
bestbuy.com&lt;br /&gt;
bestchange.ru&lt;br /&gt;
bihus.info&lt;br /&gt;
bitdefender.com&lt;br /&gt;
bitnami.com&lt;br /&gt;
blackseanews.net&lt;br /&gt;
blinkshot.io&lt;br /&gt;
bluehost.com&lt;br /&gt;
booktracker.org&lt;br /&gt;
boominfo.org&lt;br /&gt;
booth.pm&lt;br /&gt;
booth.pximg.net&lt;br /&gt;
bosch.com&lt;br /&gt;
boschaftermarket.com&lt;br /&gt;
boschautoparts.com&lt;br /&gt;
botnadzor.org&lt;br /&gt;
brawlstarsgame.com&lt;br /&gt;
bricklink.com&lt;br /&gt;
broadcom.com&lt;br /&gt;
broncosportforum.com&lt;br /&gt;
bt4gprx.com&lt;br /&gt;
btdig.com&lt;br /&gt;
btod.com&lt;br /&gt;
buanzo.org&lt;br /&gt;
buf.build&lt;br /&gt;
bufferbloat.net&lt;br /&gt;
builds.parsec.app&lt;br /&gt;
buymeacoffee.com&lt;br /&gt;
byteoversea.com&lt;br /&gt;
canva.com&lt;br /&gt;
canva.dev&lt;br /&gt;
capacitorjs.com&lt;br /&gt;
capcut.com&lt;br /&gt;
carnegieendowment.org&lt;br /&gt;
carrefouruae.com&lt;br /&gt;
cats.com&lt;br /&gt;
cbilling.eu&lt;br /&gt;
cbilling.vip&lt;br /&gt;
cdn.web-platform.io&lt;br /&gt;
cdnbunny.org&lt;br /&gt;
cdninstagram.com&lt;br /&gt;
cdromance.org&lt;br /&gt;
cdw.com&lt;br /&gt;
censor.net&lt;br /&gt;
censortracker.org&lt;br /&gt;
chaos.com&lt;br /&gt;
chat.com&lt;br /&gt;
chat.openai.com.cdn.cloudflare.net&lt;br /&gt;
chatgpt.com&lt;br /&gt;
chaturbate.com&lt;br /&gt;
chenbro.com&lt;br /&gt;
cherta.media&lt;br /&gt;
chess.com&lt;br /&gt;
chesscomfiles.com&lt;br /&gt;
chub.ai&lt;br /&gt;
circlecrewpinkcrowd.com&lt;br /&gt;
cisco.com&lt;br /&gt;
cisecurity.org&lt;br /&gt;
citrix.com&lt;br /&gt;
clamav.net&lt;br /&gt;
clashofclans.com&lt;br /&gt;
clashroyaleapp.com&lt;br /&gt;
claude.ai&lt;br /&gt;
clevelandclinic.org&lt;br /&gt;
clickup.com&lt;br /&gt;
clip.opus.pro&lt;br /&gt;
cloudtorrents.com&lt;br /&gt;
cms-twdigitalassets.com&lt;br /&gt;
cnd2exp.online&lt;br /&gt;
cock.li&lt;br /&gt;
code.gist.build&lt;br /&gt;
codecguide.com&lt;br /&gt;
codeium.com&lt;br /&gt;
codelinaro.org&lt;br /&gt;
coingate.com&lt;br /&gt;
coinpayments.net&lt;br /&gt;
coinsbee.com&lt;br /&gt;
coldfilm.city&lt;br /&gt;
coldfilm.ink&lt;br /&gt;
coldfilm.xyz&lt;br /&gt;
colta.ru&lt;br /&gt;
community.sophos.com&lt;br /&gt;
connect.ngrok-agent.com&lt;br /&gt;
contabo.com&lt;br /&gt;
coomer.su&lt;br /&gt;
copilot.microsoft.com&lt;br /&gt;
corsair.com&lt;br /&gt;
coursera.org&lt;br /&gt;
cpu-monkey.com&lt;br /&gt;
credly.com&lt;br /&gt;
croxyproxy.com&lt;br /&gt;
crunchyroll.com&lt;br /&gt;
csskor.ill.in.ua&lt;br /&gt;
cub.red&lt;br /&gt;
currenttime.tv&lt;br /&gt;
cursorinfo.co.il&lt;br /&gt;
cvedetails.com&lt;br /&gt;
cyberghostvpn.com&lt;br /&gt;
cyxymu.info&lt;br /&gt;
daemon-tools.cc&lt;br /&gt;
dailylviv.com&lt;br /&gt;
dailymotion.com&lt;br /&gt;
danbooru.donmai.us&lt;br /&gt;
darthsternie.net&lt;br /&gt;
data-cdn.mbamupdates.com&lt;br /&gt;
data.cline.bot&lt;br /&gt;
deckbrew.xyz&lt;br /&gt;
decrypt.day&lt;br /&gt;
deepl.com&lt;br /&gt;
deepstatemap.live&lt;br /&gt;
deezer.com&lt;br /&gt;
delfi.lt&lt;br /&gt;
delfi.lv&lt;br /&gt;
delivery.2d.net.co&lt;br /&gt;
dell.com&lt;br /&gt;
dellcdn.com&lt;br /&gt;
depositphotos.com&lt;br /&gt;
designer.microsoft.com&lt;br /&gt;
designify.com&lt;br /&gt;
developer.nvidia.com&lt;br /&gt;
devexpress.com&lt;br /&gt;
deviantart.com&lt;br /&gt;
diabrowser.com&lt;br /&gt;
digash.live&lt;br /&gt;
digikey.com&lt;br /&gt;
digitalcontent.sky&lt;br /&gt;
digitalocean.com&lt;br /&gt;
discord.tools&lt;br /&gt;
discours.io&lt;br /&gt;
disctech.com&lt;br /&gt;
disneyplus.com&lt;br /&gt;
dlpsgame.com&lt;br /&gt;
docs.liquibase.com&lt;br /&gt;
document360.com&lt;br /&gt;
document360.io&lt;br /&gt;
dorama.live&lt;br /&gt;
doramalive.ru&lt;br /&gt;
doramy.club&lt;br /&gt;
dovod.online&lt;br /&gt;
download3.omnissa.com&lt;br /&gt;
doxa.team&lt;br /&gt;
dpidetector.org&lt;br /&gt;
dreamhost.com&lt;br /&gt;
dreamina.capcut.com&lt;br /&gt;
dub.sh&lt;br /&gt;
ducati.com&lt;br /&gt;
dumka.media&lt;br /&gt;
dw.com&lt;br /&gt;
dyson.com&lt;br /&gt;
e-hentai.org&lt;br /&gt;
e621.net&lt;br /&gt;
easydmarc.com&lt;br /&gt;
echofm.online&lt;br /&gt;
edemtv.me&lt;br /&gt;
editorx.com&lt;br /&gt;
edu-cisco.org&lt;br /&gt;
ef.com&lt;br /&gt;
ef.edu&lt;br /&gt;
eggertspiele.de&lt;br /&gt;
ej.ru&lt;br /&gt;
ekhokavkaza.com&lt;br /&gt;
element14.com&lt;br /&gt;
elements.envato.com&lt;br /&gt;
elevenlabs.io&lt;br /&gt;
elgato.com&lt;br /&gt;
eneba.com&lt;br /&gt;
epidemz.net.co&lt;br /&gt;
eporner.com&lt;br /&gt;
espreso.tv&lt;br /&gt;
etahub.com&lt;br /&gt;
etsy.com&lt;br /&gt;
euronews.com&lt;br /&gt;
euroradio.fm&lt;br /&gt;
eutrp.eu&lt;br /&gt;
everand.com&lt;br /&gt;
exchanger.bits.media&lt;br /&gt;
exler.ru&lt;br /&gt;
expandrive.com&lt;br /&gt;
expres.online&lt;br /&gt;
extremetech.com&lt;br /&gt;
f1.com&lt;br /&gt;
f95-zone.to&lt;br /&gt;
facebook.com&lt;br /&gt;
facebook.net&lt;br /&gt;
fansly.com&lt;br /&gt;
fast-torrent.club&lt;br /&gt;
fast.com&lt;br /&gt;
fastpic.org&lt;br /&gt;
fb.com&lt;br /&gt;
fbcdn.net&lt;br /&gt;
fbsbx.com&lt;br /&gt;
ficbook.net&lt;br /&gt;
filebin.net&lt;br /&gt;
filmitorrent.net&lt;br /&gt;
filmix.ac&lt;br /&gt;
filmix.biz&lt;br /&gt;
filmix.day&lt;br /&gt;
filmix.fan&lt;br /&gt;
filmix.fm&lt;br /&gt;
filmix.la&lt;br /&gt;
flibusta.is&lt;br /&gt;
flibusta.net&lt;br /&gt;
flipboard.com&lt;br /&gt;
flir.com&lt;br /&gt;
flir.eu&lt;br /&gt;
flisland.net&lt;br /&gt;
flourish.studio&lt;br /&gt;
fls.guru&lt;br /&gt;
fluke.com&lt;br /&gt;
flukenetworks.com&lt;br /&gt;
flyertalk.com&lt;br /&gt;
fn-volga.ru&lt;br /&gt;
fonge.org&lt;br /&gt;
footballapi.pulselive.com&lt;br /&gt;
force-user-content.com&lt;br /&gt;
force.com&lt;br /&gt;
forklog.com&lt;br /&gt;
formula1.com&lt;br /&gt;
fortanga.org&lt;br /&gt;
forum.netgate.com&lt;br /&gt;
forum.ru-board.com&lt;br /&gt;
foxnews.com&lt;br /&gt;
framer.com&lt;br /&gt;
freedomletters.org&lt;br /&gt;
freeimages.com&lt;br /&gt;
freemedia.io&lt;br /&gt;
freeones.com&lt;br /&gt;
fxnetworks.com&lt;br /&gt;
g2a.com&lt;br /&gt;
gagadget.com&lt;br /&gt;
gamedistribution.com&lt;br /&gt;
gamesrepack.com&lt;br /&gt;
gamestop.com&lt;br /&gt;
gaming.amazon.com&lt;br /&gt;
geforcenow.com&lt;br /&gt;
gelbooru.com&lt;br /&gt;
genspark.ai&lt;br /&gt;
geolocation.onetrust.com&lt;br /&gt;
germania.one&lt;br /&gt;
getoutline.com&lt;br /&gt;
getoutline.org&lt;br /&gt;
gfn.am&lt;br /&gt;
ghostrc.game.idtech.services&lt;br /&gt;
gifyu.com&lt;br /&gt;
git.new&lt;br /&gt;
glavred.info&lt;br /&gt;
glavred.net&lt;br /&gt;
global.fncstatic.com&lt;br /&gt;
global.platform.seconddinnertech.com&lt;br /&gt;
glpals.com&lt;br /&gt;
godaddy.com&lt;br /&gt;
gofile.io&lt;br /&gt;
gofundme.com&lt;br /&gt;
golosameriki.com&lt;br /&gt;
gonitro.com&lt;br /&gt;
goodreads.com&lt;br /&gt;
&lt;br /&gt;
gpsonextra.net&lt;br /&gt;
gpu-monkey.com&lt;br /&gt;
gql.twitch.tv&lt;br /&gt;
gr-assets.com&lt;br /&gt;
grafana.com&lt;br /&gt;
grani.ru&lt;br /&gt;
graty.me&lt;br /&gt;
graylog.org&lt;br /&gt;
grizzlysms.com&lt;br /&gt;
grok.com&lt;br /&gt;
groq.com&lt;br /&gt;
groupon.com&lt;br /&gt;
guilded.gg&lt;br /&gt;
gulagu.net&lt;br /&gt;
habr.com&lt;br /&gt;
hackernoon.com&lt;br /&gt;
hackmd.io&lt;br /&gt;
halooglasi.com&lt;br /&gt;
hashicorp.com&lt;br /&gt;
haydaygame.com&lt;br /&gt;
hbomax.com&lt;br /&gt;
hc-ping.com&lt;br /&gt;
hchk.io&lt;br /&gt;
hd-rezka.tv&lt;br /&gt;
hdkinoteatr.com&lt;br /&gt;
hdrezka.ac&lt;br /&gt;
hdrezka.ag&lt;br /&gt;
hdrezka.app&lt;br /&gt;
hdrezka.me&lt;br /&gt;
hdrezka.tech&lt;br /&gt;
hdrezka.tv&lt;br /&gt;
hdrzk.org&lt;br /&gt;
hdstudio.org&lt;br /&gt;
healthline.com&lt;br /&gt;
hentai-foundry.com&lt;br /&gt;
herokucdn.com&lt;br /&gt;
hetzner.com&lt;br /&gt;
hitomi.la&lt;br /&gt;
hollisterco.com&lt;br /&gt;
holod.media&lt;br /&gt;
home-connect.com&lt;br /&gt;
home.by.me&lt;br /&gt;
hostgator.com&lt;br /&gt;
hostinger.com&lt;br /&gt;
hotels.com&lt;br /&gt;
housebrand.com&lt;br /&gt;
hqporner.com&lt;br /&gt;
hromadske.ua&lt;br /&gt;
hs.fi&lt;br /&gt;
htmhell.dev&lt;br /&gt;
hume.ai&lt;br /&gt;
hybrid-analysis.com&lt;br /&gt;
i.sakh.com&lt;br /&gt;
ibm.com&lt;br /&gt;
ibytedtos.com&lt;br /&gt;
idelreal.org&lt;br /&gt;
iditelesombase.org&lt;br /&gt;
iedb.org&lt;br /&gt;
ig.me&lt;br /&gt;
ign.com&lt;br /&gt;
iherb.com&lt;br /&gt;
iichan.hk&lt;br /&gt;
ikea.com&lt;br /&gt;
ilook.tv&lt;br /&gt;
image.tmdb.org&lt;br /&gt;
imgburn.com&lt;br /&gt;
imgur.com&lt;br /&gt;
important-stories.com&lt;br /&gt;
indeed.com&lt;br /&gt;
indiehackers.com&lt;br /&gt;
infineon.com&lt;br /&gt;
insearch.site&lt;br /&gt;
instagram.com&lt;br /&gt;
install.launcher.omniverse.nvidia.com&lt;br /&gt;
intel.com&lt;br /&gt;
intel.de&lt;br /&gt;
intel.nl&lt;br /&gt;
intelix.sophos.com&lt;br /&gt;
interactivebrokers.co.uk&lt;br /&gt;
interfax.com.ua&lt;br /&gt;
internalfb.com&lt;br /&gt;
intuit.com&lt;br /&gt;
intuitibits.com&lt;br /&gt;
ionos.com&lt;br /&gt;
iptv.online&lt;br /&gt;
is.fi&lt;br /&gt;
istories.media&lt;br /&gt;
itch.io&lt;br /&gt;
itninja.com&lt;br /&gt;
itsmycity.ru&lt;br /&gt;
jamf.com&lt;br /&gt;
jellyfin.org&lt;br /&gt;
jetbrains.com&lt;br /&gt;
jetbrains.space&lt;br /&gt;
jetkvm.com&lt;br /&gt;
jskor.ill.in.ua&lt;br /&gt;
jut-su.net&lt;br /&gt;
jut.su&lt;br /&gt;
jw-russia.org&lt;br /&gt;
jw.org&lt;br /&gt;
kaktus.media&lt;br /&gt;
kaleido.ai&lt;br /&gt;
kamatera.com&lt;br /&gt;
kara.su&lt;br /&gt;
kasparov.ru&lt;br /&gt;
kavkaz-uzel.eu&lt;br /&gt;
kavkazr.com&lt;br /&gt;
kemono.su&lt;br /&gt;
keysight.com&lt;br /&gt;
kilo.ai&lt;br /&gt;
kilocode.ai&lt;br /&gt;
kino.pub&lt;br /&gt;
kinogo.ec&lt;br /&gt;
kinogo.la&lt;br /&gt;
kinogo.uk&lt;br /&gt;
kinopub.me&lt;br /&gt;
kinovod.net&lt;br /&gt;
kinozal.guru&lt;br /&gt;
kinozal.me&lt;br /&gt;
kinozal.tv&lt;br /&gt;
kinozaltv.life&lt;br /&gt;
klarna.com&lt;br /&gt;
kmail-lists.com&lt;br /&gt;
knaben.xyz&lt;br /&gt;
knews.kg&lt;br /&gt;
knowyourmeme.com&lt;br /&gt;
kolsar.org&lt;br /&gt;
kor.ill.in.ua&lt;br /&gt;
korrespondent.net&lt;br /&gt;
kovcheg.live&lt;br /&gt;
krymr.com&lt;br /&gt;
kupujemprodajem.com&lt;br /&gt;
kym-cdn.com&lt;br /&gt;
lambdalabs.com&lt;br /&gt;
lamcdn.net&lt;br /&gt;
langdock.com&lt;br /&gt;
lantern.io&lt;br /&gt;
latestmodapks.com&lt;br /&gt;
ldoceonline.com&lt;br /&gt;
le-production.tv&lt;br /&gt;
leafletjs.com&lt;br /&gt;
legalshield.com&lt;br /&gt;
lgeapi.com&lt;br /&gt;
lgthinq.com&lt;br /&gt;
lib.rus.ec&lt;br /&gt;
libgen.li&lt;br /&gt;
licdn.com&lt;br /&gt;
lidarr.audio&lt;br /&gt;
lifehacker.com&lt;br /&gt;
liga.net&lt;br /&gt;
lightning.ai&lt;br /&gt;
linear.app&lt;br /&gt;
lingq.com&lt;br /&gt;
linkedin.com&lt;br /&gt;
linktr.ee&lt;br /&gt;
livetv.sx&lt;br /&gt;
liveuamap.com&lt;br /&gt;
locals.md&lt;br /&gt;
lolz.guru&lt;br /&gt;
lostfilm.download&lt;br /&gt;
lostfilm.tv&lt;br /&gt;
lostfilm.tw&lt;br /&gt;
lostfilm.uno&lt;br /&gt;
lostfilmtv2.site&lt;br /&gt;
lu4.org&lt;br /&gt;
lucid.app&lt;br /&gt;
magaz.global&lt;br /&gt;
mail-api.proton.me&lt;br /&gt;
mailerlite.com&lt;br /&gt;
mailfence.com&lt;br /&gt;
mailinator.com&lt;br /&gt;
mailo.com&lt;br /&gt;
make.com&lt;br /&gt;
malwarebytes.com&lt;br /&gt;
mangadex.org&lt;br /&gt;
mangahub.ru&lt;br /&gt;
mangapark.net&lt;br /&gt;
manus.im&lt;br /&gt;
manybooks.net&lt;br /&gt;
manyvids.com&lt;br /&gt;
marvelsnap.com&lt;br /&gt;
mashable.com&lt;br /&gt;
mattermost.com&lt;br /&gt;
max.com&lt;br /&gt;
mbk-news.appspot.com&lt;br /&gt;
mediazona.ca&lt;br /&gt;
medicalnewstoday.com&lt;br /&gt;
medium.com&lt;br /&gt;
meduza.io&lt;br /&gt;
mega.nz&lt;br /&gt;
megapeer.ru&lt;br /&gt;
megapeer.vip&lt;br /&gt;
merezha.co&lt;br /&gt;
meta.com&lt;br /&gt;
metacritic.com&lt;br /&gt;
metal-archives.com&lt;br /&gt;
metla.press&lt;br /&gt;
metopera.org&lt;br /&gt;
middlewareinventory.com&lt;br /&gt;
mignews.com&lt;br /&gt;
mintmobile.com&lt;br /&gt;
miracleptr.wordpress.com&lt;br /&gt;
mirror.jellyfin.org&lt;br /&gt;
mixcloud.com&lt;br /&gt;
modrinth.com&lt;br /&gt;
mongodb.com&lt;br /&gt;
monolisa.dev&lt;br /&gt;
monoprice.com&lt;br /&gt;
more.fm&lt;br /&gt;
moscowtimes.ru&lt;br /&gt;
mouser.com&lt;br /&gt;
mouser.fi&lt;br /&gt;
mssg.me&lt;br /&gt;
mullvad.net&lt;br /&gt;
multporn.net&lt;br /&gt;
muscdn.com&lt;br /&gt;
musical.ly&lt;br /&gt;
musicbrainz.org&lt;br /&gt;
musixmatch.com&lt;br /&gt;
mw2.wiki&lt;br /&gt;
myanimelist.net&lt;br /&gt;
mydoramy.club&lt;br /&gt;
myheritage.com&lt;br /&gt;
myjetbrains.com&lt;br /&gt;
myparallels.com&lt;br /&gt;
myqrcode.com&lt;br /&gt;
navalny.com&lt;br /&gt;
nba.com&lt;br /&gt;
neformat.com.ua&lt;br /&gt;
neo4j.com&lt;br /&gt;
netacad.com&lt;br /&gt;
netapp.com&lt;br /&gt;
netflix.ca&lt;br /&gt;
netflix.com&lt;br /&gt;
netflix.net&lt;br /&gt;
netflixinvestor.com&lt;br /&gt;
netflixtechblog.com&lt;br /&gt;
netlify.com&lt;br /&gt;
networksolutions.com&lt;br /&gt;
new.abb.com&lt;br /&gt;
newark.com&lt;br /&gt;
newsroom.porsche.com&lt;br /&gt;
newsru.co.il&lt;br /&gt;
newsru.com&lt;br /&gt;
newtimes.ru&lt;br /&gt;
nexus-cdn.com&lt;br /&gt;
nfl.com&lt;br /&gt;
nflxext.com&lt;br /&gt;
nflximg.com&lt;br /&gt;
nflximg.net&lt;br /&gt;
nflxsearch.net&lt;br /&gt;
nflxso.net&lt;br /&gt;
nflxvideo.net&lt;br /&gt;
ngrok.com&lt;br /&gt;
nhentai.com&lt;br /&gt;
nhentai.net&lt;br /&gt;
nhl.com&lt;br /&gt;
nih.gov&lt;br /&gt;
nike.com&lt;br /&gt;
nippon.com&lt;br /&gt;
nitropdf.com&lt;br /&gt;
nnmclub.to&lt;br /&gt;
nnmstatic.win&lt;br /&gt;
nordaccount.com&lt;br /&gt;
nordcdn.com&lt;br /&gt;
nordvpn.com&lt;br /&gt;
notepad-plus-plus.org&lt;br /&gt;
notion-emojis.s3-us-west-2.amazonaws.com&lt;br /&gt;
notion-static.com&lt;br /&gt;
notion.com&lt;br /&gt;
notion.new&lt;br /&gt;
notion.site&lt;br /&gt;
notion.so&lt;br /&gt;
novaline.fm&lt;br /&gt;
novaya.no&lt;br /&gt;
novayagazeta.eu&lt;br /&gt;
novayagazeta.ru&lt;br /&gt;
novyny.live&lt;br /&gt;
ntc.party&lt;br /&gt;
ntp.msn.com&lt;br /&gt;
nude-moon.org&lt;br /&gt;
nxp.com&lt;br /&gt;
oaistatic.com&lt;br /&gt;
oaiusercontent.com&lt;br /&gt;
obozrevatel.com&lt;br /&gt;
ocstore.com&lt;br /&gt;
oculus.com&lt;br /&gt;
ohmyswift.ru&lt;br /&gt;
oi.legal&lt;br /&gt;
okx.com&lt;br /&gt;
olx.ua&lt;br /&gt;
omnissa.com&lt;br /&gt;
omv-extras.org&lt;br /&gt;
onfastspring.com&lt;br /&gt;
onlinesim.io&lt;br /&gt;
onshape.com&lt;br /&gt;
open-vsx.org&lt;br /&gt;
openai.com&lt;br /&gt;
openh264.org&lt;br /&gt;
openmedia.io&lt;br /&gt;
openrouter.ai&lt;br /&gt;
opensanctions.org&lt;br /&gt;
opensea.io&lt;br /&gt;
opentrackr.org&lt;br /&gt;
openvsx.eclipsecontent.org&lt;br /&gt;
opposition-news.com&lt;br /&gt;
oracle.com&lt;br /&gt;
orbispatches.com&lt;br /&gt;
orcaslicer.com&lt;br /&gt;
ottplayer.tv&lt;br /&gt;
ovd.info&lt;br /&gt;
ovd.legal&lt;br /&gt;
ovd.news&lt;br /&gt;
ovdinfo.org&lt;br /&gt;
ozodi.org&lt;br /&gt;
paddle.com&lt;br /&gt;
paddlestatus.com&lt;br /&gt;
pages.dev&lt;br /&gt;
pandasecurity.com&lt;br /&gt;
pap.pl&lt;br /&gt;
paperpaper.io&lt;br /&gt;
paperpaper.ru&lt;br /&gt;
parallels.cn&lt;br /&gt;
parallels.com&lt;br /&gt;
parallels.net&lt;br /&gt;
parallelsaccess.com&lt;br /&gt;
paritydeals.com&lt;br /&gt;
patreon.com&lt;br /&gt;
patreonusercontent.com&lt;br /&gt;
patriot.dp.ua&lt;br /&gt;
pb.wtf&lt;br /&gt;
pcgamesn.com&lt;br /&gt;
pcmag.com&lt;br /&gt;
penguin.com&lt;br /&gt;
penguinrandomhouse.com&lt;br /&gt;
periscope.tv&lt;br /&gt;
pexels.com&lt;br /&gt;
phncdn.com&lt;br /&gt;
phncdn.com.sds.rncdn7.com&lt;br /&gt;
pimpletv.ru&lt;br /&gt;
pingdom.com&lt;br /&gt;
piratbit.top&lt;br /&gt;
pixabay.com&lt;br /&gt;
pkg-zone.com&lt;br /&gt;
pkgs.tailscale.com&lt;br /&gt;
platform.activestate.com&lt;br /&gt;
playboy.com&lt;br /&gt;
plugshare.com&lt;br /&gt;
polit.ru&lt;br /&gt;
politico.eu&lt;br /&gt;
politiken.dk&lt;br /&gt;
polymarket.com&lt;br /&gt;
pornhub.com&lt;br /&gt;
pornhub.org&lt;br /&gt;
pornolab.net&lt;br /&gt;
portal.lviv.ua&lt;br /&gt;
posle.media&lt;br /&gt;
posthog.com&lt;br /&gt;
postimees.ee&lt;br /&gt;
pravda.com&lt;br /&gt;
pravda.com.ua&lt;br /&gt;
premierleague.com&lt;br /&gt;
primevideo.com&lt;br /&gt;
primevue.org&lt;br /&gt;
privatekeys.pw&lt;br /&gt;
prnt.sc&lt;br /&gt;
production-openaicom-storage.azureedge.net&lt;br /&gt;
proekt.media&lt;br /&gt;
profitwell.com&lt;br /&gt;
prosleduetmedia.com&lt;br /&gt;
prosperopatches.com&lt;br /&gt;
prostovpn.org&lt;br /&gt;
proton.me&lt;br /&gt;
protonvpn.com&lt;br /&gt;
provereno.media&lt;br /&gt;
prowlarr.com&lt;br /&gt;
pscp.tv&lt;br /&gt;
psiphon.ca&lt;br /&gt;
psxhax.com&lt;br /&gt;
public.parsec.app&lt;br /&gt;
pvpessence.com&lt;br /&gt;
qdrant.io&lt;br /&gt;
qdrant.tech&lt;br /&gt;
qt.io&lt;br /&gt;
quadratichq.com&lt;br /&gt;
qualcomm.com&lt;br /&gt;
quicknode.com&lt;br /&gt;
quora.com&lt;br /&gt;
quora.com.cdn.cloudflare.net&lt;br /&gt;
qwant.com&lt;br /&gt;
r4.err.ee&lt;br /&gt;
radiojar.com&lt;br /&gt;
radiosakharov.org&lt;br /&gt;
radiosvoboda.org&lt;br /&gt;
rbc.ua&lt;br /&gt;
reactflow.dev&lt;br /&gt;
realbooru.com&lt;br /&gt;
realist.online&lt;br /&gt;
recraft.ai&lt;br /&gt;
reddxxx.com&lt;br /&gt;
redgifs.com&lt;br /&gt;
redis.io&lt;br /&gt;
redislabs.com&lt;br /&gt;
redshieldvpn.com&lt;br /&gt;
refactoring.guru&lt;br /&gt;
remna.st&lt;br /&gt;
remove.bg&lt;br /&gt;
render-state.to&lt;br /&gt;
rentry.co&lt;br /&gt;
rentry.org&lt;br /&gt;
repo.jellyfin.org&lt;br /&gt;
republic.ru&lt;br /&gt;
research.net&lt;br /&gt;
restream.io&lt;br /&gt;
reve.art&lt;br /&gt;
rezka-ua.in&lt;br /&gt;
rezka.ag&lt;br /&gt;
rezka.cc&lt;br /&gt;
rezka.fi&lt;br /&gt;
rezka.land&lt;br /&gt;
rezka.my&lt;br /&gt;
rezka.tv&lt;br /&gt;
rezkify.com&lt;br /&gt;
rezonans.media&lt;br /&gt;
rf.dobrochan.net&lt;br /&gt;
rferl.org&lt;br /&gt;
rima.media&lt;br /&gt;
riperam.org&lt;br /&gt;
riseup.net&lt;br /&gt;
roar-review.com&lt;br /&gt;
root-nation.com&lt;br /&gt;
roskomsvoboda.org&lt;br /&gt;
rublacklist.net&lt;br /&gt;
rule34.art&lt;br /&gt;
rule34.us&lt;br /&gt;
rule34.xxx&lt;br /&gt;
rus.delfi.ee&lt;br /&gt;
rus.jauns.lv&lt;br /&gt;
ruscryde.net&lt;br /&gt;
rustorka.com&lt;br /&gt;
rutor.info&lt;br /&gt;
rutor.is&lt;br /&gt;
rutor.org&lt;br /&gt;
rutracker.cc&lt;br /&gt;
rutracker.net&lt;br /&gt;
rutracker.org&lt;br /&gt;
rutracker.wiki&lt;br /&gt;
sakhalin.info&lt;br /&gt;
sakharovfoundation.org&lt;br /&gt;
salesforce-experience.com&lt;br /&gt;
salesforce-hub.com&lt;br /&gt;
salesforce-scrt.com&lt;br /&gt;
salesforce-setup.com&lt;br /&gt;
salesforce-sites.com&lt;br /&gt;
salesforce.com&lt;br /&gt;
salesforceiq.com&lt;br /&gt;
salesforceliveagent.com&lt;br /&gt;
sap.com&lt;br /&gt;
saverudata.net&lt;br /&gt;
schemas.microsoft.com&lt;br /&gt;
schemas.openxmlformats.org&lt;br /&gt;
sci-hub.se&lt;br /&gt;
sci-hub.st&lt;br /&gt;
scrollrevealjs.org&lt;br /&gt;
scryde.io&lt;br /&gt;
scryde.net&lt;br /&gt;
scryde.ru&lt;br /&gt;
scryde.world&lt;br /&gt;
scryde1.net&lt;br /&gt;
scryde10.net&lt;br /&gt;
scryde11.net&lt;br /&gt;
scryde12.net&lt;br /&gt;
scryde2.net&lt;br /&gt;
scryde3.net&lt;br /&gt;
scryde4.net&lt;br /&gt;
scryde5.net&lt;br /&gt;
scryde6.net&lt;br /&gt;
scryde7.net&lt;br /&gt;
scryde8.net&lt;br /&gt;
scryde9.net&lt;br /&gt;
sdxcentral.com&lt;br /&gt;
searchfloor.org&lt;br /&gt;
seasonvar.ru&lt;br /&gt;
selezen.org&lt;br /&gt;
semnasem.org&lt;br /&gt;
semrush.com&lt;br /&gt;
sentry.dev&lt;br /&gt;
sentry.io&lt;br /&gt;
sephora.com&lt;br /&gt;
servarr.com&lt;br /&gt;
severreal.org&lt;br /&gt;
sfdcopens.com&lt;br /&gt;
shikimori.me&lt;br /&gt;
shinyhardware.co.uk&lt;br /&gt;
shiza-project.com&lt;br /&gt;
shop.gameloft.com&lt;br /&gt;
showip.net&lt;br /&gt;
sibreal.org&lt;br /&gt;
signal.org&lt;br /&gt;
simplex.chat&lt;br /&gt;
simplex.im&lt;br /&gt;
simplix.info&lt;br /&gt;
singlekey-id.com&lt;br /&gt;
site.com&lt;br /&gt;
siteground.com&lt;br /&gt;
skat.media&lt;br /&gt;
sketchup.com&lt;br /&gt;
skiff.com&lt;br /&gt;
skladchik.com&lt;br /&gt;
sklatchiki.ru&lt;br /&gt;
sky.com&lt;br /&gt;
skycdp.com&lt;br /&gt;
slashlib.me&lt;br /&gt;
slavicsac.com&lt;br /&gt;
slifki.biz&lt;br /&gt;
smartbear.co&lt;br /&gt;
smartbear.com&lt;br /&gt;
smartdeploy.com&lt;br /&gt;
sms-activate.io&lt;br /&gt;
snapgene.com&lt;br /&gt;
sndcdn.com&lt;br /&gt;
snort.org&lt;br /&gt;
snyk.io&lt;br /&gt;
sobesednik.com&lt;br /&gt;
solarwinds.com&lt;br /&gt;
sonara.ai&lt;br /&gt;
sophos.com&lt;br /&gt;
sora.com&lt;br /&gt;
sotaproject.com&lt;br /&gt;
soundcloud.cloud&lt;br /&gt;
soundcloud.com&lt;br /&gt;
sovetromantica.com&lt;br /&gt;
spacelift.io&lt;br /&gt;
speedtest.net&lt;br /&gt;
spektr.press&lt;br /&gt;
spiceworks.com&lt;br /&gt;
spiegel.de&lt;br /&gt;
spitfireaudio.com&lt;br /&gt;
spotify.com&lt;br /&gt;
spreadthesign.com&lt;br /&gt;
sputnikipogrom.com&lt;br /&gt;
squadbustersgame.com&lt;br /&gt;
squareup.com&lt;br /&gt;
squietpc.com&lt;br /&gt;
startmail.com&lt;br /&gt;
startpage.com&lt;br /&gt;
static.lostfilm.top&lt;br /&gt;
statology.org&lt;br /&gt;
steamstat.info&lt;br /&gt;
strana.news&lt;br /&gt;
strana.today&lt;br /&gt;
strava.com&lt;br /&gt;
&lt;br /&gt;
supercell.com&lt;br /&gt;
supersliv.biz&lt;br /&gt;
support.anydesk.com&lt;br /&gt;
support.xerox.com&lt;br /&gt;
surfshark.com&lt;br /&gt;
surveymonkey.com&lt;br /&gt;
suspilne.media&lt;br /&gt;
svidomi.in.ua&lt;br /&gt;
svoboda.org&lt;br /&gt;
svoi.kr.ua&lt;br /&gt;
svtv.org&lt;br /&gt;
swagger.io&lt;br /&gt;
swapd.co&lt;br /&gt;
swissinfo.ch&lt;br /&gt;
synoforum.com&lt;br /&gt;
t-invariant.org&lt;br /&gt;
t.co&lt;br /&gt;
tableau.com&lt;br /&gt;
talosintelligence.com&lt;br /&gt;
tampermonkey.net&lt;br /&gt;
tayga.info&lt;br /&gt;
te-st.org&lt;br /&gt;
teamviewer.com&lt;br /&gt;
techbargains.com&lt;br /&gt;
telegraf.by&lt;br /&gt;
telegraf.news&lt;br /&gt;
telegram.org&lt;br /&gt;
telegraph.co.uk&lt;br /&gt;
telemetr.io&lt;br /&gt;
tellapart.com&lt;br /&gt;
tempmail.plus&lt;br /&gt;
temu.com&lt;br /&gt;
terraform.io&lt;br /&gt;
the-village.ru&lt;br /&gt;
theaudiodb.com&lt;br /&gt;
thebarentsobserver.com&lt;br /&gt;
thebell.io&lt;br /&gt;
theins.press&lt;br /&gt;
theins.ru&lt;br /&gt;
themoviedb.org&lt;br /&gt;
thepiratebay.org&lt;br /&gt;
theporndude.com&lt;br /&gt;
therarbg.to&lt;br /&gt;
thetruestory.news&lt;br /&gt;
thetvdb.com&lt;br /&gt;
threads.net&lt;br /&gt;
threema.ch&lt;br /&gt;
ti.com&lt;br /&gt;
tidal.com&lt;br /&gt;
tik-tokapi.com&lt;br /&gt;
tiktok.com&lt;br /&gt;
tiktokcdn-eu.com&lt;br /&gt;
tiktokcdn-us.com&lt;br /&gt;
tiktokcdn.com&lt;br /&gt;
tiktokd.net&lt;br /&gt;
tiktokd.org&lt;br /&gt;
tiktokv.com&lt;br /&gt;
tiktokv.eu&lt;br /&gt;
tiktokv.us&lt;br /&gt;
tiktokw.us&lt;br /&gt;
timberland.de&lt;br /&gt;
tiptop-vpn.com&lt;br /&gt;
tmdb-image-prod.b-cdn.net&lt;br /&gt;
tmdb.com&lt;br /&gt;
tmdb.org&lt;br /&gt;
toolbox.app&lt;br /&gt;
torproject.org&lt;br /&gt;
torrenteditor.com&lt;br /&gt;
torrentgalaxy.to&lt;br /&gt;
trae.ai&lt;br /&gt;
trailblazer.me&lt;br /&gt;
trailhead.com&lt;br /&gt;
transferwise.com&lt;br /&gt;
trellix.com&lt;br /&gt;
tria.ge&lt;br /&gt;
trueblackmetalradio.com&lt;br /&gt;
truthsocial.com&lt;br /&gt;
tsmc.com&lt;br /&gt;
ttwstatic.com&lt;br /&gt;
turbobit.net&lt;br /&gt;
tuta.com&lt;br /&gt;
tuta.io&lt;br /&gt;
tutanota.com&lt;br /&gt;
tvdevinfo.com&lt;br /&gt;
tvfreedom.io&lt;br /&gt;
tvrain.ru&lt;br /&gt;
tvrain.tv&lt;br /&gt;
tweetdeck.com&lt;br /&gt;
twimg.com&lt;br /&gt;
twin.me&lt;br /&gt;
twinlife-systems.com&lt;br /&gt;
twinme.com&lt;br /&gt;
twirpx.com&lt;br /&gt;
twitpic.com&lt;br /&gt;
twitter.biz&lt;br /&gt;
twitter.com&lt;br /&gt;
twitter.jp&lt;br /&gt;
twittercommunity.com&lt;br /&gt;
twitterflightschool.com&lt;br /&gt;
twitterinc.com&lt;br /&gt;
twitteroauth.com&lt;br /&gt;
twitterstat.us&lt;br /&gt;
twtrdns.net&lt;br /&gt;
twttr.com&lt;br /&gt;
twttr.net&lt;br /&gt;
twvid.com&lt;br /&gt;
typing.com&lt;br /&gt;
uaudio.com&lt;br /&gt;
ui.ill.in.ua&lt;br /&gt;
uizard.io&lt;br /&gt;
ukdevilz.com&lt;br /&gt;
ukr.net&lt;br /&gt;
ukr.radio&lt;br /&gt;
ukrtelcdn.net&lt;br /&gt;
unian.ua&lt;br /&gt;
unscreen.com&lt;br /&gt;
unsplash.com&lt;br /&gt;
upwork.com&lt;br /&gt;
urlr.me&lt;br /&gt;
usa.one&lt;br /&gt;
usatoday.com&lt;br /&gt;
usher.ttvnw.net&lt;br /&gt;
uupdump.net&lt;br /&gt;
v.vrv.co&lt;br /&gt;
vagrantcloud.com&lt;br /&gt;
veeam.com&lt;br /&gt;
verstka.media&lt;br /&gt;
vesma.one&lt;br /&gt;
vesma.today&lt;br /&gt;
vesty.co.il&lt;br /&gt;
viber.com&lt;br /&gt;
vice.com&lt;br /&gt;
vine.co&lt;br /&gt;
vip110.vip&lt;br /&gt;
vipergirls.to&lt;br /&gt;
visualcapitalist.com&lt;br /&gt;
vmware.com&lt;br /&gt;
vndb.org&lt;br /&gt;
voanews.com&lt;br /&gt;
vod-fy.crunchyrollcdn.com&lt;br /&gt;
voidboost.cc&lt;br /&gt;
volkswagen-classic-parts.com&lt;br /&gt;
vot-tak.tv&lt;br /&gt;
vpngate.net&lt;br /&gt;
vpngen.org&lt;br /&gt;
vpnlove.me&lt;br /&gt;
vpnpay.io&lt;br /&gt;
vyos.io&lt;br /&gt;
w.atwiki.jp&lt;br /&gt;
wa.me&lt;br /&gt;
walmart.com&lt;br /&gt;
watchguard.com&lt;br /&gt;
watermarkremover.io&lt;br /&gt;
wbgames.com&lt;br /&gt;
weather.com&lt;br /&gt;
webmd.com&lt;br /&gt;
webnames.ca&lt;br /&gt;
webtoons.com&lt;br /&gt;
weebly.com&lt;br /&gt;
welt.de&lt;br /&gt;
whatsapp.biz&lt;br /&gt;
whatsapp.com&lt;br /&gt;
whatsapp.net&lt;br /&gt;
widgetapp.stream&lt;br /&gt;
wiki.fextralife.com&lt;br /&gt;
wikidot.com&lt;br /&gt;
wilsoncenter.org&lt;br /&gt;
windows10spotlight.com&lt;br /&gt;
windsurf.com&lt;br /&gt;
wise.com&lt;br /&gt;
wonderzine.com&lt;br /&gt;
wpengine.com&lt;br /&gt;
wunderground.com&lt;br /&gt;
www3.corsair.com&lt;br /&gt;
x.ai&lt;br /&gt;
x.com&lt;br /&gt;
xhamster.com&lt;br /&gt;
xhamsterlive.com&lt;br /&gt;
xhcdn.com&lt;br /&gt;
xiaomi.eu&lt;br /&gt;
xnxx-cdn.com&lt;br /&gt;
xnxx-ru.com&lt;br /&gt;
xnxx.com&lt;br /&gt;
xnxx.net&lt;br /&gt;
xnxx.tv&lt;br /&gt;
xnxx3.com&lt;br /&gt;
xsts.auth.xboxlive.com&lt;br /&gt;
xtracloud.net&lt;br /&gt;
xv-ru.com&lt;br /&gt;
xvideos-cdn.com&lt;br /&gt;
xvideos.com&lt;br /&gt;
yande.re&lt;br /&gt;
yeggi.com&lt;br /&gt;
yle.fi&lt;br /&gt;
youtrack.cloud&lt;br /&gt;
yummyani.me&lt;br /&gt;
z.ai&lt;br /&gt;
zahav.ru&lt;br /&gt;
zapier.com&lt;br /&gt;
zaxid.net&lt;br /&gt;
zbigz.com&lt;br /&gt;
zedge.net&lt;br /&gt;
zendesk.com&lt;br /&gt;
zerkalo.io&lt;br /&gt;
zona.media&lt;br /&gt;
2fa.fb.tools&lt;br /&gt;
pytorch.org&lt;br /&gt;
rutracker.cc&lt;br /&gt;
uvnc.com&lt;br /&gt;
api.pwnedpasswords.com&lt;br /&gt;
pwnedpasswords.com&lt;br /&gt;
haveibeenpwned.com&lt;br /&gt;
nekoray.org&lt;br /&gt;
cursor.directory&lt;br /&gt;
fedoraproject.org&lt;br /&gt;
jdownloader.org&lt;br /&gt;
oii.la&lt;br /&gt;
sysdig.com&lt;br /&gt;
lizardbyte.dev&lt;br /&gt;
lego.com&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
google.com&lt;br /&gt;
lookerstudio.google.com&lt;br /&gt;
news.google.com&lt;br /&gt;
googleapis.com&lt;br /&gt;
suggestqueries.google.com&lt;br /&gt;
gemini.google.com&lt;br /&gt;
&lt;br /&gt;
2ip.ru&lt;br /&gt;
cline.bot&lt;br /&gt;
fragment.com&lt;br /&gt;
shikimori.one&lt;br /&gt;
thingiverse.com&lt;br /&gt;
rippersanime.info&lt;br /&gt;
forum.rippersanime.info&lt;br /&gt;
weloma.art&lt;br /&gt;
brave.com&lt;br /&gt;
search.brave.com&lt;br /&gt;
claude.com&lt;br /&gt;
roocode.com&lt;br /&gt;
unsloth.ai&lt;br /&gt;
sourceforge.net&lt;br /&gt;
trakt.tv&lt;br /&gt;
&lt;br /&gt;
statics.life&lt;br /&gt;
openstat.net&lt;br /&gt;
google-analytics.com&lt;br /&gt;
googletagmanager.com&lt;br /&gt;
torrenttip220.top&lt;br /&gt;
529072.xyz&lt;br /&gt;
asnet.pw&lt;br /&gt;
apachetorrent.com&lt;br /&gt;
bangumi.moe&lt;br /&gt;
bigfangroup.org&lt;br /&gt;
bitru.org&lt;br /&gt;
bitsearch.to&lt;br /&gt;
demonoid.nl&lt;br /&gt;
share.dmhy.org&lt;br /&gt;
extratorrent.st&lt;br /&gt;
eztvx.to&lt;br /&gt;
limontorrents.vip&lt;br /&gt;
www.frozen-layer.com&lt;br /&gt;
kickass.torrentbay.st&lt;br /&gt;
kickass.ws&lt;br /&gt;
limetorrents.fun&lt;br /&gt;
magnetcatcat.com&lt;br /&gt;
newstudio.tv&lt;br /&gt;
ww3-nortorrent.com&lt;br /&gt;
nyaa.si&lt;br /&gt;
torrentcore.xyz&lt;br /&gt;
torrenttip220.top&lt;br /&gt;
dontorrent.cash&lt;br /&gt;
electro-torrent.pl&lt;br /&gt;
extratorrent.ninjaproxy1.com&lt;br /&gt;
extratorrent.proxyninja.org&lt;br /&gt;
extratorrent.proxyninja.net&lt;br /&gt;
hdrtorrent.com&lt;br /&gt;
ilcorsaronero.link&lt;br /&gt;
magnetcatcat.com&lt;br /&gt;
clmclm.com&lt;br /&gt;
8800546.xyz&lt;br /&gt;
torrentbay.st&lt;br /&gt;
torrentsbay.org&lt;br /&gt;
unblockninja.com&lt;br /&gt;
ninjaproxy1.com&lt;br /&gt;
proxyninja.org&lt;br /&gt;
proxyninja.net&lt;br /&gt;
newstudio.tv&lt;br /&gt;
nekobt.to&lt;br /&gt;
moviesdvdr.co&lt;br /&gt;
nyaa.iss.ink&lt;br /&gt;
nyaa.land&lt;br /&gt;
nyaa.mom&lt;br /&gt;
nyaa.unblockninja.com&lt;br /&gt;
zktorrent.com&lt;br /&gt;
zamunda.rip&lt;br /&gt;
yts.bz&lt;br /&gt;
yts.unblockninja.com&lt;br /&gt;
yts.ninjaproxy1.com&lt;br /&gt;
yts.proxyninja.org&lt;br /&gt;
yts.proxyninja.net&lt;br /&gt;
yts.torrentbay.st&lt;br /&gt;
yts.torrentsbay.org&lt;br /&gt;
mania-torrent.org&lt;br /&gt;
cine-torrent.org&lt;br /&gt;
magnet-torrent.org&lt;br /&gt;
mega-torrent.cc&lt;br /&gt;
zone-torrent.com&lt;br /&gt;
warez-torrent.org&lt;br /&gt;
wolfmax4k.com&lt;br /&gt;
uztracker.net&lt;br /&gt;
uindex.org&lt;br /&gt;
ygg.gratis&lt;br /&gt;
torrenttip219.top&lt;br /&gt;
torrentsome238.com&lt;br /&gt;
torrentqq412.com&lt;br /&gt;
torrentegg87.com&lt;br /&gt;
torrentgalaxy.one&lt;br /&gt;
torrentgalaxy.info&lt;br /&gt;
torrentgalaxy.space&lt;br /&gt;
torrentdownload.info&lt;br /&gt;
torrentcore.xyz&lt;br /&gt;
www5.torrent9.to&lt;br /&gt;
www.torrentdownloads.pro&lt;br /&gt;
torrentdownloads.unblockninja.com&lt;br /&gt;
torrentdownloads.ninjaproxy1.com&lt;br /&gt;
torrentdownloads.proxyninja.org&lt;br /&gt;
torrentdownloads.proxyninja.net&lt;br /&gt;
therarbg.to&lt;br /&gt;
therarbg.com&lt;br /&gt;
therar.site&lt;br /&gt;
rarbg.unblockninja.com&lt;br /&gt;
rarbg.ninjaproxy1.com&lt;br /&gt;
rarbg.proxyninja.org&lt;br /&gt;
rarbg.proxyninja.net&lt;br /&gt;
rarbg.torrentbay.st&lt;br /&gt;
thepiratebay.org&lt;br /&gt;
solidtorrents.eu&lt;br /&gt;
kickasstorrents.bz&lt;br /&gt;
dolby.com&lt;br /&gt;
repo.webosbrew.org&lt;br /&gt;
qt.io&lt;br /&gt;
cursor.com&lt;br /&gt;
cursor.sh&lt;br /&gt;
cursorapi.com&lt;br /&gt;
duckdns.org&lt;br /&gt;
funpay.com&lt;br /&gt;
anidub.vip&lt;br /&gt;
anidub.shop&lt;br /&gt;
skillsmp.com&lt;br /&gt;
reactbits.dev&lt;br /&gt;
mediaarea.net&lt;br /&gt;
mkvtoolnix.download&lt;br /&gt;
excalidraw.com&lt;br /&gt;
claudeusercontent.com&lt;br /&gt;
kodi.tv&lt;br /&gt;
githubusercontent.com&lt;br /&gt;
qbittorrent.org&lt;br /&gt;
audioz.download&lt;br /&gt;
ishosting.com&lt;br /&gt;
netgrid.host&lt;br /&gt;
infomaniak.com&lt;br /&gt;
endcore.cloud&lt;br /&gt;
breakfastquay.com&lt;br /&gt;
pythonhosted.org&lt;br /&gt;
pypi.org&lt;br /&gt;
fbaipublicfiles.com&lt;br /&gt;
he.net&lt;br /&gt;
googleadservices.com&lt;br /&gt;
overwolf.com&lt;br /&gt;
shields.io&lt;br /&gt;
curseforge.com&lt;br /&gt;
bisecthosting.com&lt;br /&gt;
forgecdn.net&lt;br /&gt;
freshworks.com&lt;br /&gt;
digitaloceanspaces.com&lt;br /&gt;
adriver.ru&lt;br /&gt;
arxiv.org&lt;br /&gt;
thecvf.com&lt;br /&gt;
openreview.net&lt;br /&gt;
paperswithcode.com&lt;br /&gt;
meta.com&lt;br /&gt;
facebook.com&lt;br /&gt;
semanticscholar.org&lt;br /&gt;
neurips.cc&lt;br /&gt;
mlr.press&lt;br /&gt;
acm.org&lt;br /&gt;
ieee.org&lt;br /&gt;
researchhub.blog&lt;br /&gt;
uvnc.eu&lt;br /&gt;
iedem.tv&lt;br /&gt;
designingsound.org&lt;br /&gt;
pypi.org&lt;br /&gt;
yani.tv&lt;br /&gt;
animetop.info&lt;br /&gt;
microsoft.com&lt;br /&gt;
ibb.co&lt;br /&gt;
rutracker.cc&lt;br /&gt;
proxmox.com&lt;br /&gt;
findanime.me&lt;br /&gt;
gekko.ink&lt;br /&gt;
animakima.ru&lt;br /&gt;
cloudfront.net&lt;br /&gt;
jetbrains.team&lt;br /&gt;
cerebras.ai&lt;br /&gt;
canopywave.io&lt;br /&gt;
chkr.cc&lt;br /&gt;
madleets.me&lt;br /&gt;
fakepersongenerator.com&lt;br /&gt;
etawen.dev&lt;br /&gt;
onet.pl&lt;br /&gt;
dreamlab.pl&lt;br /&gt;
ocdn.eu&lt;br /&gt;
gemius.pl&lt;br /&gt;
downloadgameps3.net&lt;br /&gt;
rootz.so&lt;br /&gt;
akirabox.com&lt;br /&gt;
vik1ngfile.site&lt;br /&gt;
findani.me&lt;br /&gt;
yummyanime.tv&lt;br /&gt;
utsu.ru&lt;br /&gt;
jutsucdn.ru&lt;br /&gt;
jutsu66.ru&lt;br /&gt;
vost.pw&lt;br /&gt;
aff1best.org&lt;br /&gt;
anime1.best&lt;br /&gt;
deltarockme.com&lt;br /&gt;
animebestdub.com&lt;br /&gt;
animego.online&lt;br /&gt;
animego.me&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Шаг 2: Добавьте список в Podkop ====&lt;br /&gt;
&lt;br /&gt;
# Откройте веб-интерфейс Podkop: &#039;&#039;&#039;Services → Podkop&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Перейдите в раздел конфигурации секции (обычно &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
# Найдите параметр &#039;&#039;&#039;Custom Domain Lists&#039;&#039;&#039; (Список пользовательских доменов)&lt;br /&gt;
&lt;br /&gt;
# Убедитесь, что &#039;&#039;&#039;Тип пользовательского списка доменов&#039;&#039;&#039; установлен в &amp;lt;code&amp;gt;Текстовый список&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Скопируйте весь список из спойлера выше&lt;br /&gt;
&lt;br /&gt;
# Вставьте его в поле &#039;&#039;&#039;Список пользовательских доменов&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Нажмите &#039;&#039;&#039;Save &amp;amp; Apply&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Самостоятельная генерация списка (опционально) ===&lt;br /&gt;
Если вы хотите &#039;&#039;&#039;самостоятельно сгенерировать&#039;&#039;&#039; актуальный список или добавить свои домены, используйте наш скрипт.&lt;br /&gt;
&lt;br /&gt;
==== Требования ====&lt;br /&gt;
&lt;br /&gt;
* Python 3.6 или выше&lt;br /&gt;
&lt;br /&gt;
* 4 файла:&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;inside-raw.lst&amp;lt;/code&amp;gt; — актуальный список Russian inside&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;zapret-hosts-google.txt&amp;lt;/code&amp;gt; — скопируйте с роутера из &amp;lt;code&amp;gt;/opt/zapret/ipset/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;zapret-hosts-user.txt&amp;lt;/code&amp;gt; — скопируйте с роутера из &amp;lt;code&amp;gt;/opt/zapret/ipset/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;custom-user-list.txt&amp;lt;/code&amp;gt; — ваши дополнительные домены (по одному на строку)&lt;br /&gt;
&lt;br /&gt;
==== Исходный код скрипта ====&lt;br /&gt;
Создайте файл &amp;lt;code&amp;gt;generate_podkop_list.py&amp;lt;/code&amp;gt;:&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%; overflow:auto;&amp;quot;&amp;gt;&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Список доменов для Podkop (нажмите, чтобы развернуть)&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Domain List Generator for Podkop&lt;br /&gt;
&lt;br /&gt;
Merges inside-raw.lst with custom domains while removing duplicates from Zapret lists&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
def read_domains(filepath):&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Read domains from file, cleaning and normalizing them&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    domains = set()&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
&lt;br /&gt;
        with open(filepath, &#039;r&#039;, encoding=&#039;utf-8&#039;) as f:&lt;br /&gt;
&lt;br /&gt;
            for line in f:&lt;br /&gt;
&lt;br /&gt;
                # Remove comments, whitespace, and normalize&lt;br /&gt;
&lt;br /&gt;
                domain = line.split(&#039;#&#039;)[0].strip().lower()&lt;br /&gt;
&lt;br /&gt;
                if domain and not domain.startswith(&#039;[&#039;):&lt;br /&gt;
&lt;br /&gt;
                    # Remove trailing dots&lt;br /&gt;
&lt;br /&gt;
                    domain = domain.rstrip(&#039;.&#039;)&lt;br /&gt;
&lt;br /&gt;
                    domains.add(domain)&lt;br /&gt;
&lt;br /&gt;
    except FileNotFoundError:&lt;br /&gt;
&lt;br /&gt;
        print(f&amp;quot;Warning: File {filepath} not found, skipping&amp;quot;, file=sys.stderr)&lt;br /&gt;
&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
&lt;br /&gt;
        print(f&amp;quot;Error reading {filepath}: {e}&amp;quot;, file=sys.stderr)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    return domains&lt;br /&gt;
&lt;br /&gt;
def main():&lt;br /&gt;
&lt;br /&gt;
    # Define input files&lt;br /&gt;
&lt;br /&gt;
    inside_raw = Path(&#039;inside-raw.lst&#039;)&lt;br /&gt;
&lt;br /&gt;
    zapret_google = Path(&#039;zapret-hosts-google.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    zapret_user = Path(&#039;zapret-hosts-user.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    custom_list = Path(&#039;custom-user-list.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    output_file = Path(&#039;podkop-final-list.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;=== Podkop Domain List Generator ===\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Read all domain lists&lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading inside-raw.lst...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    inside_domains = read_domains(inside_raw)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(inside_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading zapret-hosts-google.txt...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    zapret_google_domains = read_domains(zapret_google)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(zapret_google_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading zapret-hosts-user.txt...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    zapret_user_domains = read_domains(zapret_user)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(zapret_user_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading custom-user-list.txt...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    custom_domains = read_domains(custom_list)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(custom_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Combine Zapret lists (domains to exclude from inside-raw)&lt;br /&gt;
&lt;br /&gt;
    zapret_combined = zapret_google_domains | zapret_user_domains&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\nTotal Zapret domains to exclude: {len(zapret_combined)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Remove Zapret domains from inside-raw&lt;br /&gt;
&lt;br /&gt;
    filtered_inside = inside_domains - zapret_combined&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;Inside-raw after filtering: {len(filtered_inside)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Combine filtered inside-raw with custom domains&lt;br /&gt;
&lt;br /&gt;
    final_domains = filtered_inside | custom_domains&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\nFinal domain count: {len(final_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Sort and save&lt;br /&gt;
&lt;br /&gt;
    sorted_domains = sorted(final_domains)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    with open(output_file, &#039;w&#039;, encoding=&#039;utf-8&#039;) as f:&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;# Podkop Domain List\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;# Generated automatically from inside-raw.lst (filtered) + custom domains\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(f&amp;quot;# Total: {len(sorted_domains)} domains\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;#\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;# Excludes domains already handled by Zapret:\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(f&amp;quot;#   - zapret-hosts-google.txt ({len(zapret_google_domains)} domains)\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(f&amp;quot;#   - zapret-hosts-user.txt ({len(zapret_user_domains)} domains)\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;#\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
        for domain in sorted_domains:&lt;br /&gt;
&lt;br /&gt;
            f.write(f&amp;quot;{domain}\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\n✓ Successfully created {output_file}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Ready to use in Podkop Custom Domain Lists&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
&lt;br /&gt;
    main()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Использование скрипта ====&lt;br /&gt;
&lt;br /&gt;
# Скачайте актуальный &amp;lt;code&amp;gt;inside-raw.lst&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wget https://community.antifilter.download/list/inside-raw.lst&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Скопируйте с роутера файлы &amp;lt;code&amp;gt;zapret-hosts-google.txt&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;zapret-hosts-user.txt&amp;lt;/code&amp;gt; из &amp;lt;code&amp;gt;/opt/zapret/ipset/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Создайте файл &amp;lt;code&amp;gt;custom-user-list.txt&amp;lt;/code&amp;gt; со своими доменами (если нужно)&lt;br /&gt;
&lt;br /&gt;
# Запустите скрипт:&lt;br /&gt;
&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
python3 generate_podkop_list.py&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Используйте полученный файл &amp;lt;code&amp;gt;podkop-final-list.txt&amp;lt;/code&amp;gt; в Podkop&lt;br /&gt;
&lt;br /&gt;
=== Настройка прокси/VPN ===&lt;br /&gt;
После добавления списка доменов настройте подключение к вашему VPN/прокси-серверу.&lt;br /&gt;
&lt;br /&gt;
В разделе &#039;&#039;&#039;Outbounds&#039;&#039;&#039; (в интерфейсе Podkop) настройте:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Connection Type&#039;&#039;&#039;: &amp;lt;code&amp;gt;proxy&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (в зависимости от вашего сервера)&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Proxy String&#039;&#039;&#039;: вставьте ссылку на ваш VPN/прокси (формат: &amp;lt;code&amp;gt;protocol://user:pass@server:port&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
# Или настройте &#039;&#039;&#039;Interface&#039;&#039;&#039; для WireGuard/OpenVPN&lt;br /&gt;
&lt;br /&gt;
=== Применение настроек ===&lt;br /&gt;
Нажмите &#039;&#039;&#039;Save &amp;amp; Restart&#039;&#039;&#039; для применения конфигурации Podkop.&lt;br /&gt;
&lt;br /&gt;
== Проверка работы ==&lt;br /&gt;
&lt;br /&gt;
=== Тестирование Zapret ===&lt;br /&gt;
&lt;br /&gt;
# Откройте YouTube — должен работать &#039;&#039;&#039;без VPN&#039;&#039;&#039; (регион РФ, русскоязычные рекомендации)&lt;br /&gt;
&lt;br /&gt;
# Проверьте Discord — должен открываться &#039;&#039;&#039;через Zapret&#039;&#039;&#039; (без VPN)&lt;br /&gt;
&lt;br /&gt;
=== Тестирование Podkop ===&lt;br /&gt;
&lt;br /&gt;
# Откройте домен из вашего списка Podkop (например, &amp;lt;code&amp;gt;rutracker.org&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
# Проверьте смену IP (через &amp;lt;code&amp;gt;2ip.ru&amp;lt;/code&amp;gt; или аналог) — должен показать IP вашего VPN-сервера&lt;br /&gt;
&lt;br /&gt;
=== Диагностика ===&lt;br /&gt;
Если что-то не работает:&lt;br /&gt;
&lt;br /&gt;
# Проверьте логи Zapret: &#039;&#039;&#039;Services → Zapret → Log Viewer&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Проверьте статус Podkop: &#039;&#039;&#039;Services → Podkop&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Убедитесь, что домен &#039;&#039;&#039;не находится одновременно&#039;&#039;&#039; в списках Zapret и Podkop&lt;br /&gt;
&lt;br /&gt;
== Заключение ==&lt;br /&gt;
Связка Zapret + Podkop — это оптимальное решение для обхода блокировок на OpenWRT, которое позволяет:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Использовать DPI-обход&#039;&#039;&#039; (Zapret) для потоковых сервисов (YouTube, Twitch, Discord, Steam)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Оставаться в регионе РФ&#039;&#039;&#039; на YouTube и других сервисах (получать русскоязычный контент и рекомендации)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Экономить VPN-трафик&#039;&#039;&#039; (95% трафика идёт через Zapret без шифрования)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Использовать VPN&#039;&#039;&#039; (Podkop) только для действительно заблокированных ресурсов (торрент-трекеры, специализированные сервисы)&lt;br /&gt;
&lt;br /&gt;
Таким образом, вы получаете &#039;&#039;&#039;лучшее из обоих миров&#039;&#039;&#039;: быстрый доступ к популярным сервисам без потери региона и полноценный VPN для всего остального.&lt;br /&gt;
&lt;br /&gt;
== Полезные ссылки ==&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/remittor/zapret-openwrt Zapret для OpenWRT (GitHub)]&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/itdoginfo/podkop Podkop (GitHub)]&lt;br /&gt;
&lt;br /&gt;
* [https://podkop.net/ Документация Podkop]&lt;br /&gt;
&lt;br /&gt;
* [https://community.antifilter.download/ Списки Russian inside]&lt;br /&gt;
&lt;br /&gt;
== Дополнительно ==&lt;br /&gt;
&lt;br /&gt;
=== Автоматизация обновления списков ===&lt;br /&gt;
Для автоматического обновления пользовательских списков доменов можно настроить cron-задачу, которая будет периодически скачивать обновлённый список и перезапускать сервисы.&lt;br /&gt;
&lt;br /&gt;
=== Мониторинг ===&lt;br /&gt;
Рекомендуется периодически проверять:&lt;br /&gt;
&lt;br /&gt;
* Логи Zapret на наличие ошибок&lt;br /&gt;
&lt;br /&gt;
* Статус подключения Podkop к VPN-серверу&lt;br /&gt;
&lt;br /&gt;
* Актуальность версий обоих сервисов&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%9E%D0%B1%D0%BB%D0%B0%D1%87%D0%BD%D1%8B%D0%B9_%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80_Nextcloud&amp;diff=1033</id>
		<title>Облачный сервер Nextcloud</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%9E%D0%B1%D0%BB%D0%B0%D1%87%D0%BD%D1%8B%D0%B9_%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80_Nextcloud&amp;diff=1033"/>
		<updated>2026-06-21T18:40:22Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Полная актуализация инструкции (перенос из черновика): среда, установка, кеш, обновление, Collabora, nginx, split-DNS, обслуживание&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Заметка|Страница переработана и актуализирована. Описывает развёртывание Nextcloud на отдельном сервере (LXC/VM) с внешним reverse-proxy &#039;&#039;&#039;nginx&#039;&#039;&#039;, офисным пакетом &#039;&#039;&#039;Collabora&#039;&#039;&#039; в Docker и локальным доступом по доменам через split-DNS. Все адреса, домены и пароли &#039;&#039;&#039;обезличены&#039;&#039;&#039; — подставляйте свои.}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nextcloud&#039;&#039;&#039; — self-hosted облако (аналог Google Drive / Яндекс.Диска): веб-доступ к файлам и клиенты синхронизации для всех платформ. Данные хранятся на вашем железе, под вашим контролем.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Архитектура ==&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Простыми словами: «приёмщик» запросов (nginx) и «рабочий» (сам Nextcloud) — это &#039;&#039;&#039;разные&#039;&#039;&#039; машины. nginx стоит на входе, шифрует трафик (HTTPS) и передаёт запросы внутрь. Так безопаснее и гибче: за одним nginx можно держать много сервисов.}}&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
graph TD&lt;br /&gt;
    A[&amp;quot;Браузер / клиент&amp;quot;] --&amp;gt; B[&amp;quot;Внешний nginx&amp;lt;br/&amp;gt;reverse-proxy, SSL&amp;lt;br/&amp;gt;напр. 192.168.3.47&amp;quot;]&lt;br /&gt;
    B --&amp;gt; C[&amp;quot;Сервер Nextcloud (LXC/VM)&amp;lt;br/&amp;gt;напр. 192.168.1.145&amp;quot;]&lt;br /&gt;
    C --&amp;gt; D[&amp;quot;Apache + PHP 8.3&amp;quot;]&lt;br /&gt;
    C --&amp;gt; E[&amp;quot;MariaDB&amp;quot;]&lt;br /&gt;
    C --&amp;gt; F[&amp;quot;Redis + APCu (кеш)&amp;quot;]&lt;br /&gt;
    C --&amp;gt; G[&amp;quot;Docker → Collabora CODE&amp;lt;br/&amp;gt;порт 9980&amp;quot;]&lt;br /&gt;
    C --&amp;gt; H[&amp;quot;Данные: /media/nextcloud&amp;quot;]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Компонент !! Назначение !! Пример адреса&lt;br /&gt;
|-&lt;br /&gt;
| Внешний nginx || HTTPS, reverse-proxy, точка входа || 192.168.3.47&lt;br /&gt;
|-&lt;br /&gt;
| Сервер Nextcloud || Apache+PHP, БД, кеш, Docker-офис || 192.168.1.145&lt;br /&gt;
|-&lt;br /&gt;
| Домен Nextcloud || для пользователей || nc.example.com&lt;br /&gt;
|-&lt;br /&gt;
| Домен Collabora || для офисного движка || collab.example.com&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
&lt;br /&gt;
* Контейнер LXC или ВМ с &#039;&#039;&#039;Ubuntu 24.04 LTS&#039;&#039;&#039; (минимум 2 ГБ RAM; для Collabora «рядом» — 4 ГБ).&lt;br /&gt;
* Хранилище для данных (отдельный диск/массив), смонтированное, например, в &amp;lt;code&amp;gt;/media/nextcloud&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Отдельный сервер/контейнер с &#039;&#039;&#039;nginx&#039;&#039;&#039; (reverse-proxy) — см. [[#Внешний nginx|раздел про nginx]].&lt;br /&gt;
* Доменные имена, указывающие на ваш публичный IP (например &amp;lt;code&amp;gt;nc.example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;collab.example.com&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Установка Nextcloud ==&lt;br /&gt;
&lt;br /&gt;
=== Системные пакеты ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Ставим всё необходимое: веб-сервер Apache, базу данных MariaDB, язык PHP с нужными модулями, кеш Redis и архиватор. Команда длинная — это нормально, она ставит всё разом.}}&lt;br /&gt;
&lt;br /&gt;
Обновляем список доступных пакетов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Устанавливаем веб-сервер, БД, PHP и сопутствующие модули:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt install -y apache2 mariadb-server libapache2-mod-php8.3 \&lt;br /&gt;
  php8.3 php8.3-{gd,mysql,curl,mbstring,intl,gmp,bcmath,xml,zip,imagick,apcu,redis} \&lt;br /&gt;
  redis-server unzip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Настройка PHP ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=По умолчанию PHP разрешает загружать совсем небольшие файлы и выделяет мало памяти. Для облака это нужно увеличить — иначе крупные файлы не загрузятся.}}&lt;br /&gt;
&lt;br /&gt;
Откройте конфиг PHP для Apache — &amp;lt;code&amp;gt;/etc/php/8.3/apache2/php.ini&amp;lt;/code&amp;gt; — и задайте значения (если параметр есть — поправьте, если нет — добавьте):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
memory_limit = 512M&lt;br /&gt;
upload_max_filesize = 10G&lt;br /&gt;
post_max_size = 10G&lt;br /&gt;
max_execution_time = 3600&lt;br /&gt;
max_input_time = 3600&lt;br /&gt;
output_buffering = Off&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=&amp;lt;code&amp;gt;upload_max_filesize&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;post_max_size&amp;lt;/code&amp;gt; задают максимальный размер загружаемого файла (поставьте сколько нужно). &amp;lt;code&amp;gt;output_buffering&amp;lt;/code&amp;gt; обязательно &amp;lt;code&amp;gt;Off&amp;lt;/code&amp;gt; — иначе большие загрузки могут обрываться. Изменения применятся после перезапуска Apache (сделаем ниже).}}&lt;br /&gt;
&lt;br /&gt;
=== База данных ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Nextcloud хранит служебные данные (пользователи, настройки, связи между файлами) в базе данных. Сейчас создадим &#039;&#039;&#039;отдельную&#039;&#039;&#039; базу и пользователя для неё. &#039;&#039;&#039;Запомните три значения&#039;&#039;&#039; — имя базы, имя пользователя и пароль: они понадобятся на следующем шаге, при первом запуске Nextcloud.}}&lt;br /&gt;
&lt;br /&gt;
Заходим в консоль MariaDB от имени администратора:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mariadb&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Создаём саму базу данных — назовём её &amp;lt;code&amp;gt;nextclouddb&amp;lt;/code&amp;gt; (с поддержкой эмодзи и любых символов):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE DATABASE nextclouddb CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Создаём пользователя &amp;lt;code&amp;gt;nextcloud&amp;lt;/code&amp;gt;, который будет обращаться к базе только с этого же сервера (&amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;). &#039;&#039;&#039;Вместо &amp;lt;code&amp;gt;&amp;lt;ВАШ_ПАРОЛЬ_БД&amp;gt;&amp;lt;/code&amp;gt; придумайте свой пароль и не потеряйте его&#039;&#039;&#039; — он понадобится при установке Nextcloud:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE USER &#039;nextcloud&#039;@&#039;localhost&#039; IDENTIFIED BY &#039;&amp;lt;ВАШ_ПАРОЛЬ_БД&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выдаём этому пользователю полные права на нашу базу (и только на неё):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
GRANT ALL PRIVILEGES ON nextclouddb.* TO &#039;nextcloud&#039;@&#039;localhost&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Применяем (сохраняем) выданные права — без этого они не вступят в силу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выходим из консоли базы данных:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
EXIT;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=Три значения — &#039;&#039;&#039;имя базы&#039;&#039;&#039; (&amp;lt;code&amp;gt;nextclouddb&amp;lt;/code&amp;gt;), &#039;&#039;&#039;пользователь&#039;&#039;&#039; (&amp;lt;code&amp;gt;nextcloud&amp;lt;/code&amp;gt;) и &#039;&#039;&#039;пароль&#039;&#039;&#039; — понадобятся на шаге [[#Первичная установка через occ|первого запуска Nextcloud]]: их вписывают в соответствующие поля. Держите их под рукой.}}&lt;br /&gt;
&lt;br /&gt;
=== Загрузка Nextcloud ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Скачиваем сам Nextcloud с официального сайта и распаковываем в папку веб-сервера &amp;lt;code&amp;gt;/var/www&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
Переходим в папку веб-сервера:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /var/www&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скачиваем последнюю версию ([https://nextcloud.com/install/ официальный сайт]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo wget https://download.nextcloud.com/server/releases/latest.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Распаковываем архив (появится папка &amp;lt;code&amp;gt;nextcloud&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo unzip latest.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Передаём папку во владение веб-серверу (пользователь &amp;lt;code&amp;gt;www-data&amp;lt;/code&amp;gt;) — иначе Nextcloud не сможет писать файлы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo chown -R www-data:www-data /var/www/nextcloud&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Настройка Apache ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Теперь объясним Apache, где лежит Nextcloud, разрешим ему свои правила (&amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt; — нужны для чистых ссылок) и научим определять реальные адреса пользователей, приходящих через nginx.}}&lt;br /&gt;
&lt;br /&gt;
Включаем необходимые модули Apache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo a2enmod rewrite headers env dir mime setenvif remoteip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Создаём конфиг сайта. Откройте новый файл &amp;lt;code&amp;gt;/etc/apache2/sites-available/nextcloud.conf&amp;lt;/code&amp;gt; и впишите (подставьте свой &amp;lt;code&amp;gt;ServerName&amp;lt;/code&amp;gt; и IP вашего nginx в &amp;lt;code&amp;gt;RemoteIPInternalProxy&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;apache&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;
  DocumentRoot /var/www/nextcloud/&lt;br /&gt;
  ServerName  nc.example.com&lt;br /&gt;
&lt;br /&gt;
  # определять реальный IP пользователя за nginx:&lt;br /&gt;
  RemoteIPHeader X-Forwarded-For&lt;br /&gt;
  RemoteIPInternalProxy 192.168.3.47/24&lt;br /&gt;
&lt;br /&gt;
  # безопасные cookie:&lt;br /&gt;
  Header always edit Set-Cookie ^(.*)$ &amp;quot;$1; Secure; HttpOnly; SameSite=Strict&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;Directory /var/www/nextcloud/&amp;gt;&lt;br /&gt;
    Require all granted&lt;br /&gt;
    AllowOverride All&lt;br /&gt;
    Options FollowSymLinks MultiViews&lt;br /&gt;
    LimitRequestBody 10737418240&lt;br /&gt;
    &amp;lt;IfModule mod_dav.c&amp;gt;&lt;br /&gt;
      Dav off&lt;br /&gt;
    &amp;lt;/IfModule&amp;gt;&lt;br /&gt;
  &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Внимание|1=&amp;lt;code&amp;gt;AllowOverride All&amp;lt;/code&amp;gt; обязателен — без него не работают &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt; Nextcloud, а значит сломаются и чистые ссылки, и часть защиты. &amp;lt;code&amp;gt;LimitRequestBody&amp;lt;/code&amp;gt; задаёт максимальный размер запроса (здесь 10 ГБ).}}&lt;br /&gt;
&lt;br /&gt;
Включаем наш сайт, отключаем дефолтный и перезапускаем Apache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo a2ensite nextcloud&lt;br /&gt;
sudo a2dissite 000-default&lt;br /&gt;
sudo systemctl restart apache2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Первичная установка через occ ===&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=&amp;lt;code&amp;gt;occ&amp;lt;/code&amp;gt; — это «пульт управления» Nextcloud из командной строки. Запускается от пользователя веб-сервера: &amp;lt;code&amp;gt;sudo -u www-data php occ ...&amp;lt;/code&amp;gt;. Установить можно и через браузер (мастер установки), но через команду — быстрее и нагляднее.}}&lt;br /&gt;
&lt;br /&gt;
Переходим в папку Nextcloud:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /var/www/nextcloud&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Запускаем установку. &#039;&#039;&#039;Подставьте свои значения&#039;&#039;&#039;: пароль БД (из шага выше), логин и пароль администратора, и путь к хранилищу данных:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ maintenance:install \&lt;br /&gt;
  --database mysql --database-name nextclouddb \&lt;br /&gt;
  --database-user nextcloud --database-pass &#039;&amp;lt;ВАШ_ПАРОЛЬ_БД&amp;gt;&#039; \&lt;br /&gt;
  --admin-user admin --admin-pass &#039;&amp;lt;ПАРОЛЬ_АДМИНА&amp;gt;&#039; \&lt;br /&gt;
  --data-dir /media/nextcloud&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=Здесь как раз и используются три значения из раздела про базу: &amp;lt;code&amp;gt;--database-name&amp;lt;/code&amp;gt; (имя базы), &amp;lt;code&amp;gt;--database-user&amp;lt;/code&amp;gt; (пользователь) и &amp;lt;code&amp;gt;--database-pass&amp;lt;/code&amp;gt; (пароль). А &amp;lt;code&amp;gt;--admin-user&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;--admin-pass&amp;lt;/code&amp;gt; — это логин и пароль, под которыми вы зайдёте в само облако через браузер.}}&lt;br /&gt;
&lt;br /&gt;
=== Ключевые параметры config.php ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Файл &amp;lt;code&amp;gt;/var/www/nextcloud/config/config.php&amp;lt;/code&amp;gt; — главный конфиг Nextcloud. Откройте его редактором и проверьте/добавьте параметры ниже (подставляя свои домен и IP).}}&lt;br /&gt;
&lt;br /&gt;
Разрешённые домены, по которым открывается ваше облако:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&#039;trusted_domains&#039; =&amp;gt; array(&#039;nc.example.com&#039;),&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Адрес, который Nextcloud использует в ссылках и фоновых задачах:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&#039;overwrite.cli.url&#039; =&amp;gt; &#039;https://nc.example.com&#039;,&lt;br /&gt;
&#039;overwriteprotocol&#039; =&amp;gt; &#039;https&#039;,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IP вашего внешнего nginx — чтобы Nextcloud правильно определял реальные адреса пользователей:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&#039;trusted_proxies&#039; =&amp;gt; array(&#039;192.168.3.47&#039;),&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Внимание|1=&amp;lt;code&amp;gt;trusted_proxies&amp;lt;/code&amp;gt; обязательно укажите на IP вашего nginx — иначе Nextcloud будет видеть всех пользователей под одним адресом (адресом прокси), и сломается защита от перебора паролей.}}&lt;br /&gt;
&lt;br /&gt;
Путь к хранилищу данных и регион телефонов (для контактов):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&#039;datadirectory&#039; =&amp;gt; &#039;/media/nextcloud&#039;,&lt;br /&gt;
&#039;default_phone_region&#039; =&amp;gt; &#039;RU&#039;,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Чистые ссылки (без index.php) ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=По умолчанию ссылки выглядят как &amp;lt;code&amp;gt;.../index.php/apps/files&amp;lt;/code&amp;gt;. Чтобы они были короткими и чистыми (&amp;lt;code&amp;gt;.../apps/files&amp;lt;/code&amp;gt;), включаем перезапись адресов. Работает за счёт модуля &amp;lt;code&amp;gt;mod_rewrite&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt; (мы их уже включили в разделе про Apache).}}&lt;br /&gt;
&lt;br /&gt;
Добавьте в &amp;lt;code&amp;gt;config.php&amp;lt;/code&amp;gt; строку:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&#039;htaccess.RewriteBase&#039; =&amp;gt; &#039;/&#039;,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Затем попросите Nextcloud сгенерировать правила перезаписи в файле &amp;lt;code&amp;gt;.htaccess&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /var/www/nextcloud&lt;br /&gt;
sudo -u www-data php occ maintenance:update:htaccess&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=После этого ссылки станут чистыми. Если вдруг сайт перестал открываться — значит не включён &amp;lt;code&amp;gt;mod_rewrite&amp;lt;/code&amp;gt; или в VirtualHost не стоит &amp;lt;code&amp;gt;AllowOverride All&amp;lt;/code&amp;gt; (см. раздел [[#Настройка Apache|Настройка Apache]]).}}&lt;br /&gt;
&lt;br /&gt;
== Кеш и производительность ==&lt;br /&gt;
&lt;br /&gt;
=== APCu + Redis ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Кеш — это «блокнот под рукой»: часто нужные данные держатся в памяти, а не считываются каждый раз с диска. APCu ускоряет локально, Redis отвечает за блокировки файлов и общий кеш. Это заметно ускоряет интерфейс.}}&lt;br /&gt;
&lt;br /&gt;
Устанавливаем APCu и Redis (если ещё не стоят):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt install -y php-apcu redis-server php-redis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Включаем APCu для командной строки (нужно для occ и фоновых задач):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &amp;quot;apc.enable_cli=1&amp;quot; | sudo tee /etc/php/8.3/mods-available/apcu-cli.ini&lt;br /&gt;
sudo ln -sf /etc/php/8.3/mods-available/apcu-cli.ini /etc/php/8.3/cli/conf.d/30-apcu-cli.ini&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Настраиваем Redis на работу через быстрый локальный «сокет». Откройте &amp;lt;code&amp;gt;/etc/redis/redis.conf&amp;lt;/code&amp;gt; и задайте:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
unixsocket /run/redis/redis-server.sock&lt;br /&gt;
unixsocketperm 770&lt;br /&gt;
maxmemory 128mb&lt;br /&gt;
maxmemory-policy noeviction&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Даём веб-серверу доступ к сокету Redis и перезапускаем его:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo usermod -aG redis www-data&lt;br /&gt;
sudo systemctl restart redis-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подключаем кеш в &amp;lt;code&amp;gt;config.php&amp;lt;/code&amp;gt; (APCu — локальный, Redis — блокировки и общий кеш):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&#039;memcache.local&#039; =&amp;gt; &#039;\\OC\\Memcache\\APCu&#039;,&lt;br /&gt;
&#039;memcache.distributed&#039; =&amp;gt; &#039;\\OC\\Memcache\\Redis&#039;,&lt;br /&gt;
&#039;memcache.locking&#039; =&amp;gt; &#039;\\OC\\Memcache\\Redis&#039;,&lt;br /&gt;
&#039;redis&#039; =&amp;gt; array(&#039;host&#039; =&amp;gt; &#039;/run/redis/redis-server.sock&#039;, &#039;port&#039; =&amp;gt; 0),&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Внимание|1=&#039;&#039;&#039;Не&#039;&#039;&#039; используйте memcached для &amp;lt;code&amp;gt;memcache.locking&amp;lt;/code&amp;gt; — блокировки файлов на нём ненадёжны. Для блокировок нужен именно Redis.}}&lt;br /&gt;
&lt;br /&gt;
=== Тюнинг под ограниченную память ===&lt;br /&gt;
&lt;br /&gt;
{{Опасно|1=Если сервер на 2 ГБ — главная беда это &#039;&#039;&#039;OOM&#039;&#039;&#039;: при пике нагрузки Apache съедает всю память, и система убивает MariaDB. Признак — в логах &amp;lt;code&amp;gt;nextcloud.log&amp;lt;/code&amp;gt; массовые «Connection refused», а в &amp;lt;code&amp;gt;journalctl -u mariadb&amp;lt;/code&amp;gt; — «killed by OOM killer». Лечится ограничением воркеров Apache (ниже).}}&lt;br /&gt;
&lt;br /&gt;
Ограничиваем число процессов Apache. Откройте &amp;lt;code&amp;gt;/etc/apache2/mods-available/mpm_prefork.conf&amp;lt;/code&amp;gt; и задайте:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;apache&amp;quot;&amp;gt;&lt;br /&gt;
ServerLimit             16&lt;br /&gt;
MaxRequestWorkers       12&lt;br /&gt;
MaxConnectionsPerChild  1000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Защищаем базу от убийства при нехватке памяти. Создайте файл &amp;lt;code&amp;gt;/etc/systemd/system/mariadb.service.d/oom.conf&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
[Service]&lt;br /&gt;
OOMScoreAdjust=-900&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Эта настройка говорит системе: «если памяти не хватит — убивай что угодно, только не базу данных». Жертвой станет лишний веб-процесс, а данные останутся целы.}}&lt;br /&gt;
&lt;br /&gt;
Применяем защиту:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl daemon-reload&lt;br /&gt;
sudo systemctl restart mariadb&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Настраиваем саму MariaDB под небольшую память. Создайте &amp;lt;code&amp;gt;/etc/mysql/mariadb.conf.d/90-nextcloud.cnf&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
[mysqld]&lt;br /&gt;
innodb_buffer_pool_size = 256M&lt;br /&gt;
tmp_table_size = 64M&lt;br /&gt;
max_heap_table_size = 64M&lt;br /&gt;
max_connections = 40&lt;br /&gt;
innodb_flush_log_at_trx_commit = 2&lt;br /&gt;
skip-name-resolve&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Настраиваем OPcache (ускоряет выполнение PHP). Создайте &amp;lt;code&amp;gt;/etc/php/8.3/apache2/conf.d/99-nextcloud-opcache.ini&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
opcache.enable=1&lt;br /&gt;
opcache.interned_strings_buffer=16&lt;br /&gt;
opcache.memory_consumption=256&lt;br /&gt;
opcache.max_accelerated_files=10000&lt;br /&gt;
opcache.save_comments=1&lt;br /&gt;
opcache.revalidate_freq=60&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Перезапускаем Apache, чтобы настройки применились:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl restart apache2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Фоновые задачи (cron) ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Nextcloud регулярно делает фоновую работу (уборка, уведомления, превью). Лучший способ — запускать её по расписанию через cron каждые 5 минут.}}&lt;br /&gt;
&lt;br /&gt;
Открываем расписание пользователя www-data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data crontab -e&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Добавляем строку:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
*/5 * * * * php -f /var/www/nextcloud/cron.php&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В настройках Nextcloud (Администрирование → Основные параметры) выберите режим фоновых задач «Cron».&lt;br /&gt;
&lt;br /&gt;
== Обновление Nextcloud ==&lt;br /&gt;
&lt;br /&gt;
{{Опасно|1=Мажорные версии &#039;&#039;&#039;нельзя перепрыгивать&#039;&#039;&#039;. Обновление строго по ступеням: 31 → 32 → 33 → 34. Прыжок через версию сломает базу данных. Перед каждым шагом — бэкап.}}&lt;br /&gt;
&lt;br /&gt;
=== Бэкап перед обновлением ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Всегда делайте резервную копию перед обновлением — если что-то пойдёт не так, сможете вернуться назад.}}&lt;br /&gt;
&lt;br /&gt;
Сохраняем базу данных в сжатый файл:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mysqldump -uroot --single-transaction nextclouddb | gzip &amp;gt; /root/nc-db-$(date +%F).sql.gz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраняем конфиг:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cp /var/www/nextcloud/config/config.php /root/config.php.bak&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Обновление одной ступени ===&lt;br /&gt;
&lt;br /&gt;
Включаем режим обслуживания (сайт станет недоступен на время):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /var/www/nextcloud&lt;br /&gt;
sudo -u www-data php occ maintenance:mode --on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скачиваем нужную версию и проверяем её контрольную сумму (целостность):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
wget https://download.nextcloud.com/server/releases/nextcloud-33.0.5.zip&lt;br /&gt;
wget https://download.nextcloud.com/server/releases/nextcloud-33.0.5.zip.sha256&lt;br /&gt;
sha256sum -c nextcloud-33.0.5.zip.sha256&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Распакуйте новую версию в отдельную папку, перенесите туда из старой папки каталог &amp;lt;code&amp;gt;config/&amp;lt;/code&amp;gt; и ваши сторонние приложения, затем замените старую папку новой. После этого запускаем обновление базы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ upgrade&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Добавляем недостающие индексы и колонки (ускоряет БД):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ db:add-missing-indices&lt;br /&gt;
sudo -u www-data php occ db:add-missing-columns&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выключаем режим обслуживания и перезапускаем Apache (сбросить кеш старого кода):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ maintenance:mode --off&lt;br /&gt;
sudo systemctl restart apache2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=Сторонние приложения (onlyoffice, music, keeweb и т.п.) Nextcloud обновляет из App Store автоматически в процессе &amp;lt;code&amp;gt;occ upgrade&amp;lt;/code&amp;gt; — &#039;&#039;&#039;если&#039;&#039;&#039; для целевой версии уже есть совместимый релиз. Перед мажорным апгрейдом проверьте совместимость на [https://apps.nextcloud.com apps.nextcloud.com].}}&lt;br /&gt;
&lt;br /&gt;
== Collabora Office (Docker) ==&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Collabora — это онлайн-редактор документов (как Google Docs). Чтобы он не мешал Nextcloud и имел свою изоляцию, его запускают в &#039;&#039;&#039;Docker&#039;&#039;&#039; рядом. Подключение — через &#039;&#039;&#039;отдельный домен&#039;&#039;&#039; collab.example.com.}}&lt;br /&gt;
&lt;br /&gt;
=== Установка Docker ===&lt;br /&gt;
&lt;br /&gt;
Устанавливаем Docker и включаем его автозапуск:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt install -y docker.io&lt;br /&gt;
sudo systemctl enable --now docker&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Внимание|1=Если сервер — это &#039;&#039;&#039;LXC-контейнер&#039;&#039;&#039;, Docker не запустит образы из-за AppArmor (им управляет хост, не контейнер). Решение — запускать с &amp;lt;code&amp;gt;--security-opt apparmor=unconfined&amp;lt;/code&amp;gt; (см. ниже). Контейнеру LXC также нужен nesting и нужные права (на хосте Proxmox: &amp;lt;code&amp;gt;pct set &amp;lt;id&amp;gt; -features nesting=1&amp;lt;/code&amp;gt;).}}&lt;br /&gt;
&lt;br /&gt;
=== Запуск Collabora CODE ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Запускаем движок Collabora в контейнере. Подставьте &#039;&#039;&#039;свой&#039;&#039;&#039; домен Nextcloud (в &amp;lt;code&amp;gt;aliasgroup1&amp;lt;/code&amp;gt;) и домен Collabora (в &amp;lt;code&amp;gt;server_name&amp;lt;/code&amp;gt;).}}&lt;br /&gt;
&lt;br /&gt;
Скачиваем образ и запускаем контейнер:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo docker run -d --name collabora --restart unless-stopped \&lt;br /&gt;
  -p 9980:9980 \&lt;br /&gt;
  --security-opt apparmor=unconfined \&lt;br /&gt;
  --security-opt seccomp=unconfined \&lt;br /&gt;
  --cap-add MKNOD \&lt;br /&gt;
  -e &#039;aliasgroup1=https://nc\.example\.com:443&#039; \&lt;br /&gt;
  -e &#039;server_name=collab.example.com&#039; \&lt;br /&gt;
  -e &#039;extra_params=--o:ssl.enable=false --o:ssl.termination=true&#039; \&lt;br /&gt;
  collabora/code&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=&amp;lt;code&amp;gt;aliasgroup1&amp;lt;/code&amp;gt; — доверенный адрес Nextcloud (точки экранированы, это регулярное выражение). &amp;lt;code&amp;gt;ssl.termination=true&amp;lt;/code&amp;gt; — SSL расшифровывает nginx, а движок работает по HTTP внутри сети.}}&lt;br /&gt;
&lt;br /&gt;
Проверяем, что движок отвечает (ожидаем код 200):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s -o /dev/null -w &amp;quot;%{http_code}\n&amp;quot; http://127.0.0.1:9980/hosting/discovery&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключение в Nextcloud ===&lt;br /&gt;
&lt;br /&gt;
Устанавливаем приложение-коннектор «Nextcloud Office»:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /var/www/nextcloud&lt;br /&gt;
sudo -u www-data php occ app:install richdocuments&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Указываем адрес нашего движка Collabora (домен collab):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ config:app:set richdocuments wopi_url --value &#039;https://collab.example.com&#039;&lt;br /&gt;
sudo -u www-data php occ config:app:set richdocuments public_wopi_url --value &#039;https://collab.example.com&#039;&lt;br /&gt;
sudo -u www-data php occ config:app:set richdocuments disable_certificate_verification --value &#039;yes&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Опасно|1=&#039;&#039;&#039;Не&#039;&#039;&#039; устанавливайте приложение &amp;lt;code&amp;gt;richdocumentscode&amp;lt;/code&amp;gt; (встроенный CODE) — оно принудительно гонит весь трафик через медленный &amp;lt;code&amp;gt;proxy.php&amp;lt;/code&amp;gt;, и документы открываются &#039;&#039;&#039;пустыми&#039;&#039;&#039;. Используйте только внешний Docker-движок через &amp;lt;code&amp;gt;wopi_url&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
== Внешний nginx ==&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Reverse-proxy принимает HTTPS снаружи и передаёт запросы внутрь по HTTP. Здесь же живут SSL-сертификаты. Для офиса критично проксировать &#039;&#039;&#039;WebSocket&#039;&#039;&#039; — через него идёт содержимое документа.}}&lt;br /&gt;
&lt;br /&gt;
=== Конфиг Nextcloud (nc.example.com) ===&lt;br /&gt;
&lt;br /&gt;
Создайте файл &amp;lt;code&amp;gt;/etc/nginx/sites-enabled/nc.example.com&amp;lt;/code&amp;gt; со следующим содержимым (подставьте свой домен и IP сервера Nextcloud):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
map $http_upgrade $nc_connection_upgrade { default upgrade; &amp;quot;&amp;quot; close; }&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name nc.example.com;&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl http2;&lt;br /&gt;
    server_name nc.example.com;&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/nc.example.com/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/nc.example.com/privkey.pem;&lt;br /&gt;
    client_max_body_size 10G;&lt;br /&gt;
&lt;br /&gt;
    location / {&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
        proxy_pass http://192.168.1.145;          # IP сервера Nextcloud&lt;br /&gt;
        proxy_read_timeout 900s;&lt;br /&gt;
        proxy_request_buffering off;&lt;br /&gt;
        proxy_buffering off;&lt;br /&gt;
        proxy_max_temp_file_size 0;&lt;br /&gt;
        proxy_http_version 1.1;&lt;br /&gt;
        proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
        proxy_set_header Connection $nc_connection_upgrade;&lt;br /&gt;
    }&lt;br /&gt;
    add_header Strict-Transport-Security &amp;quot;max-age=15552000; includeSubDomains&amp;quot; always;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Конфиг Collabora (collab.example.com) ===&lt;br /&gt;
&lt;br /&gt;
{{Внимание|1=Путь WebSocket &amp;lt;code&amp;gt;/cool/ws&amp;lt;/code&amp;gt; требует заголовков upgrade. HTTP/2 на этом домене лучше &#039;&#039;&#039;не&#039;&#039;&#039; включать (WebSocket надёжнее по HTTP/1.1).}}&lt;br /&gt;
&lt;br /&gt;
Создайте файл &amp;lt;code&amp;gt;/etc/nginx/sites-enabled/collab.example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
map $http_upgrade $collab_upgrade { default upgrade; &amp;quot;&amp;quot; close; }&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
    listen 80;&lt;br /&gt;
    server_name collab.example.com;&lt;br /&gt;
    return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
server {&lt;br /&gt;
    listen 443 ssl;&lt;br /&gt;
    server_name collab.example.com;&lt;br /&gt;
    ssl_certificate     /etc/letsencrypt/live/collab.example.com/fullchain.pem;&lt;br /&gt;
    ssl_certificate_key /etc/letsencrypt/live/collab.example.com/privkey.pem;&lt;br /&gt;
&lt;br /&gt;
    location ^~ /browser              { proxy_pass http://192.168.1.145:9980; proxy_set_header Host $host; }&lt;br /&gt;
    location ^~ /hosting/discovery    { proxy_pass http://192.168.1.145:9980; proxy_set_header Host $host; }&lt;br /&gt;
    location ^~ /hosting/capabilities { proxy_pass http://192.168.1.145:9980; proxy_set_header Host $host; }&lt;br /&gt;
&lt;br /&gt;
    location ~ ^/cool {&lt;br /&gt;
        proxy_pass http://192.168.1.145:9980;&lt;br /&gt;
        proxy_http_version 1.1;&lt;br /&gt;
        proxy_set_header Upgrade $http_upgrade;&lt;br /&gt;
        proxy_set_header Connection $collab_upgrade;&lt;br /&gt;
        proxy_set_header Host $host;&lt;br /&gt;
        proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
        proxy_read_timeout 36000s;&lt;br /&gt;
        proxy_send_timeout 36000s;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем SSL-сертификаты для обоих доменов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo certbot --nginx -d nc.example.com -d collab.example.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверяем конфигурацию и перезагружаем nginx:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Локальный доступ по доменам (split-DNS) ==&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Задача: внутри дома открывать nc.example.com по тем же доменам, но &#039;&#039;&#039;не выходя в интернет&#039;&#039;&#039; (чтобы работало даже без него и быстрее). Решение — заставить локальный DNS отдавать для этих доменов &#039;&#039;&#039;внутренний&#039;&#039;&#039; IP вашего nginx. Это и есть split-DNS.}}&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=Если у вас &#039;&#039;&#039;две подсети&#039;&#039;&#039; с разными роутерами — настройку нужно повторить &#039;&#039;&#039;на каждом&#039;&#039;&#039; роутере, который раздаёт DNS клиентам.}}&lt;br /&gt;
&lt;br /&gt;
=== Вариант OpenWrt ===&lt;br /&gt;
&lt;br /&gt;
Добавляем DNS-записи (подставьте свои домены и IP nginx):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
uci add_list dhcp.@dnsmasq[0].address=&#039;/nc.example.com/192.168.3.47&#039;&lt;br /&gt;
uci add_list dhcp.@dnsmasq[0].address=&#039;/collab.example.com/192.168.3.47&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраняем и перезапускаем DNS-сервер:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
uci commit dhcp&lt;br /&gt;
/etc/init.d/dnsmasq restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=То же самое можно сделать в веб-интерфейсе: &#039;&#039;&#039;Network → DHCP and DNS → General → Addresses&#039;&#039;&#039;, добавив строки вида &amp;lt;code&amp;gt;/nc.example.com/192.168.3.47&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
=== Вариант Keenetic ===&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=В KeeneticOS (ветка 3.7) нет графического раздела для таких DNS-записей — только командная строка. Подключитесь к роутеру по Telnet или SSH (логин admin).}}&lt;br /&gt;
&lt;br /&gt;
Добавляем записи (подставьте свои домены и IP nginx):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ip host nc.example.com 192.168.3.47&lt;br /&gt;
ip host collab.example.com 192.168.3.47&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраняем конфигурацию роутера:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Внимание|1=&amp;lt;code&amp;gt;system configuration save&amp;lt;/code&amp;gt; обязательна — без неё после перезагрузки роутера записи сбросятся. Посмотреть их можно командой &amp;lt;code&amp;gt;show running-config&amp;lt;/code&amp;gt; (строки &amp;lt;code&amp;gt;ip host&amp;lt;/code&amp;gt;).}}&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&lt;br /&gt;
С любого устройства в локальной сети выполните:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nslookup nc.example.com&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В ответе должен быть &#039;&#039;&#039;внутренний&#039;&#039;&#039; IP nginx (например 192.168.3.47), а не публичный. Значит, трафик идёт локально. SSL по имени домена при этом остаётся валидным.&lt;br /&gt;
&lt;br /&gt;
== Обслуживание ==&lt;br /&gt;
&lt;br /&gt;
=== Логи и их ротация ===&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=Лог &amp;lt;code&amp;gt;/media/nextcloud/nextcloud.log&amp;lt;/code&amp;gt; со временем разрастается (бывает до сотен МБ). Ротация автоматически режет его на части и сжимает.}}&lt;br /&gt;
&lt;br /&gt;
Создайте файл &amp;lt;code&amp;gt;/etc/logrotate.d/nextcloud&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/media/nextcloud/nextcloud.log {&lt;br /&gt;
    weekly&lt;br /&gt;
    rotate 8&lt;br /&gt;
    size 50M&lt;br /&gt;
    compress&lt;br /&gt;
    delaycompress&lt;br /&gt;
    copytruncate&lt;br /&gt;
    su www-data www-data&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Превью для быстрой галереи ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Без предгенерации миниатюры (превью) создаются в момент открытия — поэтому галерея «тормозит» при первом просмотре. Приложение previewgenerator готовит их заранее.}}&lt;br /&gt;
&lt;br /&gt;
Устанавливаем приложение:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ app:install previewgenerator&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ограничиваем размеры превью (компактнее и быстрее):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ config:app:set previewgenerator squareSizes --value &amp;quot;256&amp;quot;&lt;br /&gt;
sudo -u www-data php occ config:system:set jpeg_quality --value &amp;quot;80&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ставим защитный лимит памяти на генерацию (чтобы большое фото не съело всю память):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo -u www-data php occ config:system:set preview_max_memory --value 256 --type integer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Запускаем разовый прогрев в фоне с низким приоритетом (не мешает работе; &amp;lt;code&amp;gt;--workers&amp;lt;/code&amp;gt; — под число дисков/ядер):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nohup nice -n 19 ionice -c 3 sudo -u www-data php occ preview:generate-all --workers=4 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=Открытие фото в полный экран использует превью большего размера (~2048px). Если важна и галерея, и быстрое открытие — добавьте второй размер: &amp;lt;code&amp;gt;occ config:app:set previewgenerator widthSizes --value &amp;quot;1024&amp;quot;&amp;lt;/code&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
=== Шпаргалка обслуживания ===&lt;br /&gt;
&lt;br /&gt;
{{Совет|1=Здесь собраны частые команды на каждый день: перезапуск сервисов, режим обслуживания, работа с базой, диагностика. Держите этот раздел под рукой.}}&lt;br /&gt;
&lt;br /&gt;
==== Сервисы: статус и перезапуск ====&lt;br /&gt;
&lt;br /&gt;
Посмотреть, работают ли ключевые сервисы (active = работает):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status apache2 mariadb redis-server docker --no-pager&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Перезапустить веб-сервер / базу / кеш:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl restart apache2&lt;br /&gt;
sudo systemctl restart mariadb&lt;br /&gt;
sudo systemctl restart redis-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Коротко проверить, активен ли сервис (отвечает active / inactive):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl is-active apache2 mariadb redis-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Режим обслуживания ====&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=В режиме обслуживания сайт недоступен пользователям — включайте его перед обновлениями и тяжёлыми операциями с базой.}}&lt;br /&gt;
&lt;br /&gt;
Включить / выключить / проверить режим обслуживания:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /var/www/nextcloud&lt;br /&gt;
sudo -u www-data php occ maintenance:mode --on&lt;br /&gt;
sudo -u www-data php occ maintenance:mode --off&lt;br /&gt;
sudo -u www-data php occ maintenance:mode&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== База данных ====&lt;br /&gt;
&lt;br /&gt;
Войти в консоль базы (от администратора) или под пользователем Nextcloud:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mariadb nextclouddb&lt;br /&gt;
mysql -u nextcloud -p nextclouddb&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посмотреть размер базы и крупнейшие таблицы (выполняется внутри консоли БД):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT table_name, ROUND((data_length+index_length)/1024/1024,1) AS mb&lt;br /&gt;
FROM information_schema.tables WHERE table_schema=&#039;nextclouddb&#039;&lt;br /&gt;
ORDER BY mb DESC LIMIT 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделать резервную копию базы / восстановить из копии:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mysqldump -uroot --single-transaction nextclouddb | gzip &amp;gt; /root/nc-db-$(date +%F).sql.gz&lt;br /&gt;
zcat /root/nc-db-2026-01-01.sql.gz | mysql -uroot nextclouddb&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== occ — частые команды ====&lt;br /&gt;
&lt;br /&gt;
{{Заметка|1=Все команды occ запускаются из папки &amp;lt;code&amp;gt;/var/www/nextcloud&amp;lt;/code&amp;gt; от пользователя www-data.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# статус и версия&lt;br /&gt;
sudo -u www-data php occ status&lt;br /&gt;
# проверка конфигурации (ошибки и рекомендации)&lt;br /&gt;
sudo -u www-data php occ setupchecks&lt;br /&gt;
# список приложений&lt;br /&gt;
sudo -u www-data php occ app:list&lt;br /&gt;
# список пользователей&lt;br /&gt;
sudo -u www-data php occ user:list&lt;br /&gt;
# пересканировать файлы (если что-то добавляли в хранилище мимо Nextcloud)&lt;br /&gt;
sudo -u www-data php occ files:scan --all&lt;br /&gt;
# прочитать / изменить параметр конфигурации&lt;br /&gt;
sudo -u www-data php occ config:system:get memcache.local&lt;br /&gt;
# починка/обслуживание (повторная генерация .htaccess, ремонт)&lt;br /&gt;
sudo -u www-data php occ maintenance:update:htaccess&lt;br /&gt;
sudo -u www-data php occ maintenance:repair&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Логи ====&lt;br /&gt;
&lt;br /&gt;
Следить за логом Nextcloud / ошибками Apache / журналом сервиса в реальном времени:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tail -f /media/nextcloud/nextcloud.log&lt;br /&gt;
tail -f /var/log/apache2/error.log&lt;br /&gt;
journalctl -u mariadb -f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить, были ли падения базы из-за нехватки памяти (OOM):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
journalctl -u mariadb | grep -i oom&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Collabora / Docker ====&lt;br /&gt;
&lt;br /&gt;
Список запущенных контейнеров и их состояние:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker ps&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Лог Collabora / перезапуск контейнера:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
docker logs --tail 50 collabora&lt;br /&gt;
docker restart collabora&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Проверить, что движок отвечает (ожидаем код 200):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -s -o /dev/null -w &amp;quot;%{http_code}\n&amp;quot; http://127.0.0.1:9980/hosting/discovery&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Система ====&lt;br /&gt;
&lt;br /&gt;
Память / место на диске / нагрузка и самые «прожорливые» процессы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
free -h&lt;br /&gt;
df -h&lt;br /&gt;
ps aux --sort=-%mem | head&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Сервисы]]&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9E%D0%BF%D0%B0%D1%81%D0%BD%D0%BE&amp;diff=1024</id>
		<title>Шаблон:Опасно</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9E%D0%BF%D0%B0%D1%81%D0%BD%D0%BE&amp;diff=1024"/>
		<updated>2026-06-21T17:42:32Z</updated>

		<summary type="html">&lt;p&gt;Владимир: иконки вместо эмодзи&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-danger&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;Опасно:&amp;lt;/span&amp;gt; {{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Красный блок «можно сломать». &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Опасно|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%92%D0%BD%D0%B8%D0%BC%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=1023</id>
		<title>Шаблон:Внимание</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%92%D0%BD%D0%B8%D0%BC%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=1023"/>
		<updated>2026-06-21T17:42:31Z</updated>

		<summary type="html">&lt;p&gt;Владимир: иконки вместо эмодзи&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-warning&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;Внимание:&amp;lt;/span&amp;gt; {{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Жёлтый блок «важный нюанс». &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Внимание|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%A1%D0%BE%D0%B2%D0%B5%D1%82&amp;diff=1022</id>
		<title>Шаблон:Совет</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%A1%D0%BE%D0%B2%D0%B5%D1%82&amp;diff=1022"/>
		<updated>2026-06-21T17:42:31Z</updated>

		<summary type="html">&lt;p&gt;Владимир: иконки вместо эмодзи&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-tip&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;Совет:&amp;lt;/span&amp;gt; {{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Зелёный блок «простыми словами». &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Совет|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%97%D0%B0%D0%BC%D0%B5%D1%82%D0%BA%D0%B0&amp;diff=1021</id>
		<title>Шаблон:Заметка</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%97%D0%B0%D0%BC%D0%B5%D1%82%D0%BA%D0%B0&amp;diff=1021"/>
		<updated>2026-06-21T17:42:31Z</updated>

		<summary type="html">&lt;p&gt;Владимир: иконки вместо эмодзи&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-info&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;Заметка:&amp;lt;/span&amp;gt; {{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Синий блок «к сведению». &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Заметка|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D1%8F/styles.css&amp;diff=1020</id>
		<title>Шаблон:Примечания/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D1%8F/styles.css&amp;diff=1020"/>
		<updated>2026-06-21T17:42:31Z</updated>

		<summary type="html">&lt;p&gt;Владимир: иконки вместо эмодзи&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.note{border-left:4px solid;padding:.6em 1em;margin:.8em 0;border-radius:4px;line-height:1.5}&lt;br /&gt;
.note .ttl{font-weight:bold}&lt;br /&gt;
.note .ttl::before{content:&amp;quot;&amp;quot;;display:inline-block;width:1.3em;height:1.3em;line-height:1.3em;text-align:center;border-radius:50%;color:#fff;margin-right:.5em;font-weight:bold;font-style:normal;font-size:.85em;vertical-align:.05em}&lt;br /&gt;
.note-info{border-color:#3366cc;background:#eaf3ff}&lt;br /&gt;
.note-info .ttl::before{content:&amp;quot;i&amp;quot;;background:#3366cc}&lt;br /&gt;
.note-tip{border-color:#14866d;background:#e6f6f0}&lt;br /&gt;
.note-tip .ttl::before{content:&amp;quot;\2713&amp;quot;;background:#14866d}&lt;br /&gt;
.note-warning{border-color:#e0a800;background:#fff8e1}&lt;br /&gt;
.note-warning .ttl::before{content:&amp;quot;!&amp;quot;;background:#e0a800}&lt;br /&gt;
.note-danger{border-color:#dd3333;background:#fdecea}&lt;br /&gt;
.note-danger .ttl::before{content:&amp;quot;!&amp;quot;;background:#dd3333}&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9E%D0%BF%D0%B0%D1%81%D0%BD%D0%BE&amp;diff=1018</id>
		<title>Шаблон:Опасно</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9E%D0%BF%D0%B0%D1%81%D0%BD%D0%BE&amp;diff=1018"/>
		<updated>2026-06-21T17:38:10Z</updated>

		<summary type="html">&lt;p&gt;Владимир: шаблоны примечаний (демо)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-danger&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;🔴 Опасно:&amp;lt;/span&amp;gt;{{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Красный блок «можно сломать». Использование: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Опасно|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%92%D0%BD%D0%B8%D0%BC%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=1017</id>
		<title>Шаблон:Внимание</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%92%D0%BD%D0%B8%D0%BC%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=1017"/>
		<updated>2026-06-21T17:38:10Z</updated>

		<summary type="html">&lt;p&gt;Владимир: шаблоны примечаний (демо)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-warning&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;⚠️ Внимание:&amp;lt;/span&amp;gt;{{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Жёлтый блок «важный нюанс». Использование: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Внимание|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%A1%D0%BE%D0%B2%D0%B5%D1%82&amp;diff=1016</id>
		<title>Шаблон:Совет</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%A1%D0%BE%D0%B2%D0%B5%D1%82&amp;diff=1016"/>
		<updated>2026-06-21T17:38:10Z</updated>

		<summary type="html">&lt;p&gt;Владимир: шаблоны примечаний (демо)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-tip&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;✅ Совет:&amp;lt;/span&amp;gt;{{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Зелёный блок «простыми словами». Использование: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Совет|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%97%D0%B0%D0%BC%D0%B5%D1%82%D0%BA%D0%B0&amp;diff=1015</id>
		<title>Шаблон:Заметка</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%97%D0%B0%D0%BC%D0%B5%D1%82%D0%BA%D0%B0&amp;diff=1015"/>
		<updated>2026-06-21T17:38:10Z</updated>

		<summary type="html">&lt;p&gt;Владимир: шаблоны примечаний (демо)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Примечания/styles.css&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;note note-info&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;ttl&amp;quot;&amp;gt;💡 Заметка:&amp;lt;/span&amp;gt;{{{1}}}&amp;lt;/div&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Синий блок «к сведению». Использование: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Заметка|текст}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
[[Категория:Шаблоны оформления]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D1%8F/styles.css&amp;diff=1014</id>
		<title>Шаблон:Примечания/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D1%8F/styles.css&amp;diff=1014"/>
		<updated>2026-06-21T17:38:10Z</updated>

		<summary type="html">&lt;p&gt;Владимир: шаблоны примечаний (демо)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.note{border-left:4px solid;padding:.6em 1em;margin:.8em 0;border-radius:4px;line-height:1.5}&lt;br /&gt;
.note .ttl{font-weight:bold;margin-right:.3em}&lt;br /&gt;
.note-info{border-color:#3366cc;background:#eaf3ff}&lt;br /&gt;
.note-tip{border-color:#14866d;background:#e6f6f0}&lt;br /&gt;
.note-warning{border-color:#ffcc33;background:#fff8e1}&lt;br /&gt;
.note-danger{border-color:#dd3333;background:#fdecea}&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1012</id>
		<title>Track Muxer</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1012"/>
		<updated>2026-06-20T06:20:18Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Динамическая сетка make_T(dur) вместо фикс. arange(2.5,1400) — укладка любой длительности&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Назначение документа.&#039;&#039;&#039; Полное инженерное описание двух алгоритмов выравнивания, на которых стоит модуль &amp;lt;code&amp;gt;conform&amp;lt;/code&amp;gt;: &#039;&#039;&#039;анализатора зрения&#039;&#039;&#039; (строит карту таймлайна по видеоряду) и &#039;&#039;&#039;аудио-доводки band/muq&#039;&#039;&#039; (правит остаточный сдвиг звука поверх зрения). Документ самодостаточен: математика, геометрия, рекурренты, точные константы и их обоснования, схемы пайплайнов и разбор реальных диагностических графиков. Читатель — инженер, который при желании сможет воспроизвести метод.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Конвенция знаков (сквозная).&#039;&#039;&#039; Сдвиг измеряем в &#039;&#039;кадрах рефа&#039;&#039;; &#039;&#039;&#039;правее (дубль отстаёт) = +&#039;&#039;&#039;, левее (спешит) = −. Один кадр = &#039;&#039;&#039;41.708 мс&#039;&#039;&#039; (23.976 fps, &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; в &amp;lt;code&amp;gt;anchor/params.py&amp;lt;/code&amp;gt;). Сетка анализа &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; &#039;&#039;&#039;строится от реальной длительности пары&#039;&#039;&#039; функцией &amp;lt;code&amp;gt;make_T(dur)&amp;lt;/code&amp;gt; (см. §0.3), шаг &amp;lt;code&amp;gt;STEP = 0.5&amp;lt;/code&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Источники.&#039;&#039;&#039; Всё, что ниже, сверено с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt; и отчётами &amp;lt;code&amp;gt;doc/reports/**&amp;lt;/code&amp;gt;. Ключевые файлы перечислены в Приложении Б.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;0-постановка-задачи-и-общая-архитектура&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 0. Постановка задачи и общая архитектура ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;01-что-мы-выравниваем&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.1. Что мы выравниваем ===&lt;br /&gt;
&lt;br /&gt;
Дан &#039;&#039;&#039;эпизод сериала&#039;&#039;&#039; (или отдельный фильм) в виде набора файлов разных озвучек (студий). Все они содержат &#039;&#039;&#039;один и тот же видеоряд&#039;&#039;&#039; (с точностью до монтажных отличий: разные заставки, вырезы, вставки, иногда другой fps), но &#039;&#039;&#039;разные аудиодорожки&#039;&#039;&#039;. Нужно для каждой пары &amp;lt;code&amp;gt;(референс, дубль)&amp;lt;/code&amp;gt; так преобразовать аудио дубля во времени, чтобы оно &#039;&#039;&#039;синхронно&#039;&#039;&#039; легло на таймлайн референса — и потом смуксировать все дорожки в один MKV.&lt;br /&gt;
&lt;br /&gt;
Главная трудность — это &#039;&#039;&#039;не «найти один сдвиг»&#039;&#039;&#039;. Сдвиг переменный по всей длине:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Источник рассинхрона&lt;br /&gt;
! Как выглядит на карте &amp;lt;code&amp;gt;(t_реф → t_дубль)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Постоянный сдвиг (priming, encoder delay)&lt;br /&gt;
| вертикальный параллельный перенос&lt;br /&gt;
|-&lt;br /&gt;
| Дрейф скорости (разный fps, PAL/NTSC)&lt;br /&gt;
| наклон кривой ≠ 1&lt;br /&gt;
|-&lt;br /&gt;
| Монтажный &#039;&#039;&#039;вырез&#039;&#039;&#039; в дубле (короче рефа)&lt;br /&gt;
| ступень вверх (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Монтажная &#039;&#039;&#039;вставка&#039;&#039;&#039; в дубле (длиннее рефа)&lt;br /&gt;
| ступень вниз (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Заставка/реклама в начале&lt;br /&gt;
| свободный левый конец&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И всё это — на контенте, где прямое применение обоих слоёв теряет чувствительность: видеоряд может идти «на двойках» (соседние кадры идентичны — анимация на 2 кадра, статичные планы), музыка повторяется (корреляция фиксируется на такт), у дубля чужой голос поверх общей музыки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 0.1 — «Проблема в одной картинке».&#039;&#039;&#039; Слева: две киноленты (реф и дубль) друг под другом, на дубле — вырезанный кусок (короче) и вставленная заставка в начале (длиннее), стрелки соответствия кадров между лентами расходятся (не параллельны). Справа: те же соответствия как точки в осях &amp;lt;code&amp;gt;t_реф&amp;lt;/code&amp;gt;(X) / &amp;lt;code&amp;gt;t_дубль&amp;lt;/code&amp;gt;(Y) — складываются в ломаную линию с наклоном и двумя вертикальными ступенями. Подпись: «Выравнивание = восстановить эту ломаную».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;02-двухслойная-архитектура-зрение-строит-карту-звук-доводит&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.2. Двухслойная архитектура: зрение строит карту, звук доводит ===&lt;br /&gt;
&lt;br /&gt;
Фундаментальное решение проекта (отчёт &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;): &#039;&#039;&#039;видеоряд — общий инвариант&#039;&#039;&#039; всех озвучек, поэтому грубую и структурную карту строим по видео, а аудио используем лишь как &#039;&#039;&#039;тонкую доводку остатка&#039;&#039;&#039;, который видео физически не видит (аудио источника бывает смещено относительно своего же видео).&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    subgraph IN[Вход]&lt;br /&gt;
        REF[Референс mp4/mkv]&lt;br /&gt;
        DUB[Дубль mp4/mkv]&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph VIS[&amp;quot;СЛОЙ 1 · ЗРЕНИЕ (видео-карта)&amp;quot;]&lt;br /&gt;
        SRM[SRM-отпечатки кадров&amp;lt;br/&amp;gt;features.py]&lt;br /&gt;
        CO[Грубый проход · LIS&amp;lt;br/&amp;gt;kernel/coarse.py]&lt;br /&gt;
        DD[Drop-DTW в полосе&amp;lt;br/&amp;gt;kernel/dropdtw + band_align]&lt;br /&gt;
        LV[Разбор правок&amp;lt;br/&amp;gt;level_decide / creep_drop]&lt;br /&gt;
        PW[Укладка ломаной + детект резов&amp;lt;br/&amp;gt;vision_detect.py]&lt;br /&gt;
        SRM --&amp;gt; CO --&amp;gt; DD --&amp;gt; LV --&amp;gt; PW&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph RES[Ресэмпл аудио дубля]&lt;br /&gt;
        MAP[Карта t_реф → t_дубль]&lt;br /&gt;
        SIL1[Тишина в монтажных резах]&lt;br /&gt;
        MAP --&amp;gt; SIL1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph AUD[&amp;quot;СЛОЙ 2 · ЗВУК (доводка)&amp;quot;]&lt;br /&gt;
        OFF0[Грубая off0 · band ±2.5с]&lt;br /&gt;
        FOL[band ±0.7с следит за off0]&lt;br /&gt;
        DET[Детект ступеней · detect.py]&lt;br /&gt;
        DRIFT[Кривая дрейфа + потолок скорости]&lt;br /&gt;
        WARP[Варп по кускам + тишина]&lt;br /&gt;
        OFF0 --&amp;gt; FOL --&amp;gt; DET --&amp;gt; DRIFT --&amp;gt; WARP&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    FILL[Заполнение тишины синхронным рефом&amp;lt;br/&amp;gt;fill_silence]&lt;br /&gt;
    PLOTS[Единый график зрение+звук&amp;lt;br/&amp;gt;plots_unified.py]&lt;br /&gt;
    OUT[Выровненный WAV → mux]&lt;br /&gt;
&lt;br /&gt;
    REF --&amp;gt; SRM&lt;br /&gt;
    DUB --&amp;gt; SRM&lt;br /&gt;
    PW --&amp;gt; MAP&lt;br /&gt;
    SIL1 --&amp;gt; OFF0&lt;br /&gt;
    WARP --&amp;gt; FILL --&amp;gt; PLOTS --&amp;gt; OUT&lt;br /&gt;
}}&lt;br /&gt;
Почему именно так, а не «всё по аудио» или «всё по видео»:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Только аудио&#039;&#039;&#039; упирается в потолок: 5 разных аудио-методов (PHAT, MFCC, 2D-спектр) дают &#039;&#039;одни и те же промахи&#039;&#039; на повторяющемся контенте — это не алгоритмическая, а &#039;&#039;информационная&#039;&#039; нехватка в одном аудиоканале (&amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §2&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Только видео&#039;&#039;&#039; не видит сдвиг аудио относительно видео внутри самого источника (AV-sync delta, студийный сдвиг M&amp;amp;amp;E) — его видит лишь аудио.&lt;br /&gt;
* Поэтому: &#039;&#039;&#039;видео даёт структуру&#039;&#039;&#039; (где вырез, где вставка, какой дрейф), &#039;&#039;&#039;аудио доводит&#039;&#039;&#039; субкадровый остаток там, где видео слепо по физике (двойки) или где аудио смещено относительно картинки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;03-сквозные-величины-и-обозначения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.3. Сквозные величины и обозначения ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Обозначение&lt;br /&gt;
! Смысл&lt;br /&gt;
! Значение / где&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| мс на кадр&lt;br /&gt;
| 41.708 (≈23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| сетка анализа (звук и зрение)&lt;br /&gt;
| &amp;lt;code&amp;gt;make_T(dur) = arange(2.5, dur, 0.5)&amp;lt;/code&amp;gt; с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;make_T(dur)&amp;lt;/code&amp;gt;&lt;br /&gt;
| строит сетку от длины пары&lt;br /&gt;
| от &amp;lt;code&amp;gt;T0 = 2.5&amp;lt;/code&amp;gt; с до конца дорожки &amp;lt;code&amp;gt;dur&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| шаг сетки&lt;br /&gt;
| 0.5 с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;o[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| измеренный сдвиг в точке t&lt;br /&gt;
| кадры рефа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;w[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| уверенность измерения&lt;br /&gt;
| норм. по медиане&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pred[j]&amp;lt;/code&amp;gt;&lt;br /&gt;
| для кадра дубля j — кадр рефа (или −1)&lt;br /&gt;
| целое&lt;br /&gt;
|-&lt;br /&gt;
| cos&lt;br /&gt;
| мера схожести двух кадров&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\in[-1,1]&amp;lt;/math&amp;gt;, на практике &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0,1]&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Сетка &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; — от реальной длины пары.&#039;&#039;&#039; &amp;lt;code&amp;gt;T = make_T(dur)&amp;lt;/code&amp;gt; строит узлы от &amp;lt;code&amp;gt;T0 = 2.5&amp;lt;/code&amp;gt; с до конца дорожки &amp;lt;code&amp;gt;dur&amp;lt;/code&amp;gt; с шагом &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;, где &amp;lt;code&amp;gt;dur&amp;lt;/code&amp;gt; — длительность рефа, на чей таймлайн кладётся дубль (&amp;lt;code&amp;gt;dur_ref&amp;lt;/code&amp;gt; у зрения, &amp;lt;code&amp;gt;n_out/sr&amp;lt;/code&amp;gt; у звука). Сетка одна на &#039;&#039;&#039;оба&#039;&#039;&#039; слоя: зрение (&amp;lt;code&amp;gt;vision_detect.build_map&amp;lt;/code&amp;gt; зовёт &amp;lt;code&amp;gt;make_T(dur_ref)&amp;lt;/code&amp;gt;) и звук (&amp;lt;code&amp;gt;audio_anchor&amp;lt;/code&amp;gt; зовёт &amp;lt;code&amp;gt;make_T(n_out/sr)&amp;lt;/code&amp;gt;) анализируют по ней, поэтому покрывается дорожка &#039;&#039;&#039;любой длительности&#039;&#039;&#039; — и 4-минутный ролик, и многочасовой фильм.&lt;br /&gt;
&lt;br /&gt;
Раньше сетка была &#039;&#039;&#039;фиксированной&#039;&#039;&#039; &amp;lt;code&amp;gt;arange(2.5, 1400, 0.5)&amp;lt;/code&amp;gt; (до 1399.5 с). Это молча предполагало, что любая серия длиннее ~23.4 мин, и ломало длину в обе стороны: на коротких хвостовые окна вылезали за конец дорожки и &amp;lt;code&amp;gt;band&amp;lt;/code&amp;gt; падал на &amp;lt;code&amp;gt;torch.stack&amp;lt;/code&amp;gt; (ragged-тензор), а на длинных (&amp;amp;gt; 23.4 мин) анализ &#039;&#039;&#039;обрывался на 23-й минуте&#039;&#039;&#039; — дальше сдвиг «замораживался» на последнем измеренном значении (и в зрении, и в звуке, и на графике). &amp;lt;code&amp;gt;make_T(dur)&amp;lt;/code&amp;gt; снимает оба дефекта.&lt;br /&gt;
&lt;br /&gt;
Верхняя граница сетки — ровно &amp;lt;code&amp;gt;dur&amp;lt;/code&amp;gt; (а не &amp;lt;code&amp;gt;dur − T0&amp;lt;/code&amp;gt;): зрению достаточно &#039;&#039;&#039;наличия кадров&#039;&#039;&#039; (окна у него нет), а у &amp;lt;code&amp;gt;band&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;multispec&amp;lt;/code&amp;gt; хвостовые окна, выходящие за конец дорожки, гасит &#039;&#039;&#039;леаф-кламп&#039;&#039;&#039; — старт окна клампится в &amp;lt;code&amp;gt;[0, n−w]&amp;lt;/code&amp;gt;, реально вышедшие узлы дают &amp;lt;code&amp;gt;o=NaN&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;w=0&amp;lt;/code&amp;gt; и на укладку не влияют. Сетка передаётся &#039;&#039;&#039;параметром&#039;&#039;&#039; по всему тракту (а не мутирует глобал) — при параллельной очереди серии разной длины не мешают друг другу. Глобальный &amp;lt;code&amp;gt;params.T&amp;lt;/code&amp;gt; остаётся лишь дефолтом для стендов и тестов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дальше — два алгоритма по слоям.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-i-зрение--построитель-видео-карты&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть I. Зрение — построитель видео-карты =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/band_align.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;, разбор правок в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;. Это &#039;&#039;&#039;единственный&#039;&#039;&#039; построитель видео-карты в conform (прежний «скат» &amp;lt;code&amp;gt;_build_map_linear&amp;lt;/code&amp;gt; удалён 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;1-геометрия-задачи-карта-как-монотонная-кусочно-линейная-кривая&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 1. Геометрия задачи: карта как монотонная кусочно-линейная кривая ==&lt;br /&gt;
&lt;br /&gt;
Всё зрение сводится к одной геометрической цели — построить функцию&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tau:\; t_{\text{реф}} \;\longmapsto\; t_{\text{дубль}},&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
которая каждому моменту таймлайна референса сопоставляет момент в дубле, откуда брать звук. У этой функции жёсткая физическая структура:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;кусочно-линейная&#039;&#039;&#039; — внутри монтажного блока скорость постоянна (дрейф = наклон);&lt;br /&gt;
* &#039;&#039;&#039;монотонно неубывающая&#039;&#039;&#039; — время не идёт вспять (контент не переставляется);&lt;br /&gt;
* &#039;&#039;&#039;с вертикальными разрывами на монтажных стыках&#039;&#039;&#039; — вырез/вставка = ступень.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; t_дубль&lt;br /&gt;
   ▲&lt;br /&gt;
   │                                        ╱ (наклон ≈ fps_реф/fps_дубль)&lt;br /&gt;
   │                                   ╱╱╱&lt;br /&gt;
   │            вставка ↓         ╱╱╱&lt;br /&gt;
   │                        ┌────╱           ← разрыв вниз: в дубле лишний кусок&lt;br /&gt;
   │                        ┊&lt;br /&gt;
   │                   ╱╱╱╱╱┘&lt;br /&gt;
   │              ╱╱╱╱&lt;br /&gt;
   │   вырез ↑   ╱&lt;br /&gt;
   │         ┌──┘                            ← разрыв вверх: в дубле кусок вырезан&lt;br /&gt;
   │      ╱╱╱┊&lt;br /&gt;
   │  ╱╱╱╱   ┊&lt;br /&gt;
   └──┴──────┴──────────────────────────────►  t_реф&amp;lt;/pre&amp;gt;&lt;br /&gt;
Задача зрения — &#039;&#039;&#039;восстановить эту ломаную из зашумлённых измерений соответствия кадров&#039;&#039;&#039;. Шум двух видов: (а) ложные соответствия (двойки, лого, тёмные сцены), (б) пропуски (вырезы/вставки рвут непрерывность). Поэтому метод строится в три эшелона надёжности: грубая монотонная цепочка (§3) → точное выравнивание с пропусками (§4) → робастная ломаная поверх (§6).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;2-srm-отпечаток-кадра&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 2. SRM-отпечаток кадра ==&lt;br /&gt;
&lt;br /&gt;
Чтобы сравнивать кадры рефа и дубля, каждый кадр сворачивается в вектор-отпечаток так, чтобы &#039;&#039;&#039;схожесть контента&#039;&#039;&#039; была устойчива к различиям источника (BD vs Web, разный кодек, яркость, логотип студии). Наивный путь — сравнивать пиксели яркости — ломается на первом же логотипе и перепаде гаммы. Решение — &#039;&#039;&#039;SRM (Spatial Rich Model): отпечаток не яркости, а остаточного высокочастотного сигнала&#039;&#039;&#039; (текстуры/краёв).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: что такое «отпечаток кадра» и SRM.&#039;&#039;&#039; Отпечаток (дескриптор) — это короткий числовой слепок картинки: вектор чисел, по которому два кадра можно сравнить, не сличая их попиксельно. Хороший отпечаток одинаков у одного и того же кадра в разном качестве и не сбивается от логотипа или яркости. &#039;&#039;&#039;SRM (Spatial Rich Model)&#039;&#039;&#039; — приём из цифровой криминалистики изображений: вместо самой картинки берут её &#039;&#039;высокочастотный остаток&#039;&#039; — то, что остаётся после вычитания плавных перепадов яркости (контуры, мелкая текстура). Именно остаток устойчив к перекодированию и смене источника.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;21-конвейер-построения-featurespybuild_srm&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.1. Конвейер построения (&amp;lt;code&amp;gt;features.py::build_srm&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    A[Кадр ffmpeg&amp;lt;br/&amp;gt;-vsync 0] --&amp;gt; B[Серый 128×72]&lt;br /&gt;
    B --&amp;gt; C1[Свёртка KB&amp;lt;br/&amp;gt;лапласиан]&lt;br /&gt;
    B --&amp;gt; C2[Свёртка D1&amp;lt;br/&amp;gt;градиент x]&lt;br /&gt;
    C1 --&amp;gt; D1[clip ±3]&lt;br /&gt;
    C2 --&amp;gt; D2[clip ±3]&lt;br /&gt;
    D1 --&amp;gt; E1[L2-норма канала]&lt;br /&gt;
    D2 --&amp;gt; E2[L2-норма канала]&lt;br /&gt;
    E1 --&amp;gt; F[concat × 1/√2&amp;lt;br/&amp;gt;float16, dim=18432]&lt;br /&gt;
    E2 --&amp;gt; F&lt;br /&gt;
}}&lt;br /&gt;
Пошагово. Кадр приводится к серому &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;g&amp;lt;/math&amp;gt; размера &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;128\times72&amp;lt;/math&amp;gt; (&amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: свёртка ядром.&#039;&#039;&#039; Свёртка — это проход маленькой матрицей-окном по всем пикселям: в каждой точке берётся взвешенная сумма соседних пикселей. Подбором весов окно настраивают на нужный признак — перепад яркости, край, текстуру. Сумма весов, равная нулю, означает, что окно «не замечает» ровной заливки и реагирует только на изменения.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Здесь два фиксированных ядра свёртки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
K_B=\begin{pmatrix}-1&amp;amp;2&amp;amp;-1\\ 2&amp;amp;-4&amp;amp;2\\ -1&amp;amp;2&amp;amp;-1\end{pmatrix},&lt;br /&gt;
\qquad&lt;br /&gt;
D_1=\begin{pmatrix}0&amp;amp;0&amp;amp;0\\ 0&amp;amp;-1&amp;amp;1\\ 0&amp;amp;0&amp;amp;0\end{pmatrix}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; — дискретный оператор второго порядка (лапласиан-подобный, сумма коэффициентов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0&amp;lt;/math&amp;gt;). Он &#039;&#039;&#039;зануляет постоянную и линейную составляющую&#039;&#039;&#039; яркости и реагирует на &#039;&#039;кривизну&#039;&#039; — текстуру, контуры, мелкие детали. Именно эта высокочастотная «подпись» одинакова у одного и того же кадра в разных рипах.&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; — первая горизонтальная разность (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\partial/\partial x&amp;lt;/math&amp;gt;), ловит вертикальные края и направление градиента.&lt;br /&gt;
&lt;br /&gt;
Свёртка идёт с краевым режимом &amp;lt;code&amp;gt;reflect&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_K = (g * K_B)&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_D = (g * D_1)&amp;lt;/math&amp;gt;. Далее — обрезка выбросов и нормировка каждого канала по отдельности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tilde r_K=\frac{\operatorname{clip}(r_K,-3,3)}{\lVert\operatorname{clip}(r_K,-3,3)\rVert_2+\varepsilon},&lt;br /&gt;
\qquad&lt;br /&gt;
\tilde r_D=\frac{\operatorname{clip}(r_D,-3,3)}{\lVert\operatorname{clip}(r_D,-3,3)\rVert_2+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обрезка &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm3&amp;lt;/math&amp;gt; давит редкие яркие выбросы (блики, субтитры), L2-нормировка убирает зависимость от общего контраста кадра. Итоговый вектор кадра — конкатенация обоих каналов, масштабированная на &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v=\tfrac{1}{\sqrt2}\,[\,\tilde r_K \;\Vert\; \tilde r_D\,]\in\mathbb{R}^{18432},\qquad&lt;br /&gt;
18432 = 2\cdot128\cdot72 .&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;22-почему-cos--v_1cdot-v_2-напрямую&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.2. Почему &amp;lt;code&amp;gt;cos = v_1\cdot v_2&amp;lt;/code&amp;gt; напрямую ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: косинусная близость.&#039;&#039;&#039; Если каждый кадр представить стрелкой (вектором) в многомерном пространстве, то мерой их похожести служит угол между стрелками: сонаправленные (один контент) дают косинус ≈ 1, перпендикулярные (разный контент) ≈ 0. Косинус зависит только от направления, не от длины стрелок, — поэтому он устойчив к общей яркости и контрасту.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Множитель &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt; выбран так, что &#039;&#039;&#039;готовый вектор уже нормирован&#039;&#039;&#039;: поскольку каждый канал имеет единичную норму,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\lVert v\rVert_2^2=\tfrac12\big(\lVert\tilde r_K\rVert_2^2+\lVert\tilde r_D\rVert_2^2\big)=\tfrac12(1+1)=1.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поэтому скалярное произведение двух отпечатков &#039;&#039;&#039;сразу равно косинусной близости&#039;&#039;&#039;, и она же — среднее канальных косинусов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v_1\cdot v_2=\tfrac12\big(\cos_{K_B}+\cos_{D_1}\big)=\cos(v_1,v_2)\in[-1,1].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это ключ к скорости всего зрения: матрица всех попарных схожестей — одно матричное умножение &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G = C_s C_r^{\top}&amp;lt;/math&amp;gt; (косинусы без отдельной нормировки), а стоимость для Drop-DTW — просто &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C = 1 - \cos&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Инженерные детали, ломающие бит-в-бит совпадение, если их нарушить&#039;&#039;&#039; (&amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;): ffmpeg &amp;lt;code&amp;gt;-vsync 0&amp;lt;/code&amp;gt; (passthrough — индекс кадра = позиция во времени), &amp;lt;code&amp;gt;float16&amp;lt;/code&amp;gt;, порядок &amp;lt;code&amp;gt;clip → norm → concat&amp;lt;/code&amp;gt;. Декод потоковый, при &amp;lt;code&amp;gt;low_mem&amp;lt;/code&amp;gt; фичи пишутся в memmap, значения идентичны in-RAM пути.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 2.1 — «Что видит SRM».&#039;&#039;&#039; Три колонки: (1) исходный кадр видео с логотипом студии в углу; (2) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; (контуры/текстура, лого почти не выделяется — оно гладкое); (3) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; (вертикальные края). Подпись: «Отпечаток строится по высокочастотному остатку → устойчив к яркости и логотипу».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;3-грубый-проход-матрица-косинусов-надёжные-якоря-lis&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 3. Грубый проход: матрица косинусов, надёжные якоря, LIS ==&lt;br /&gt;
&lt;br /&gt;
Цель первого эшелона — получить &#039;&#039;&#039;грубую, но монотонную&#039;&#039;&#039; оценку сдвига &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;j&amp;lt;/math&amp;gt;, чтобы потом узкая полоса Drop-DTW искала точное соответствие не во всём рефе, а в коридоре вокруг этой линии. Файл &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;31-надёжные-якоря-_anchors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.1. Надёжные якоря (&amp;lt;code&amp;gt;_anchors&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кадры прореживаются с шагом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K=8&amp;lt;/math&amp;gt; (грубому проходу субкадровая точность не нужна). Строится матрица косинусов всех прореженных synth × ref:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
G = C_s\,C_r^{\top},\qquad G_{m,r}=\cos\big(v^{\text{syn}}_m, v^{\text{ref}}_r\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;m&amp;lt;/math&amp;gt; берётся лучший партнёр и его близость &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{best}_m=\arg\max_r G_{m,r}&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{bestv}_m=\max_r G_{m,r}&amp;lt;/math&amp;gt;. Затем окрестность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm W&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;W=80&amp;lt;/math&amp;gt; прореженных кадров) этого пика &#039;&#039;&#039;зануляется&#039;&#039;&#039;, и берётся второй максимум &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{second}_m&amp;lt;/math&amp;gt;. Кадр становится &#039;&#039;&#039;надёжным якорем&#039;&#039;&#039; только если выполнены оба условия:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\underbrace{\text{bestv}_m &amp;gt; V_{HI}}_{\text{пик высокий}}\quad\wedge\quad&lt;br /&gt;
\underbrace{\text{bestv}_m-\text{second}_m &amp;gt; C_{MIN}}_{\text{пик одинок (различим)}},\qquad&lt;br /&gt;
V_{HI}=0.60,\; C_{MIN}=0.12.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первое условие отсекает мусорные сцены (тьма, переход), второе — &#039;&#039;&#039;двойки и повторы&#039;&#039;&#039;: если в рефе есть второй почти такой же кадр, отрыв мал и кадр НЕ берётся в якоря. Это прямой ответ на главную проблему повторяющегося контента.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;32-монотонная-цепочка-через-lis-lis_nd-coarse_robust&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.2. Монотонная цепочка через LIS (&amp;lt;code&amp;gt;lis_nd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;coarse_robust&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: зачем «длиннейшая неубывающая подпоследовательность» (LIS).&#039;&#039;&#039; Среди якорей есть верные и единичные ложные. Верные обязаны идти «по возрастанию»: чем позже кадр в дубле, тем позже его партнёр в рефе (время не отматывается назад). LIS — классический алгоритм, который из разбросанных точек вытаскивает самую длинную цепочку, идущую только вверх; всё, что в неё не уложилось, почти наверняка ложные пары. Так костяк соответствия очищается без порогов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Надёжные якоря ещё могут содержать одиночные ложные пары. Истинное соответствие &#039;&#039;&#039;монотонно&#039;&#039;&#039; по рефу, поэтому из набора якорей &amp;lt;code&amp;gt;(synth_кадр → ref_кадр)&amp;lt;/code&amp;gt; извлекается &#039;&#039;&#039;длиннейшая неубывающая по ref подпоследовательность&#039;&#039;&#039; (Longest Non-Decreasing Subsequence, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(n\log n)&amp;lt;/math&amp;gt; через хвостовые индексы + бинарный поиск). Всё, что не легло в монотонную цепочку, отбрасывается как шум. По выжившим якорям линейной интерполяцией строится &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для всех кадров:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}(j)=\operatorname{interp}\big(j;\ \{a_k\},\ \{r_k-a_k\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; ref                     LIS оставляет монотонный костяк, шум выпадает&lt;br /&gt;
  ▲        ✗(ложный)&lt;br /&gt;
  │   ●───●        ✗&lt;br /&gt;
  │  ╱       ●───●───●&lt;br /&gt;
  │ ●                   ●───●     ← возрастающая цепочка = реальное соответствие&lt;br /&gt;
  │╱            ✗&lt;br /&gt;
  └───────────────────────────►  synth (кадр дубля)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;33-оконный-вариант-для-длинных-файлов-coarse_windowed&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.3. Оконный вариант для длинных файлов (&amp;lt;code&amp;gt;coarse_windowed&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Полная матрица &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G&amp;lt;/math&amp;gt; имеет размер &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; — для серии это гигабайты. Оконный проход режет дубль на окна по &amp;lt;code&amp;gt;CWIN=8000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;COV=2000&amp;lt;/code&amp;gt;; &#039;&#039;&#039;seed-offset перетекает из окна в окно&#039;&#039;&#039;, и в каждом окне ищется ref только в полосе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;CSR=4000&amp;lt;/code&amp;gt; вокруг seed. Память и время ограничены окном (не растут с длиной файла), а результат &#039;&#039;&#039;бит-в-бит совпал&#039;&#039;&#039; с полным проходом на 26 кейсах.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;4-drop-dtw-выравнивание-с-пропусками&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 4. Drop-DTW: выравнивание с пропусками ==&lt;br /&gt;
&lt;br /&gt;
Второй эшелон — точное соответствие кадров. Обычный DTW (Dynamic Time Warping) &#039;&#039;&#039;обязан&#039;&#039;&#039; сопоставить каждый кадр — он не умеет сказать «этого куска в рефе нет, это вставка». А у нас именно вырезы и вставки. Решение — &#039;&#039;&#039;Drop-DTW&#039;&#039;&#039;: DTW, которому разрешено &#039;&#039;выбрасывать&#039;&#039; кадры с обеих сторон за фиксированный штраф. Это ровно как &amp;lt;code&amp;gt;diff&amp;lt;/code&amp;gt; для текста или выравнивание последовательностей ДНК: ищем общий костяк, непарные куски помечаем как «вставка»/«удаление». Файл &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;41-стоимость-и-базовая-рекуррента-drop_dtw&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.1. Стоимость и базовая рекуррента (&amp;lt;code&amp;gt;drop_dtw&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Стоимость сопоставить кадр рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt; с кадром дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
C_{i,k}=1-\cos\!\big(v^{\text{ref}}_i,v^{\text{syn}}_k\big)\in[0,2].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами.&#039;&#039;&#039; Задача — перебрать все способы «растянуть / сжать / пропустить» кадры так, чтобы суммарная непохожесть сопоставленных кадров была минимальной, и сделать это не наивным перебором (астрономически долго), а накоплением лучшего ответа клетка за клеткой — это и есть динамическое программирование. Ниже — правило, по которому заполняется таблица лучших частичных ответов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Динамика заполняет таблицу &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_{i,k}&amp;lt;/math&amp;gt; — минимальную накопленную стоимость пути, пришедшего в клетку &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(i,k)&amp;lt;/math&amp;gt;. На каждом шаге доступны &#039;&#039;&#039;пять&#039;&#039;&#039; ходов (диагональ, два варпа, два выброса); старт — отдельная инициализация (свободный левый конец):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_{i,k}=\min\begin{cases}&lt;br /&gt;
D_{i-1,k-1}+C_{i,k} &amp;amp; \text{диагональ (1:1 совпадение)}\\&lt;br /&gt;
D_{i-1,k}+C_{i,k} &amp;amp; \text{вертикальный варп (ref тянется)}\\&lt;br /&gt;
D_{i,k-1}+C_{i,k} &amp;amp; \text{горизонтальный варп (дубль тянется)}\\&lt;br /&gt;
D_{i,k-1}+\text{DROP} &amp;amp; \text{выброс кадра дубля (вставка)}\\&lt;br /&gt;
D_{i-1,k}+\text{DROP} &amp;amp; \text{выброс кадра рефа (вырез)}\\&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с штрафом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{DROP}=0.20&amp;lt;/math&amp;gt;. Backpointer &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;B_{i,k}&amp;lt;/math&amp;gt; хранит, какой ход выбран, для восстановления пути. &#039;&#039;&#039;Свободные концы&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;свободный старт&#039;&#039;&#039; — &amp;lt;code&amp;gt;D[i,0] = C[i,0]&amp;lt;/code&amp;gt; для всех &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt;: путь может начаться с любой строки рефа (дубль не обязан стартовать с самого начала рефа);&lt;br /&gt;
* &#039;&#039;&#039;свободный конец&#039;&#039;&#039; — обратный ход начинается с &amp;lt;code&amp;gt;argmin&amp;lt;/code&amp;gt; последнего столбца: путь может закончиться на любой строке рефа.&lt;br /&gt;
&lt;br /&gt;
Восстановление пути (&amp;lt;code&amp;gt;backtrack&amp;lt;/code&amp;gt;) даёт три выхода: &amp;lt;code&amp;gt;pred[k]&amp;lt;/code&amp;gt; (для кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — его кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;drop_syn&amp;lt;/code&amp;gt; (маска выброшенных кадров дубля = вставки), &amp;lt;code&amp;gt;drop_ref&amp;lt;/code&amp;gt; (список выброшенных кадров рефа = вырезы).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 4.1 — «Путь по матрице Drop-DTW».&#039;&#039;&#039; Тепловая карта стоимости &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C_{i,k}&amp;lt;/math&amp;gt; (тёмное = похоже), ось X = кадры дубля, ось Y = кадры рефа. Поверх — оптимальный путь: диагональные участки (совпадение), один горизонтальный «провал» (выброс кадров дубля = вставка) и один вертикальный «прыжок» (выброс кадров рефа = вырез). Сбоку — легенда шести ходов. Подпись: «Зелёная линия — карта; разрывы = монтажные стыки».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;42-аффинный-штраф-острый-шов-вместо-размазни-drop_dtw_affine&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.2. Аффинный штраф: острый шов вместо размазни (&amp;lt;code&amp;gt;drop_dtw_affine&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Если вырез длинный, выбрасывать его «по одному кадру» дорого и нестабильно — путь норовит размазать выброс или убежать на логотипе. Аффинная (two-state) динамика берёт &#039;&#039;&#039;открытие&#039;&#039;&#039; прогона выброса дороже, чем его &#039;&#039;&#039;продолжение&#039;&#039;&#039; (как gap-open / gap-extend в биоинформатике):&lt;br /&gt;
&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; — «матч/варп» (платит &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C&amp;lt;/math&amp;gt;);&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; — «внутри выброса рефа»: открыть из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;OPEN=0.20&amp;lt;/code&amp;gt;, продлить из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;EXT=0.02&amp;lt;/code&amp;gt;;&lt;br /&gt;
* выброс кадра дубля (вставка) — плоская цена &amp;lt;code&amp;gt;DSYN=0.30&amp;lt;/code&amp;gt; внутри &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_r[i,k]=\min\big(M[i-1,k]+\text{OPEN},\; D_r[i-1,k]+\text{EXT}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дёшево продлевать (0.02) → длинный вырез схлопывается в один чистый шов, а не в рваную лесенку. На ровных логотипных участках состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; не открывается → путь не уходит в сторону.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;43-guard-нельзя-выбросить-кадр-с-хорошим-матчем-drop_dtw_affine_guard&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.3. Guard: нельзя выбросить кадр с хорошим матчем (&amp;lt;code&amp;gt;drop_dtw_affine_guard&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Без защиты прогон выброса склонен захватывать хвосты совпадающих кадров (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos=1&amp;lt;/math&amp;gt;), раздувая вставку. Guard заранее считает для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; лучший достижимый матч по столбцу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{colmin}_k=\min_i C_{i,k}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выброс этого кадра (ход drop-syn) разрешён &#039;&#039;&#039;только если&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{colmin}_k&amp;gt;\text{MATCH}\_\text{THR}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0.30&amp;lt;/math&amp;gt;); иначе цена выброса &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=\infty&amp;lt;/math&amp;gt; — кадр с хорошим матчем защищён от выбрасывания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;44-free-start-заставка-начала-выпадает-сама&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.4. Free-start: заставка начала выпадает сама ===&lt;br /&gt;
&lt;br /&gt;
Реклама/заставка в начале дубля не имеет пары в рефе. Free-start даёт &#039;&#039;&#039;свободный левый конец по дублю&#039;&#039;&#039;: путь может стартовать с любой клетки, выбросив ведущие кадры &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0..k-1]&amp;lt;/math&amp;gt; по цене вставки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k\cdot\text{DSYN}&amp;lt;/math&amp;gt;. Штраф растёт с &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — поэтому короткая заставка выпадает, но путь не «улетает» далеко.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{старт в } (i,k):\quad \text{cost}=C_{i,k}+k\cdot\text{DSYN}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;45-следящая-полоса-band_align&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.5. Следящая полоса (&amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Drop-DTW на полной матрице &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; невозможен по памяти. Поэтому он считается в &#039;&#039;&#039;узкой полосе&#039;&#039;&#039; вокруг грубой линии &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; (§3): дубль режется на куски по &amp;lt;code&amp;gt;CHUNK=4000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;OVERLAP=700&amp;lt;/code&amp;gt;; для каждого куска ref-окно берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[\min(\text{refk})-\text{MARG},\ \max(\text{refk})+\text{MARG}]&amp;lt;/math&amp;gt;, полуширина &amp;lt;code&amp;gt;MARG=120&amp;lt;/code&amp;gt; кадров (≈5 с — покрывает обычную правку). На стыках кусков для каждого кадра берётся ответ из того куска, где кадр &#039;&#039;&#039;дальше от края&#039;&#039;&#039; (надёжнее):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; кусок A:  ├──────────────────┤&lt;br /&gt;
 кусок B:              ├──────────────────┤&lt;br /&gt;
                       └ overlap ┘&lt;br /&gt;
 кадр в overlap → берём из того куска, где он ГЛУБЖЕ (центр надёжнее краёв)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    OFF[Грубая off из coarse] --&amp;gt; WIN[Окно ref ±MARG=120&amp;lt;br/&amp;gt;вокруг линии]&lt;br /&gt;
    CHUNK[Кусок дубля 4000&amp;lt;br/&amp;gt;overlap 700] --&amp;gt; WIN&lt;br /&gt;
    WIN --&amp;gt; C[&amp;quot;C = 1 − cos&amp;quot;]&lt;br /&gt;
    C --&amp;gt; DD[drop_dtw_affine_guard&amp;lt;br/&amp;gt;OPEN .20 / EXT .02 / DSYN .30&amp;lt;br/&amp;gt;MATCH_THR .30 / free_start]&lt;br /&gt;
    DD --&amp;gt; BT[backtrack → pred куска]&lt;br /&gt;
    BT --&amp;gt; MERGE[Склейка по «глубине» кадра]&lt;br /&gt;
}}&lt;br /&gt;
Итог слоя §4 — массив &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; длины &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s&amp;lt;/math&amp;gt;: для каждого кадра дубля либо кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; (выброшен).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;5-разбор-правок-вырезы-вставки-налипания&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 5. Разбор правок: вырезы, вставки, налипания ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; сырой: в нём вперемешку настоящие монтажные правки и артефакты (слепые зоны зрения, налипания). Их разбирает &#039;&#039;&#039;единое физическое правило&#039;&#039;&#039; в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;, а не набор порогов под контент.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;51-правка-реальна-только-при-стойком-сдвиге-уровня-offset-_level_decide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.1. Правка реальна только при стойком сдвиге УРОВНЯ offset (&amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Ключевая идея: считаем поканальный offset на сопоставленных кадрах &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{off}_{\text{asg}}=\text{pred}[\text{asg}]-\text{asg}&amp;lt;/math&amp;gt;. Для каждого выброшенного блока (drop-syn) сравниваем &#039;&#039;&#039;медиану уровня offset до и после&#039;&#039;&#039; блока (окно &amp;lt;code&amp;gt;LEVEL_WIN_S=4&amp;lt;/code&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\Delta=\big|\operatorname{median}(\text{off}_{\text{после}})-\operatorname{median}(\text{off}_{\text{до}})\big|.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta\le&amp;lt;/math&amp;gt; &amp;lt;code&amp;gt;LEVEL_EDIT_S&amp;lt;/code&amp;gt; (1 с) &#039;&#039;&#039;или&#039;&#039;&#039; блок короче &amp;lt;code&amp;gt;LEVEL_MIN_INS_S&amp;lt;/code&amp;gt; (2 с) → &#039;&#039;&#039;слепая зона / дрожь&#039;&#039;&#039; → восстановить интерполяцией (заодно перекрывает парный пробел рефа). Ложное восстановление безвредно по построению.&lt;br /&gt;
* иначе → &#039;&#039;&#039;реальная вставка&#039;&#039;&#039; → оставить выброшенной.&lt;br /&gt;
&lt;br /&gt;
Любой оставшийся пробел рефа при непрерывном дубле &amp;amp;gt; &amp;lt;code&amp;gt;CUT_MIN_S&amp;lt;/code&amp;gt; (0.3 с) — это &#039;&#039;&#039;реальный вырез&#039;&#039;&#039;. Доказано: пробел рефа при непрерывном дубле всегда сдвигает уровень ⇒ слепых вырезов не остаётся.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;52-фикс-пилы-вырез-только-при-устойчивом-сдвиге-а-не-при-выбросе-на-статике&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.2. Фикс «пилы»: вырез только при устойчивом сдвиге, а не при выбросе на статике ===&lt;br /&gt;
&lt;br /&gt;
На статике (длинный неподвижный план) зрение может дать &#039;&#039;временный&#039;&#039; выброс offset, который &#039;&#039;&#039;возвращается&#039;&#039;&#039; — это не вырез, а артефакт. Соседние пробелы склеиваются в кластер (&amp;lt;code&amp;gt;LEVEL_CUT_COALESCE_S=3&amp;lt;/code&amp;gt; с); если в пределах &amp;lt;code&amp;gt;LEVEL_CUT_RECOVER_S&amp;lt;/code&amp;gt; (8 с) уровень &#039;&#039;&#039;возвращается&#039;&#039;&#039; к доврезному — кластер восстанавливается (пила убрана), если держится — схлопывается в &#039;&#039;&#039;один&#039;&#039;&#039; интервал выреза (а не лесенку коротких тишин).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;53-детектор-налипания-_creep_drop-кейс-case04&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.3. Детектор «налипания» (&amp;lt;code&amp;gt;_creep_drop&amp;lt;/code&amp;gt;, кейс case/04) ===&lt;br /&gt;
&lt;br /&gt;
Особый дефект: вставка-повтор своего же контента с наложенным текстом присваивается Drop-DTW «ползучим ходом» вместо выброса. Сигнал кричащий: присвоенные кадры по содержимому &#039;&#039;&#039;чужие&#039;&#039;&#039; своим реф-партнёрам — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\approx0.00\text{–}0.02&amp;lt;/math&amp;gt; на много секунд подряд, тогда как честные матчи даже в тёмных сценах держат &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\ge0.22&amp;lt;/math&amp;gt;. Механика: скользящая медиана &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt; присвоенных кадров (окно ~1 с); связные зоны ниже &amp;lt;code&amp;gt;CREEP_COS=0.15&amp;lt;/code&amp;gt; длиннее &amp;lt;code&amp;gt;CREEP_MIN_S=3&amp;lt;/code&amp;gt; с → &amp;lt;code&amp;gt;pred=-1&amp;lt;/code&amp;gt;. Дальнейшую судьбу решает &amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt; (§5.1) — ложное срабатывание безвредно.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;54-возврат-краёв-_recover_edges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.4. Возврат краёв (&amp;lt;code&amp;gt;_recover_edges&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Free-start мог выкинуть &#039;&#039;&#039;совпадающее&#039;&#039;&#039; начало/конец (общий опенинг при разном вступлении BD↔WEB). Берём выброшенный кусок дубля против непокрытого куска рефа, гоняем &amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt; на подзадаче и вписываем вернувшиеся матчи (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;gt;&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;EDGE_COS_MIN=0.5&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;6-укладка-ломаной-ow--детект-ступеней--кусочно-линейная-карта&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 6. Укладка ломаной: (o,w) → детект ступеней → кусочно-линейная карта ==&lt;br /&gt;
&lt;br /&gt;
Финал зрения (&amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;) — превратить дискретный &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в &#039;&#039;&#039;гладкую кусочно-линейную карту&#039;&#039;&#039; с явными разрывами на резах. Это перенос сильного аудио-аппарата (&amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt;) на выход зрения: зрение видит структуру сдвига точнее аудио, а прежний «скат» (&amp;lt;code&amp;gt;maximum.accumulate&amp;lt;/code&amp;gt;) её подавлял.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;61-из-pred-в-узлы-сетки-o-w-vision_ow&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.1. Из &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в узлы сетки &amp;lt;code&amp;gt;(o, w)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;vision_ow&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для сопоставленных кадров считаем время в рефе и сдвиг в кадрах:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
t^{\text{ref}}_k=\frac{\text{pred}[k]}{\text{fps}_{\text{ref}}},\qquad&lt;br /&gt;
\text{sh}_k=\Big(\frac{k}{\text{fps}_{\text{dub}}}-t^{\text{ref}}_k\Big)\cdot\text{fps}_{\text{ref}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На каждом узле сетки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t\in T&amp;lt;/math&amp;gt; в окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt; (0.3 с) берём &#039;&#039;&#039;взвешенную медиану&#039;&#039;&#039; сдвига (вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;), и оцениваем уверенность как медиану &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;, умноженную на &#039;&#039;&#039;долю согласных&#039;&#039;&#039; (anchor-«agree») — кадров, чей сдвиг в пределах &amp;lt;code&amp;gt;tol_fr=2&amp;lt;/code&amp;gt; кадра от медианы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o[t]=\operatorname{wmedian}\big(\text{sh},\ \cos\big),\qquad&lt;br /&gt;
w[t]=\operatorname{median}(\cos)\cdot\underbrace{\tfrac{1}{n}\#\{|\text{sh}-o|\le2\}}_{\text{agree}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Взвешенная медиана — робастная статистика без порогов: сортируем по значению, идём по кумулятивному весу до половины. Пустые узлы интерполируются (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w=0&amp;lt;/math&amp;gt;), веса нормируются на медиану положительных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;62-детект-ступеней-dp-сегментация-ломаными-detectwarp_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.2. Детект ступеней: DP-сегментация ломаными (&amp;lt;code&amp;gt;detect.warp_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кривую &amp;lt;code&amp;gt;o(t)&amp;lt;/code&amp;gt; режем на сегменты, в каждом — &#039;&#039;&#039;одна взвешенная прямая&#039;&#039;&#039;; стыки сегментов = кандидаты в резы. Это DP по префиксным суммам.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: робастная прямая (IRLS).&#039;&#039;&#039; Обычная прямая по методу наименьших квадратов легко уводится одной выбросной точкой. IRLS (итеративно перевзвешенный МНК) лечит это: проводим прямую, смотрим, кто далеко отклонился, уменьшаем таким точкам вес и проводим заново. Через пару итераций прямая опирается на основную массу точек и игнорирует выбросы.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Взвешенная прямая (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, IRLS).&#039;&#039;&#039; Для точек &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t,y)&amp;lt;/math&amp;gt; с весами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; оценка наклона &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;a&amp;lt;/math&amp;gt; и сдвига &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;b&amp;lt;/math&amp;gt; — обычная взвешенная регрессия, но в &#039;&#039;&#039;2 итерации с даунвейтом выбросов&#039;&#039;&#039; (Iteratively Reweighted Least Squares):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\frac{\sum w\,(t-\bar t)(y-\bar y)}{\sum w\,(t-\bar t)^2},\quad b=\bar y-a\bar t,\quad&lt;br /&gt;
w\leftarrow w\cdot\exp\!\Big[-\big(\tfrac{r}{2.5\,s}\big)^2\Big],&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r=y-(at+b)&amp;lt;/math&amp;gt; — остаток, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;s&amp;lt;/math&amp;gt; — взвешенный СКО. Второй проход гасит выбросы → прямая не ведётся за единичными ложными узлами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Сегментация (&amp;lt;code&amp;gt;_wsegment&amp;lt;/code&amp;gt;).&#039;&#039;&#039; Стоимость сегмента &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[i,j)&amp;lt;/math&amp;gt; — взвешенная SSE наилучшей прямой (берётся за &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(1)&amp;lt;/math&amp;gt; из шести префиксных сумм &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sum w,\sum wt,\sum wy,\sum wt^2,&lt;br /&gt;
\sum wty,\sum wy^2&amp;lt;/math&amp;gt;). DP минимизирует суммарную ошибку плюс штраф &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt; за каждый сегмент (чем больше PEN, тем меньше резов), при минимальной длине сегмента &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{opt}[j]=\min_{i\le j-\text{msize}}\big(\text{opt}[i]+\text{SSE}(i,j)+\text{PEN}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Наклон каждого сегмента ограничен потолком &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt; (физика дрейфа ≤~2%). Рез ставится на стыке, если разрыв прямых &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\ge&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt; (3 кадра ≈ 120 мс).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;63-фильтр-резов-устойчивость-уровня-на-большом-окне-video_cut_filter&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.3. Фильтр резов: устойчивость уровня на большом окне (&amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
DP-сегментация склонна давать избыточные стыки; настоящий рез отличается тем, что уровень offset &#039;&#039;&#039;устойчиво&#039;&#039;&#039; разный слева и справа на &#039;&#039;&#039;большом&#039;&#039;&#039; окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt; (80 с), и считать его надо по &#039;&#039;&#039;надёжным&#039;&#039;&#039; узлам. Берём взвешенную медиану уровня слева/справа (робастна к «холмику» в окне — холмик у слепой зоны зрения возвращается, и медиана его игнорирует):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{рез реален}\iff \big|\operatorname{wmedian}(o_R,w^3)-\operatorname{wmedian}(o_L,w^3)\big|\ge\text{VC}\_\text{THR}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два важных нюанса:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;вес-гейт&#039;&#039;&#039; &amp;lt;code&amp;gt;VC_GATE=0.55&amp;lt;/code&amp;gt;: в окне берутся только узлы с весом выше квантиля 0.55 (&amp;lt;code&amp;gt;_gmask&amp;lt;/code&amp;gt;) — всплески у слепых зон отбрасываются;&lt;br /&gt;
* &#039;&#039;&#039;окно ограничено соседними резами&#039;&#039;&#039; (как &amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt; в аудио): на узком сегменте между двумя близкими резами окно &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm80&amp;lt;/math&amp;gt; с не должно перехлёстывать через соседний рез, иначе поймает чужой уровень и &#039;&#039;&#039;ложно подтвердит&#039;&#039;&#039; рез. Регрессия 0/36 дорожек.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;64-кусочно-линейная-укладка-piecewise_lines&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.4. Кусочно-линейная укладка (&amp;lt;code&amp;gt;piecewise_lines&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Между резами кладём &#039;&#039;&#039;одну робастную прямую&#039;&#039;&#039; (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;=0.45 к/с), изломы только на резах. Это усредняет весь кусок и потому нечувствительно к мелкой ряби &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;. Итог — кривая сдвига на сетке T.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;65-сборка-карты-build_map&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.5. Сборка карты (&amp;lt;code&amp;gt;build_map&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{shift}(t)=\operatorname{interp}(t;\ T,\ \text{curve}),\qquad&lt;br /&gt;
\tau(t)=t+\text{shift}(t)\cdot\frac{\text{FRAME}}{1000}\ \text{[с]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На резах в &amp;lt;code&amp;gt;shift&amp;lt;/code&amp;gt; ступень (разрыв) — её перекрывает &#039;&#039;&#039;тишина&#039;&#039;&#039; (вызывающий разносит контент на монтажном стыке). Между резами карта монотонна (наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;). Выход: &amp;lt;code&amp;gt;(grid, tg_s, cuts, o, w, curve)&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;tg_s&amp;lt;/code&amp;gt; идёт в ресэмпл аудио, &amp;lt;code&amp;gt;o/w/curve&amp;lt;/code&amp;gt; — в графики, &amp;lt;code&amp;gt;cuts&amp;lt;/code&amp;gt; — в тишину.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    PRED[pred / asg / cos] --&amp;gt; OW[&amp;quot;vision_ow → (o, w) на сетке T&amp;quot;]&lt;br /&gt;
    OW --&amp;gt; WC[warp_curve: DP-сегментация ломаными]&lt;br /&gt;
    WC --&amp;gt; VCF[video_cut_filter:&amp;lt;br/&amp;gt;устойчивость уровня ±80с, вес-гейт,&amp;lt;br/&amp;gt;окно по соседним резам]&lt;br /&gt;
    OW --&amp;gt; PL[piecewise_lines:&amp;lt;br/&amp;gt;робастная прямая между резами]&lt;br /&gt;
    VCF --&amp;gt; PL&lt;br /&gt;
    PL --&amp;gt; MAP[&amp;quot;build_map → tg_s + cuts&amp;quot;]&lt;br /&gt;
    MAP --&amp;gt; SIL[Тишина в резах]&lt;br /&gt;
    MAP --&amp;gt; RES[Ресэмпл аудио дубля на сетку рефа]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части I.&#039;&#039;&#039; Зрение превращает два видеофайла в карту &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\tau(t)&amp;lt;/math&amp;gt; «время рефа → время дубля» — монотонную кусочно-линейную, с явными монтажными резами, устойчивую к двойкам, лого и слепым зонам. По этой карте аудио дубля ресэмплится на таймлайн рефа; в резах ставится тишина. Дальше — слой звука.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-ii-звук--доводка-bandmuq-поверх-зрения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть II. Звук — доводка band/muq поверх зрения =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt; (оркестровка), &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt; (метрика), &amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt; (общий аппарат детекта, тот же, что у зрения), &amp;lt;code&amp;gt;anchor/assemble.py&amp;lt;/code&amp;gt;. Метод по умолчанию — &#039;&#039;&#039;band&#039;&#039;&#039; (DSP, 48 полос, без модели/лицензии); альтернатива &#039;&#039;&#039;muq&#039;&#039;&#039; (GPU-эмбеддинги, та же сборка).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;7-зачем-аудио-после-видео&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 7. Зачем аудио после видео ==&lt;br /&gt;
&lt;br /&gt;
Зрение положило дубль по картинке. Но между &#039;&#039;&#039;видео и аудио внутри самого файла&#039;&#039;&#039; бывает сдвиг, которого на картинке не видно:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;AV-sync delta&#039;&#039;&#039; — разный AAC priming / encoder delay (в JoJo измерено стабильные +89 мс между файлами, &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §4.4&amp;lt;/code&amp;gt;);&lt;br /&gt;
* &#039;&#039;&#039;студийный сдвиг M&amp;amp;amp;E&#039;&#039;&#039; — студия подвинула фон/музыку в миксе относительно своего видео;&lt;br /&gt;
* &#039;&#039;&#039;двойки&#039;&#039;&#039; — видео физически не различает соседние одинаковые кадры (предел ±83 мс), аудио снимает это ограничение.&lt;br /&gt;
&lt;br /&gt;
Поэтому второй слой меряет &#039;&#039;&#039;остаточный&#039;&#039;&#039; сдвиг аудио дубля (уже лежащего по видео-карте) относительно аудио рефа и доводит его. Важно: аудио-слой работает &#039;&#039;&#039;после&#039;&#039;&#039; простановки тишины в монтажных резах — он должен видеть реф/тишину в вырезе (как боевой эталон), а не сырой несинхронный дубль, иначе на стыке зрение↔аудио появляется ложный краевой рез.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;8-принцип-зрячих-свидетелей&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 8. Принцип зрячих свидетелей ==&lt;br /&gt;
&lt;br /&gt;
Отчёт &amp;lt;code&amp;gt;METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt; фиксирует намертво: &#039;&#039;&#039;наивная корреляция звука слепа&#039;&#039;&#039;, и это легко принять за «аудио измерить нельзя». Озвучка = чужой голос поверх той же музыки и эффектов (M&amp;amp;amp;E). Прямой NCC сырого сигнала ловит несовпадение голоса и возвращает шум. Лечится &#039;&#039;&#039;не другим инструментом, а изоляцией инварианта ПЕРЕД корреляцией&#039;&#039;&#039;. У каждого искажения дубляжа есть то, что оно не трогает:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Искажение дубляжа&lt;br /&gt;
! Что губит&lt;br /&gt;
! Инвариант (что не трогает)&lt;br /&gt;
! Как изолируем&lt;br /&gt;
|-&lt;br /&gt;
| Чужой голос диктора&lt;br /&gt;
| часть частотных полос&lt;br /&gt;
| чистые полосы M&amp;amp;amp;E&lt;br /&gt;
| &#039;&#039;&#039;48 лог-полос + взвеш. медиана&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Другой мастеринг/тембр&lt;br /&gt;
| тонкую структуру волны&lt;br /&gt;
| динамику громкости&lt;br /&gt;
| &#039;&#039;&#039;лог-огибающая RMS&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Повтор музыкального такта&lt;br /&gt;
| однозначность пика&lt;br /&gt;
| окна с одиночным пиком&lt;br /&gt;
| &#039;&#039;&#039;comb-veto&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Свидетели разной физики теряют чувствительность в &#039;&#039;&#039;разных&#039;&#039;&#039; местах → их сумма покрывает всю длину. Ниже подробно про основной свидетель боевого band — &#039;&#039;&#039;многополосную метрику&#039;&#039;&#039; (она же несёт оба первых инварианта: полосы против голоса, лог-энергия против мастеринга).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 8.1 — «Почему полосы видят сквозь голос».&#039;&#039;&#039; Спектрограмма окна озвучки: горизонтальными лентами выделены 48 лог-полос; полосы, занятые голосом диктора (≈300–3000 Гц), подсвечены красным («низкий контраст, малый вес»), полосы чистой музыки/эффектов — зелёным («высокий контраст, голосуют»). Снизу — та же сцена в рефе. Подпись: «Взвешенная медиана по полосам игнорирует испорченные голосом полосы — без знания, где голос».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;9-band-метрика-48-полос&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 9. band-метрика: 48 полос ==&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt;. Рецепт &amp;lt;code&amp;gt;NB48&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;sr=16000&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;NB=48&amp;lt;/math&amp;gt; лог-полос в диапазоне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;50\text{–}14000&amp;lt;/math&amp;gt; Гц, окно &amp;lt;code&amp;gt;win=5&amp;lt;/code&amp;gt; с, STFT &amp;lt;code&amp;gt;nfft=2048&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hop=256&amp;lt;/code&amp;gt; (⇒ кадровый темп огибающей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{fps}=sr/hop=62.5&amp;lt;/math&amp;gt; Гц), агрегация &amp;lt;code&amp;gt;wmedian&amp;lt;/code&amp;gt;, качество &amp;lt;code&amp;gt;agree&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;91-полосовые-огибающие-_benv&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.1. Полосовые огибающие (&amp;lt;code&amp;gt;_benv&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: STFT и полосы.&#039;&#039;&#039; STFT (кратковременное преобразование Фурье) режет звук на короткие кусочки и для каждого показывает, сколько в нём энергии на каждой частоте, — получается спектрограмма «время × частота». Мы группируем частоты в 48 логарифмических полос (как деления на эквалайзере) и следим за громкостью каждой полосы во времени — это и есть «огибающая полосы». Лог-шкала частот ближе к слуху: низкие частоты дробятся мельче, высокие — крупнее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Для окна сигнала &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;x&amp;lt;/math&amp;gt; берём STFT и мощность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;P=|Z|^2&amp;lt;/math&amp;gt;. Лог-частотная &#039;&#039;&#039;полосовая маска&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;BM\in\{0,1\}^{NB\times(nfft/2+1)}&amp;lt;/math&amp;gt; (границы — &amp;lt;code&amp;gt;logspace(50,14000,49)&amp;lt;/code&amp;gt;) суммирует мощность по полосам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
E_{b,t}=\sum_f BM_{b,f}\,P_{f,t},\qquad&lt;br /&gt;
\tilde E_{b,t}=\log(1+E_{b,t}).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждая полоса z-нормируется по времени (убираем общий уровень и масштаб — это инвариант к мастерингу):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\hat E_{b,t}=\frac{\tilde E_{b,t}-\operatorname{mean}_t \tilde E_b}{\operatorname{std}_t \tilde E_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;log1p&amp;lt;/code&amp;gt; + z-score — это и есть «лог-огибающая громкости полосы»: динамика, общая у дубля и рефа, остаётся; тембр выкидывается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;92-по-полосный-лаг-через-кросс-корреляцию-_om_core&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.2. По-полосный лаг через кросс-корреляцию (&amp;lt;code&amp;gt;_om_core&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для каждой полосы независимо считаем кросс-корреляцию огибающих рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{r}_b&amp;lt;/math&amp;gt; и дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{d}_b&amp;lt;/math&amp;gt; по времени (через FFT, знак &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;E_d\cdot\overline{E_r}&amp;lt;/math&amp;gt; даёт «правее=+»):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{cc}_{b}[\ell]=\big(\hat E^{d}_b \star \hat E^{r}_b\big)[\ell],\qquad&lt;br /&gt;
\ell\in[-M_f,\,M_f],\ \ M_f=\text{maxlag}\cdot\text{fps}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выраженность пика полосы (насколько он торчит над фоном):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{prom}_b=\max_\ell \text{cc}_b[\ell]-\operatorname{median}_\ell \text{cc}_b[\ell].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;93-агрегация-взвешенная-медиана-по-полосам-aggwmedian&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.3. Агрегация: взвешенная медиана по полосам (&amp;lt;code&amp;gt;agg=&amp;amp;quot;wmedian&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Вместо суммы корреляций (её подавляет голос) — &#039;&#039;&#039;по-полосный аргмакс-лаг&#039;&#039;&#039; и &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; этих лагов по 48 полосам, вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{prom}_b^{\text{promp}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{promp}=1&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{bsh}_b=\arg\max_\ell \text{cc}_b[\ell],\qquad&lt;br /&gt;
\hat\ell=\operatorname{wmedian}\big(\{\text{bsh}_b\},\ \{\text{prom}_b\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Медиана робастна: даже если голос испортил треть полос и они показывают «не туда», медиана держится за большинство чистых. Финальный сдвиг уточняется &#039;&#039;&#039;параболической интерполяцией&#039;&#039;&#039; вокруг &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat\ell&amp;lt;/math&amp;gt; по агрегированной поверхности и переводится в кадры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}=\frac{\hat\ell-\tfrac12\frac{y_{+}-y_{-}}{y_{+}-2y_0+y_{-}}}{\text{fps}}\cdot\frac{1000}{\text{FRAME}}\ \text{[кадры]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;94-качество--согласие-полос-qualityagree&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.4. Качество = согласие полос (&amp;lt;code&amp;gt;quality=&amp;amp;quot;agree&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Уверенность узла — &#039;&#039;&#039;доля полос, чей собственный аргмакс-лаг согласен&#039;&#039;&#039; с итоговым сдвигом (в пределах &amp;lt;code&amp;gt;tol=2&amp;lt;/code&amp;gt; кадра), взвешенная по выраженности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
w=\frac{\sum_b \text{prom}_b\cdot\mathbf{1}\big[|\text{bsh}_b-\text{off}|\le 2\big]}{\sum_b \text{prom}_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это самопроверка без внешней истины: если полосы единодушны — узлу можно верить; если разбрелись (повтор/тишина/голос везде) — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; мал, и детект/укладка такой узел не слушают.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    X[Окно 5с ref и dub] --&amp;gt; ST[STFT 2048/256]&lt;br /&gt;
    ST --&amp;gt; BM[48 лог-полос&amp;lt;br/&amp;gt;log1p + z-score по времени]&lt;br /&gt;
    BM --&amp;gt; CC[По-полосная кросс-корр&amp;lt;br/&amp;gt;cc_b lag]&lt;br /&gt;
    CC --&amp;gt; PR[prom_b = max − median]&lt;br /&gt;
    CC --&amp;gt; BSH[bsh_b = argmax lag]&lt;br /&gt;
    BSH --&amp;gt; WM[&amp;quot;wmedian по полосам (вес prom)&amp;quot;]&lt;br /&gt;
    PR --&amp;gt; WM&lt;br /&gt;
    WM --&amp;gt; OFF[off: сдвиг кадры + парабола]&lt;br /&gt;
    BSH --&amp;gt; AG[&amp;quot;agree: доля согласных полос → w&amp;quot;]&lt;br /&gt;
    OFF --&amp;gt; AG&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;muq как альтернатива.&#039;&#039;&#039; Метод &amp;lt;code&amp;gt;muq&amp;lt;/code&amp;gt; заменяет полосовую огибающую на GPU-эмбеддинги музыкальной модели, но &#039;&#039;&#039;дальше всё то же&#039;&#039;&#039; — та же поверхность &amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt;, тот же детект &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt;, та же сборка &amp;lt;code&amp;gt;assemble.py&amp;lt;/code&amp;gt;. band не требует новых зависимостей (только torch/numpy) и потому выбран дефолтом.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;10-грубая-off0--следящий-band&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 10. Грубая off0 + следящий band ==&lt;br /&gt;
&lt;br /&gt;
Рабочее окно band узкое — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MAXLAG=0.7&amp;lt;/code&amp;gt; с (точность ценой диапазона). Но сдвиг опенинга бывает крупнее. Поэтому, как и в зрении, две стадии: грубая «тропа» + точное слежение (&amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;101-грубая-off0-_coarse_off0&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.1. Грубая off0 (&amp;lt;code&amp;gt;_coarse_off0&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Та же band-метрика, но с &#039;&#039;&#039;широким&#039;&#039;&#039; окном лага &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;COARSE_LAG_S=2.5&amp;lt;/code&amp;gt; с — она видит большой сдвиг. Робастность только статистикой, без порогов под контент:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;страж края&#039;&#039;&#039; — если аргмакс на границе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, это «нет пика», вес обнуляется;&lt;br /&gt;
# скользящая &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; (окно &amp;lt;code&amp;gt;med_win_s=5&amp;lt;/code&amp;gt; с) сдвига;&lt;br /&gt;
# &#039;&#039;&#039;гейт согласия соседей&#039;&#039;&#039; — узел стабилен, если &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|o-\text{med}|\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;stab_tol=4&amp;lt;/code&amp;gt; кадра;&lt;br /&gt;
# повторная медиана только по стабильным + интерполяция → непрерывная &amp;lt;code&amp;gt;off0(t)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;102-слежение-bandbuild_arr-07с&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.2. Слежение (&amp;lt;code&amp;gt;band.build_arr ±0.7с&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль предварительно варпится по &amp;lt;code&amp;gt;off0&amp;lt;/code&amp;gt; (грубая коррекция), и band с &#039;&#039;&#039;узким&#039;&#039;&#039; окном &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.7&amp;lt;/math&amp;gt; с меряет &#039;&#039;&#039;остаток&#039;&#039;&#039; на пред-варпленном дубле. Полный сдвиг — сумма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o(t)=\text{off0}(t)+o_{\text{ост}}(t).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так узкое точное окно никогда не «теряет» крупный сдвиг — он уже снят грубой тропой, а band доводит лишь малый остаток. (Урок &amp;lt;code&amp;gt;EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;: GCC в грубом проходе подавляется голосом, band-грубый с медианой по полосам — нет.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;11-детект-ступеней-и-кривая-дрейфа&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 11. Детект ступеней и кривая дрейфа ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;111-детект--тот-же-аппарат-что-у-зрения-detectdetect&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.1. Детект — тот же аппарат, что у зрения (&amp;lt;code&amp;gt;detect.detect&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt; идёт в &#039;&#039;&#039;тот же&#039;&#039;&#039; &amp;lt;code&amp;gt;warp_curve&amp;lt;/code&amp;gt; (DP-сегментация взвешенными ломаными, §6.2), затем:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt;&#039;&#039;&#039; — рез реален только при устойчивом сдвиге &#039;&#039;&#039;хвоста&#039;&#039;&#039;: медиана уровня до/после на окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;win=25&amp;lt;/code&amp;gt; с, ограниченном соседними резами, с вес-гейтом; величина реза = это смещение. Аналог &amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt; из зрения, но для аудио.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;_edge_refine&amp;lt;/code&amp;gt;&#039;&#039;&#039; — отдельно выделяет рез в первом/последнем сегменте (краевая зона, короче &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;), где обычная сегментация его не выделит.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;112-кривая-дрейфа-надараяуотсон--потолок-скорости-_smooth_drift_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.2. Кривая дрейфа: Надарая–Уотсон + потолок скорости (&amp;lt;code&amp;gt;_smooth_drift_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: сглаживание Надарая–Уотсона.&#039;&#039;&#039; Это «скользящее среднее с приоритетом надёжным точкам»: значение кривой в каждый момент — среднее соседних измерений, но уверенные точки (большой вес &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt;) тянут сильнее, а шумные почти не влияют. Ширина «гауссова окна» &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma&amp;lt;/math&amp;gt; задаёт, насколько далёких соседей ещё учитывать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Внутри куска между резами строим гладкую кривую, которая &#039;&#039;&#039;следит за горками&#039;&#039;&#039; (плавный дрейф) и &#039;&#039;&#039;стоит на шуме&#039;&#039;&#039;. Это ядерное сглаживание Надарая–Уотсона с весом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w^{\text{qpow}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{qpow}=3&amp;lt;/math&amp;gt;, гауссово ядро &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma=3&amp;lt;/math&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{sm}(t)=\frac{\big(o\cdot w^{3}\big)\!*\!G_\sigma}{w^{3}\!*\!G_\sigma+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поверх — &#039;&#039;&#039;потолок скорости изменения сдвига&#039;&#039;&#039;: между соседними узлами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta\text{sm}|&amp;lt;/math&amp;gt; не больше &amp;lt;code&amp;gt;lim&amp;lt;/code&amp;gt;, что соответствует ускорению &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt; (1.25 %/с = &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{lim}=\frac{\text{max}\_\text{pct}\_\text{s}}{100}\cdot\text{STEP}\cdot\frac{1000}{\text{FRAME}}\approx0.15\ \text{кадра/узел}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Потолок не даёт кривой прыгнуть за единичными яркими выбросами в слепой зоне.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;113-опора-после-реза-фикс-2026-06-18&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.3. Опора после реза (фикс 2026-06-18) ===&lt;br /&gt;
&lt;br /&gt;
Тонкость: в куске, &#039;&#039;&#039;который начинается с реза&#039;&#039;&#039;, band теряет чувствительность на самом монтажном стыке (тусклые выбросы сразу за резом). Если вести потолок скорости от левого края куска, он занижает старт, и кривая ~35 с медленно вытягивается к плато. Идея пользователя: расходиться в &#039;&#039;&#039;обе стороны от самого уверенного якоря&#039;&#039;&#039; в первых &amp;lt;code&amp;gt;anchor_lookback_s=30&amp;lt;/code&amp;gt; с куска (= начало плато):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;   сдвиг&lt;br /&gt;
     ▲                  ● ● ● ● ● ● ●  ← плато (уверенные якоря)&lt;br /&gt;
     │      опора ↑ argmax w в первых 30с&lt;br /&gt;
     │     ╱   ╲        clamp влево к резу + вправо вдоль плато&lt;br /&gt;
     │  ✗ ╱     ╲&lt;br /&gt;
     │ слепой    &lt;br /&gt;
     │ старт      &lt;br /&gt;
     ┼──┊─────────────────────────────►  t&lt;br /&gt;
        рез&amp;lt;/pre&amp;gt;&lt;br /&gt;
Слепой старт подтягивается &#039;&#039;&#039;к&#039;&#039;&#039; плато, а не плато к старту. Первый кусок (без реза слева) — по-старому: левый край = опора. Приёмка: 47/55 дорожек не тронуты, изменения = только исправления (память &amp;lt;code&amp;gt;project_band_postcut_anchor_fix&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;12-варп-тишина-в-резах-заполнение-рефом&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 12. Варп, тишина в резах, заполнение рефом ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;121-кусочный-варп-_warp_piecewise&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.1. Кусочный варп (&amp;lt;code&amp;gt;_warp_piecewise&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль ресэмплится по кускам между резами: внутри куска — гладкая &amp;lt;code&amp;gt;wcurve&amp;lt;/code&amp;gt; (горки), на резе — резкий стык. Для выходного отсчёта &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;lt;/math&amp;gt; источник в дубле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{src}(t)=\Big(t+\text{wcurve}(t)\cdot\tfrac{\text{FRAME}}{1000}\Big)\cdot sr,\qquad&lt;br /&gt;
\text{out}[t]=\operatorname{interp}\big(\text{src}(t)\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;122-тишина-в-резах&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.2. Тишина в резах ===&lt;br /&gt;
&lt;br /&gt;
Непрерывный варп на монтажном стыке «переиграл» бы звук дважды, поэтому в резах ставится тишина (контент разносится):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;lt;0&amp;lt;/math&amp;gt; (нехватка дубля) → тишина &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta|&amp;lt;/math&amp;gt; по центру реза;&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;gt;0&amp;lt;/math&amp;gt; (лишнее вырезано) → узкий шов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.15&amp;lt;/math&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;123-заполнение-тишины-синхронным-рефом-_fill_silence_from_ref&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.3. Заполнение тишины синхронным рефом (&amp;lt;code&amp;gt;_fill_silence_from_ref&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Финальный проход — &#039;&#039;&#039;только после&#039;&#039;&#039; полного band/muq, когда дорожка уже синхронна рефу. Там, где озвучка молчит, а реф звучит, подставляем реф с кроссфейдом. Порог тишины измерен:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{RMS}_{\text{dBFS}}=20\log_{10}\!\Big(\tfrac{\text{RMS}}{32768}+\varepsilon\Big),\qquad&lt;br /&gt;
\text{fill}\iff \text{RMS}_{\text{дубль}}&amp;lt;-90\ \text{дБ}\ \wedge\ \text{RMS}_{\text{реф}}\ge-90\ \text{дБ}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порог &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-90&amp;lt;/math&amp;gt; дБ — в центре измеренного плато настоящих дыр (ниже &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-80&amp;lt;/math&amp;gt;), далеко от обрыва тихого контента (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-40\ldots-60&amp;lt;/math&amp;gt;), окно RMS 20 мс, мин. длина зоны 0.15 с, кроссфейд 30 мс. Где у обоих тишина — остаётся тишина; озвучка не теряется (трогаем только пустоты).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;Запрет (намертво).&#039;&#039;&#039; &amp;lt;code&amp;gt;fill_cuts&amp;lt;/code&amp;gt; — заливка вырезов рефом &#039;&#039;&#039;по видео, до&#039;&#039;&#039; анализа — запрещён навсегда: вставка в ещё несинхронную дорожку плодит мнимый рассинхрон. Разрешено только &amp;lt;code&amp;gt;fill_silence&amp;lt;/code&amp;gt; — заполнение &#039;&#039;&#039;после&#039;&#039;&#039; выравнивания, по синхронной дорожке (память &amp;lt;code&amp;gt;feedback_no_audio_replacement_fill_cuts&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;124-кросс-модальный-детектор-студийного-av-десинка-detect_av_desync&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.4. Кросс-модальный детектор студийного A/V-десинка (&amp;lt;code&amp;gt;detect_av_desync&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Read-only диагностика (на wav не влияет). После укладки &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; уже на сетке рефа; широким PHAT-окном (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, полоса 50–13500 Гц) меряем остаточный лаг M&amp;amp;amp;E к реф-аудио по окнам. Если он &#039;&#039;&#039;большой и устойчивый&#039;&#039;&#039; (медиана велика, MAD мал) — аудио источника смещено относительно его же видео (студийный дефект, надёжно не чинится) → дорожка помечается &#039;&#039;&#039;красным&#039;&#039;&#039;. Паттерн валидирован: AniLibria ep01 kamennyj-okean медиана −40к / MAD 2.2к против ~0/~0 у 10 здоровых.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    SIL[Дубль на сетке рефа + тишина в резах] --&amp;gt; OFF0[_coarse_off0&amp;lt;br/&amp;gt;band ±2.5с, медиана по полосам]&lt;br /&gt;
    OFF0 --&amp;gt; PRE[Пред-варп по off0]&lt;br /&gt;
    PRE --&amp;gt; BAND[&amp;quot;band ±0.7с → o_ост, w&amp;quot;]&lt;br /&gt;
    BAND --&amp;gt; SUM[&amp;quot;o = off0 + o_ост&amp;quot;]&lt;br /&gt;
    SUM --&amp;gt; DET[detect.detect&amp;lt;br/&amp;gt;warp_curve + tail_filter + edge_refine]&lt;br /&gt;
    DET --&amp;gt; DRIFT[_smooth_drift_curve&amp;lt;br/&amp;gt;Надарая–Уотсон + потолок 1.25%/с + опора-после-реза]&lt;br /&gt;
    DRIFT --&amp;gt; WP[_warp_piecewise + тишина в резах]&lt;br /&gt;
    WP --&amp;gt; FILL[_fill_silence_from_ref −90дБ]&lt;br /&gt;
    FILL --&amp;gt; AV[detect_av_desync&amp;lt;br/&amp;gt;красный флаг студийного десинка]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части II.&#039;&#039;&#039; Поверх видео-карты звук band/muq снимает остаточный сдвиг аудио, который видео не видит: многополосная метрика делает корреляцию зрячей сквозь голос и мастеринг, грубая off0 ловит крупный сдвиг, следящий band доводит остаток, общий аппарат &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt; ставит резы, кривая дрейфа с потолком скорости и опорой-после-реза кладёт гладко, тишина заполняется синхронным рефом. Дальше — как всё это читается на графиках.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-iii-графики--диагноз-с-одного-взгляда&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть III. Графики — диагноз с одного взгляда =&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/plots_unified.py&amp;lt;/code&amp;gt;. Проектная установка: алгоритмы доведены до состояния, где &#039;&#039;&#039;качество читается по графику, без ручной сверки в Premiere&#039;&#039;&#039;. Беглый взгляд на любой график даёт чёткий ответ «всё в порядке или нет». Поэтому график — не украшение, а главный инструмент приёмки и отладки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;13-зачем-графики-и-что-на-них-видно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 13. Зачем графики и что на них видно ==&lt;br /&gt;
&lt;br /&gt;
Единый график строится &#039;&#039;&#039;на каждую пару&#039;&#039;&#039; и показывает оба слоя сразу: &#039;&#039;&#039;верхняя панель — зрение&#039;&#039;&#039; (видео-укладка, всегда), &#039;&#039;&#039;нижняя — звук&#039;&#039;&#039; (доводка band/muq, если включена), на общей оси времени с синхронным зумом и ховером. Один взгляд отвечает на четыре вопроса:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Вопрос&lt;br /&gt;
! Где смотреть&lt;br /&gt;
|-&lt;br /&gt;
| Держится ли укладка за &#039;&#039;&#039;надёжные&#039;&#039;&#039; данные?&lt;br /&gt;
| цвет точек (уверенность): линия должна идти по ярким, не по тусклым&lt;br /&gt;
|-&lt;br /&gt;
| Есть ли необъяснённый &#039;&#039;&#039;перекос/увод&#039;&#039;&#039;?&lt;br /&gt;
| отклонение линии от облака точек&lt;br /&gt;
|-&lt;br /&gt;
| Верно ли найдены &#039;&#039;&#039;монтажные резы&#039;&#039;&#039;?&lt;br /&gt;
| вертикальные пунктиры и их подписи (мс)&lt;br /&gt;
|-&lt;br /&gt;
| Согласны ли &#039;&#039;&#039;зрение и звук&#039;&#039;&#039;?&lt;br /&gt;
| сравнение двух панелей в одной зоне&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;14-дрейф-форма-и-робастная-база-тейласена&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 14. Дрейф-форма и робастная база Тейла–Сена ==&lt;br /&gt;
&lt;br /&gt;
Сырой сдвиг по всей серии может быть сотни кадров (крупный масштаб оси), и на нём не видно ни плато, ни ступеней в десяток кадров. Поэтому обе панели рисуются в &#039;&#039;&#039;дрейф-форме&#039;&#039;&#039; — вычитается робастная линейная база, и всё разворачивается вокруг нуля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: база Тейла–Сена.&#039;&#039;&#039; Чтобы провести «среднюю линию» через облако точек и не дать единичным выбросам её перекосить, берут наклон между каждой парой точек и выбирают медианный — половина пар «за», половина «против». Это устойчивее обычной прямой и не требует никаких порогов отбраковки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
База оценивается &#039;&#039;&#039;методом Тейла–Сена&#039;&#039;&#039; (&amp;lt;code&amp;gt;_baseline&amp;lt;/code&amp;gt;) — он устойчив к выбросам, в отличие от МНК: на выборке точек (до 400) берутся все попарные наклоны, и базовый наклон — их медиана; сдвиг — медиана остатков:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\operatorname{median}_{i&amp;lt;j}\frac{y_j-y_i}{t_j-t_i},\qquad&lt;br /&gt;
b=\operatorname{median}_k\big(y_k-a\,t_k\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
База считается по &#039;&#039;&#039;телу&#039;&#039;&#039; дорожки (исключая голову/заставку: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;gt;\text{head}+5&amp;lt;/math&amp;gt; с). Затем:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{dev}_{\text{якорь}}=\text{shift}-(a\,t+b),\qquad&lt;br /&gt;
\text{dev}_{\text{кривая}}=\text{curve}-(a\,T+b),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а предел оси берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\max(12,\ p_{99}(|\text{dev}|)+4)&amp;lt;/math&amp;gt; кадров — авто-масштаб под реальный разброс. На резах в линию вставляется &amp;lt;code&amp;gt;NaN&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;_breaks&amp;lt;/code&amp;gt;), чтобы она &#039;&#039;&#039;не соединяла&#039;&#039;&#039; куски через разрыв — ступень видна как обрыв, а не как вертикаль.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 14.1 — «Сырой сдвиг vs дрейф-форма».&#039;&#039;&#039; Два графика друг над другом для одной дорожки: сверху сырой &amp;lt;code&amp;gt;shift(t)&amp;lt;/code&amp;gt; (линия уходит на −600 кадров, мелкие детали не видны); снизу та же дорожка в дрейф-форме (база вычтена, всё колеблется в ±15 кадров вокруг 0, видны и плато, и ступенька реза). Подпись: «Дрейф-форма вытаскивает структуру, которую крупный масштаб прячет».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;15-слои-цветокодирование-разбор-реальных-кейсов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 15. Слои, цветокодирование, разбор реальных кейсов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;151-слои-единого-графика&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.1. Слои единого графика ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхняя панель (ЗРЕНИЕ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки якорей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t_{\text{реф}},\ \text{dev}_{\text{якорь}})&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;cos&#039;&#039;&#039; (viridis, 0…1) — надёжность каждого видео-якоря;&lt;br /&gt;
* оранжевая линия — &#039;&#039;&#039;карта зрения&#039;&#039;&#039; в дрейф-форме, с разрывами на резах;&lt;br /&gt;
* crimson-пунктиры — &#039;&#039;&#039;резы зрения&#039;&#039;&#039; с подписью величины (мс);&lt;br /&gt;
* заголовок несёт абсолютную медиану сдвига.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Нижняя панель (ЗВУК):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(T,\ o)&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;w&#039;&#039;&#039; (cividis, 0…1.2) — уверенность band;&lt;br /&gt;
* серые точки — &#039;&#039;&#039;gcc-свидетель&#039;&#039;&#039; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с), справочно;&lt;br /&gt;
* зелёная линия — &#039;&#039;&#039;кривая band&#039;&#039;&#039; (дрейф), с разрывами на резах;&lt;br /&gt;
* purple-пунктиры — &#039;&#039;&#039;резы детекта&#039;&#039;&#039; аудио.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;gcc — НЕ судья.&#039;&#039;&#039; Серая gcc-линия на контенте проекта (закадр+музыка) почти слепа и скачет ±40к = шум; её нельзя брать за подтверждение укладки. Судьи сходимости — сам band (48 полос) / muq + уши/глаза на наложенных дорожках (память &amp;lt;code&amp;gt;feedback_gcc_ncc_blind_judge&amp;lt;/code&amp;gt;). gcc на графике — фон, не вердикт.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;152-как-цвет-читается-мгновенно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.2. Как цвет читается мгновенно ===&lt;br /&gt;
&lt;br /&gt;
Цвет точки = уверенность. Тёмные (фиолетовые/синие) — шумные, ярких (жёлтые/зелёные) — надёжные. &#039;&#039;&#039;Правильная линия идёт по ярким точкам.&#039;&#039;&#039; Если линия отклонилась, а рядом плотное облако ярких — это сигнал проблемы: либо увод укладки, либо линия последовала за тусклыми выбросами на краю. Это и есть «диагноз с одного взгляда».&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.1 — «Эталон единого графика (здоровая дорожка)».&#039;&#039;&#039; Реальный скрин двухпанельного графика: вверху зрение — плотное жёлтое облако якорей точно на 0, оранжевая линия плоская, резов нет; внизу звук — узкое облако вокруг 0, зелёная линия плоская. Подпись: «Так выглядит ОК: линии по ярким точкам, ноль отклонения, нет резов». &#039;&#039;(Скрин взять из &amp;lt;code&amp;gt;&amp;amp;lt;серия&amp;amp;gt;/_plots/&amp;amp;lt;stem&amp;amp;gt;__track.png&amp;lt;/code&amp;gt; любой здоровой пары.)&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.2 — «Перекос укладки (до фикса опоры-после-реза)».&#039;&#039;&#039; Скрин зоны 180–220 с AniDUB: после реза линия band провалилась вниз за парой тусклых (тёмных) якорей у края, хотя выше плотное плато ярких якорей на другом уровне. Рядом — тот же участок после фикса: линия держится за плато (опора = увереннейший якорь начала плато), слепой старт подтянут к плато. Подпись: «Линия не должна вестись за тусклыми единицами на краю».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.3 — «Ложный рез из-за слепоты зрения (ep05 Amazing, 950–979 с)».&#039;&#039;&#039; Скрин зоны с двумя близкими резами: до фикса между ними узкий клин и лишний рез (окно проверки ±80 с перехлестнуло через соседний рез и поймало чужой уровень); после фикса (окно ограничено соседними резами) лишний рез убран. Подпись: «Окно проверки уровня ограничивается соседними резами — иначе чужой уровень ложно подтверждает рез».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;153-носители-png-превью-и-интерактивный-html&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.3. Носители: PNG-превью и интерактивный HTML ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PNG&#039;&#039;&#039; (&amp;lt;code&amp;gt;matplotlib&amp;lt;/code&amp;gt;, Agg) — статичная миниатюра (1 или 2 панели) для быстрого взгляда в панели/отчёте.&lt;br /&gt;
* &#039;&#039;&#039;HTML&#039;&#039;&#039; (&amp;lt;code&amp;gt;plotly&amp;lt;/code&amp;gt;, self-contained, открывается офлайн) — для детального разбора. Точки и линия в WebGL (&amp;lt;code&amp;gt;scattergl&amp;lt;/code&amp;gt;), чтобы линия рисовалась поверх точек независимо от порядка; ховер &amp;lt;code&amp;gt;x unified&amp;lt;/code&amp;gt;, &#039;&#039;&#039;зум любой зоны вручную&#039;&#039;&#039;. Отдельные PNG каждого реза не создаём — HTML позволяет приблизить любой стык (решение 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
Read-only по контракту: падение рендера не роняет conform (ловит вызывающий).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Приложения =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-а-константы-и-их-обоснования&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение А. Константы и их обоснования ==&lt;br /&gt;
&lt;br /&gt;
Все ключевые числа калиброваны не «на глаз», а по &#039;&#039;&#039;плато нечувствительности&#039;&#039;&#039;: на свипе по диапазону результат (резы по датасету) не меняется — значит подстройки под контент нет.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;зрение&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Зрение ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;&lt;br /&gt;
| 128×72&lt;br /&gt;
| features&lt;br /&gt;
| размер серого кадра под SRM&lt;br /&gt;
|-&lt;br /&gt;
| clip &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| ±3&lt;br /&gt;
| features&lt;br /&gt;
| обрезка выбросов отклика фильтров&lt;br /&gt;
|-&lt;br /&gt;
| dim&lt;br /&gt;
| 18432&lt;br /&gt;
| features&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;2\cdot128\cdot72&amp;lt;/math&amp;gt;, два канала&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;K&amp;lt;/code&amp;gt; (coarse)&lt;br /&gt;
| 8&lt;br /&gt;
| coarse&lt;br /&gt;
| прорежение грубого прохода&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| coarse&lt;br /&gt;
| полуокно зануления при поиске 2-го пика&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VHI&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.60&lt;br /&gt;
| coarse&lt;br /&gt;
| порог «пик высокий» (надёжный якорь)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CMIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.12&lt;br /&gt;
| coarse&lt;br /&gt;
| порог различимости (пик одинок → не двойка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20&lt;br /&gt;
| dropdtw&lt;br /&gt;
| штраф выброса (базовый Drop-DTW)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;OPEN/EXT&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20 / 0.02&lt;br /&gt;
| dropdtw&lt;br /&gt;
| gap-open / gap-extend (острый шов)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DSYN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| плоская цена выброса кадра дубля (вставка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MATCH_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| guard: кадр с матчем лучше этого нельзя выбросить&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MARG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 120&lt;br /&gt;
| band_align&lt;br /&gt;
| полуширина следящей полосы (≈5 с)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CHUNK/OVERLAP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 4000 / 700&lt;br /&gt;
| band_align&lt;br /&gt;
| кусок Drop-DTW и перекрытие&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно агрегации якорей (плато 0.2–0.5)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 800&lt;br /&gt;
| vision_detect&lt;br /&gt;
| штраф DP-сегментации (плато 200–3000)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| vision_detect&lt;br /&gt;
| степень веса = (cos·agree)³ (плато 2–3)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.45&lt;br /&gt;
| vision_detect&lt;br /&gt;
| потолок наклона прямых, к/с (плато 0.3–0.6)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_MSIZE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30&lt;br /&gt;
| vision_detect&lt;br /&gt;
| мин. длина сегмента, с (плато 8–60)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно проверки устойчивости уровня (плато 45–120)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_GATE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.55&lt;br /&gt;
| vision_detect&lt;br /&gt;
| вес-гейт надёжных узлов (плато 0.3–0.8)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 200 мс&lt;br /&gt;
| vision_detect&lt;br /&gt;
| порог величины реза (плато 60–400 мс)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;звук&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Звук ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;sr&amp;lt;/code&amp;gt;&lt;br /&gt;
| 16000&lt;br /&gt;
| band&lt;br /&gt;
| частота анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NB&amp;lt;/code&amp;gt;&lt;br /&gt;
| 48&lt;br /&gt;
| band&lt;br /&gt;
| число лог-полос (50–14000 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;nfft/hop&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2048 / 256&lt;br /&gt;
| band&lt;br /&gt;
| STFT (темп огибающей 62.5 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;win&amp;lt;/code&amp;gt;&lt;br /&gt;
| 5 с&lt;br /&gt;
| band&lt;br /&gt;
| окно анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAXLAG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.7 с&lt;br /&gt;
| band&lt;br /&gt;
| рабочее окно лага (точность)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;COARSE_LAG_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2.5 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно грубой off0 (видит сдвиг опенинга)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tol&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2 кадра&lt;br /&gt;
| band&lt;br /&gt;
| допуск согласия полос (agree)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| 41.708 мс&lt;br /&gt;
| params&lt;br /&gt;
| мс/кадр (23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.5 с&lt;br /&gt;
| params&lt;br /&gt;
| шаг сетки T&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3 (120 мс)&lt;br /&gt;
| params&lt;br /&gt;
| порог величины реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 700&lt;br /&gt;
| params&lt;br /&gt;
| штраф DP-сегментации (детект)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| params&lt;br /&gt;
| степень веса якоря&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| params&lt;br /&gt;
| макс. наклон сегмента (≤~2%)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MSIZE_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 20 с&lt;br /&gt;
| params&lt;br /&gt;
| мин. длина сегмента&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 1.25 %/с&lt;br /&gt;
| apply&lt;br /&gt;
| потолок скорости кривой дрейфа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;anchor_lookback_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно поиска опоры-после-реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIL_FILL_DB&amp;lt;/code&amp;gt;&lt;br /&gt;
| −90 дБ&lt;br /&gt;
| align&lt;br /&gt;
| порог тишины озвучки (центр плато дыр)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;XFADE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 мс&lt;br /&gt;
| align&lt;br /&gt;
| кроссфейд заполнения рефом&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⏱ &#039;&#039;&#039;Правило прогонов&#039;&#039;&#039; (память &amp;lt;code&amp;gt;feedback_warn_long_runs&amp;lt;/code&amp;gt;): любой тест ≤10 мин — параллелить (клипы &amp;lt;code&amp;gt;--workers 8&amp;lt;/code&amp;gt;, конформы ×6 пар, синтетика &amp;lt;code&amp;gt;--workers 24&amp;lt;/code&amp;gt;) или резать объём; дольше 10 мин — только с явного согласия пользователя, оценку времени — заранее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-б-карта-файлов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение Б. Карта файлов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;src/track_muxer/conform/&lt;br /&gt;
├── features.py              SRM-отпечатки кадров (KB/D1, L2, dim 18432)&lt;br /&gt;
├── kernel/&lt;br /&gt;
│   ├── coarse.py            грубый проход: G=cs·crᵀ, якоря, LIS, оконный&lt;br /&gt;
│   ├── dropdtw.py           Drop-DTW (base/affine/guard/free_start)&lt;br /&gt;
│   └── band_align.py        следящая полоса + склейка кусков&lt;br /&gt;
├── vision_detect.py         УКЛАДКА зрения: (o,w) → детект → ломаная (ЕДИНСТВЕННАЯ карта)&lt;br /&gt;
├── align.py                 оркестровка пары: pred → разбор правок → ресэмпл → звук → график&lt;br /&gt;
└── anchor/                  АУДИО-доводка band/muq поверх зрения&lt;br /&gt;
    ├── apply.py             coarse_off0, следящий band, smooth_drift_curve, варп, fill&lt;br /&gt;
    ├── detect.py            ОБЩИЙ аппарат: wfit, warp_curve, tail_filter, edge_refine&lt;br /&gt;
    ├── assemble.py          freeze + сборка варпа&lt;br /&gt;
    ├── params.py            FRAME, T, STEP, PEN, QPOW, SMAX, MSIZE_S …&lt;br /&gt;
    ├── maps/band.py         48-полосная DSP-метрика (без модели) — дефолт&lt;br /&gt;
    ├── maps/muq.py          GPU-эмбеддинги (альтернатива)&lt;br /&gt;
    ├── plots_unified.py     ЕДИНЫЙ график зрение+звук (PNG + HTML)&lt;br /&gt;
    └── plots_html.py        отдельный HTML band (легаси-путь)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Тема&lt;br /&gt;
! Отчёт (суть)&lt;br /&gt;
|-&lt;br /&gt;
| Прорыв видео-синхрона&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_align/BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Методика зрячих свидетелей аудио&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Грубый→band-follow&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Аудио-аанкер (band/muq)&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_audio_auto.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Архитектура conform&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/conform_architecture_audit.md&amp;lt;/code&amp;gt; §14&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Версия документа:&#039;&#039;&#039; 2026-06-18, под архитектуру «анализатор зрения — единственная видео-карта; band/muq — доводка поверх». Числа и формулы сверены с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt;. При расхождении кода и документа — приоритет у кода, документ актуализировать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1011</id>
		<title>Track Muxer</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1011"/>
		<updated>2026-06-18T20:22:20Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Убраны литеральные &amp;lt;a name&amp;gt; якоря (отображались текстом)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Назначение документа.&#039;&#039;&#039; Полное инженерное описание двух алгоритмов выравнивания, на которых стоит модуль &amp;lt;code&amp;gt;conform&amp;lt;/code&amp;gt;: &#039;&#039;&#039;анализатора зрения&#039;&#039;&#039; (строит карту таймлайна по видеоряду) и &#039;&#039;&#039;аудио-доводки band/muq&#039;&#039;&#039; (правит остаточный сдвиг звука поверх зрения). Документ самодостаточен: математика, геометрия, рекурренты, точные константы и их обоснования, схемы пайплайнов и разбор реальных диагностических графиков. Читатель — инженер, который при желании сможет воспроизвести метод.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Конвенция знаков (сквозная).&#039;&#039;&#039; Сдвиг измеряем в &#039;&#039;кадрах рефа&#039;&#039;; &#039;&#039;&#039;правее (дубль отстаёт) = +&#039;&#039;&#039;, левее (спешит) = −. Один кадр = &#039;&#039;&#039;41.708 мс&#039;&#039;&#039; (23.976 fps, &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; в &amp;lt;code&amp;gt;anchor/params.py&amp;lt;/code&amp;gt;). Сетка анализа звука &amp;lt;code&amp;gt;T = arange(2.5, 1400, 0.5)&amp;lt;/code&amp;gt; с, шаг &amp;lt;code&amp;gt;STEP = 0.5&amp;lt;/code&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Источники.&#039;&#039;&#039; Всё, что ниже, сверено с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt; и отчётами &amp;lt;code&amp;gt;doc/reports/**&amp;lt;/code&amp;gt;. Ключевые файлы перечислены в Приложении Б.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;0-постановка-задачи-и-общая-архитектура&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 0. Постановка задачи и общая архитектура ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;01-что-мы-выравниваем&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.1. Что мы выравниваем ===&lt;br /&gt;
&lt;br /&gt;
Дан &#039;&#039;&#039;эпизод сериала&#039;&#039;&#039; (или отдельный фильм) в виде набора файлов разных озвучек (студий). Все они содержат &#039;&#039;&#039;один и тот же видеоряд&#039;&#039;&#039; (с точностью до монтажных отличий: разные заставки, вырезы, вставки, иногда другой fps), но &#039;&#039;&#039;разные аудиодорожки&#039;&#039;&#039;. Нужно для каждой пары &amp;lt;code&amp;gt;(референс, дубль)&amp;lt;/code&amp;gt; так преобразовать аудио дубля во времени, чтобы оно &#039;&#039;&#039;синхронно&#039;&#039;&#039; легло на таймлайн референса — и потом смуксировать все дорожки в один MKV.&lt;br /&gt;
&lt;br /&gt;
Главная трудность — это &#039;&#039;&#039;не «найти один сдвиг»&#039;&#039;&#039;. Сдвиг переменный по всей длине:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Источник рассинхрона&lt;br /&gt;
! Как выглядит на карте &amp;lt;code&amp;gt;(t_реф → t_дубль)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Постоянный сдвиг (priming, encoder delay)&lt;br /&gt;
| вертикальный параллельный перенос&lt;br /&gt;
|-&lt;br /&gt;
| Дрейф скорости (разный fps, PAL/NTSC)&lt;br /&gt;
| наклон кривой ≠ 1&lt;br /&gt;
|-&lt;br /&gt;
| Монтажный &#039;&#039;&#039;вырез&#039;&#039;&#039; в дубле (короче рефа)&lt;br /&gt;
| ступень вверх (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Монтажная &#039;&#039;&#039;вставка&#039;&#039;&#039; в дубле (длиннее рефа)&lt;br /&gt;
| ступень вниз (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Заставка/реклама в начале&lt;br /&gt;
| свободный левый конец&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И всё это — на контенте, где прямое применение обоих слоёв теряет чувствительность: видеоряд может идти «на двойках» (соседние кадры идентичны — анимация на 2 кадра, статичные планы), музыка повторяется (корреляция фиксируется на такт), у дубля чужой голос поверх общей музыки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 0.1 — «Проблема в одной картинке».&#039;&#039;&#039; Слева: две киноленты (реф и дубль) друг под другом, на дубле — вырезанный кусок (короче) и вставленная заставка в начале (длиннее), стрелки соответствия кадров между лентами расходятся (не параллельны). Справа: те же соответствия как точки в осях &amp;lt;code&amp;gt;t_реф&amp;lt;/code&amp;gt;(X) / &amp;lt;code&amp;gt;t_дубль&amp;lt;/code&amp;gt;(Y) — складываются в ломаную линию с наклоном и двумя вертикальными ступенями. Подпись: «Выравнивание = восстановить эту ломаную».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;02-двухслойная-архитектура-зрение-строит-карту-звук-доводит&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.2. Двухслойная архитектура: зрение строит карту, звук доводит ===&lt;br /&gt;
&lt;br /&gt;
Фундаментальное решение проекта (отчёт &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;): &#039;&#039;&#039;видеоряд — общий инвариант&#039;&#039;&#039; всех озвучек, поэтому грубую и структурную карту строим по видео, а аудио используем лишь как &#039;&#039;&#039;тонкую доводку остатка&#039;&#039;&#039;, который видео физически не видит (аудио источника бывает смещено относительно своего же видео).&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    subgraph IN[Вход]&lt;br /&gt;
        REF[Референс mp4/mkv]&lt;br /&gt;
        DUB[Дубль mp4/mkv]&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph VIS[&amp;quot;СЛОЙ 1 · ЗРЕНИЕ (видео-карта)&amp;quot;]&lt;br /&gt;
        SRM[SRM-отпечатки кадров&amp;lt;br/&amp;gt;features.py]&lt;br /&gt;
        CO[Грубый проход · LIS&amp;lt;br/&amp;gt;kernel/coarse.py]&lt;br /&gt;
        DD[Drop-DTW в полосе&amp;lt;br/&amp;gt;kernel/dropdtw + band_align]&lt;br /&gt;
        LV[Разбор правок&amp;lt;br/&amp;gt;level_decide / creep_drop]&lt;br /&gt;
        PW[Укладка ломаной + детект резов&amp;lt;br/&amp;gt;vision_detect.py]&lt;br /&gt;
        SRM --&amp;gt; CO --&amp;gt; DD --&amp;gt; LV --&amp;gt; PW&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph RES[Ресэмпл аудио дубля]&lt;br /&gt;
        MAP[Карта t_реф → t_дубль]&lt;br /&gt;
        SIL1[Тишина в монтажных резах]&lt;br /&gt;
        MAP --&amp;gt; SIL1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph AUD[&amp;quot;СЛОЙ 2 · ЗВУК (доводка)&amp;quot;]&lt;br /&gt;
        OFF0[Грубая off0 · band ±2.5с]&lt;br /&gt;
        FOL[band ±0.7с следит за off0]&lt;br /&gt;
        DET[Детект ступеней · detect.py]&lt;br /&gt;
        DRIFT[Кривая дрейфа + потолок скорости]&lt;br /&gt;
        WARP[Варп по кускам + тишина]&lt;br /&gt;
        OFF0 --&amp;gt; FOL --&amp;gt; DET --&amp;gt; DRIFT --&amp;gt; WARP&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    FILL[Заполнение тишины синхронным рефом&amp;lt;br/&amp;gt;fill_silence]&lt;br /&gt;
    PLOTS[Единый график зрение+звук&amp;lt;br/&amp;gt;plots_unified.py]&lt;br /&gt;
    OUT[Выровненный WAV → mux]&lt;br /&gt;
&lt;br /&gt;
    REF --&amp;gt; SRM&lt;br /&gt;
    DUB --&amp;gt; SRM&lt;br /&gt;
    PW --&amp;gt; MAP&lt;br /&gt;
    SIL1 --&amp;gt; OFF0&lt;br /&gt;
    WARP --&amp;gt; FILL --&amp;gt; PLOTS --&amp;gt; OUT&lt;br /&gt;
}}&lt;br /&gt;
Почему именно так, а не «всё по аудио» или «всё по видео»:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Только аудио&#039;&#039;&#039; упирается в потолок: 5 разных аудио-методов (PHAT, MFCC, 2D-спектр) дают &#039;&#039;одни и те же промахи&#039;&#039; на повторяющемся контенте — это не алгоритмическая, а &#039;&#039;информационная&#039;&#039; нехватка в одном аудиоканале (&amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §2&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Только видео&#039;&#039;&#039; не видит сдвиг аудио относительно видео внутри самого источника (AV-sync delta, студийный сдвиг M&amp;amp;amp;E) — его видит лишь аудио.&lt;br /&gt;
* Поэтому: &#039;&#039;&#039;видео даёт структуру&#039;&#039;&#039; (где вырез, где вставка, какой дрейф), &#039;&#039;&#039;аудио доводит&#039;&#039;&#039; субкадровый остаток там, где видео слепо по физике (двойки) или где аудио смещено относительно картинки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;03-сквозные-величины-и-обозначения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.3. Сквозные величины и обозначения ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Обозначение&lt;br /&gt;
! Смысл&lt;br /&gt;
! Значение / где&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| мс на кадр&lt;br /&gt;
| 41.708 (≈23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| сетка анализа звука&lt;br /&gt;
| &amp;lt;code&amp;gt;arange(2.5, 1400, 0.5)&amp;lt;/code&amp;gt; с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| шаг сетки&lt;br /&gt;
| 0.5 с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;o[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| измеренный сдвиг в точке t&lt;br /&gt;
| кадры рефа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;w[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| уверенность измерения&lt;br /&gt;
| норм. по медиане&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pred[j]&amp;lt;/code&amp;gt;&lt;br /&gt;
| для кадра дубля j — кадр рефа (или −1)&lt;br /&gt;
| целое&lt;br /&gt;
|-&lt;br /&gt;
| cos&lt;br /&gt;
| мера схожести двух кадров&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\in[-1,1]&amp;lt;/math&amp;gt;, на практике &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0,1]&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Дальше — два алгоритма по слоям.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-i-зрение--построитель-видео-карты&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть I. Зрение — построитель видео-карты =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/band_align.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;, разбор правок в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;. Это &#039;&#039;&#039;единственный&#039;&#039;&#039; построитель видео-карты в conform (прежний «скат» &amp;lt;code&amp;gt;_build_map_linear&amp;lt;/code&amp;gt; удалён 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;1-геометрия-задачи-карта-как-монотонная-кусочно-линейная-кривая&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 1. Геометрия задачи: карта как монотонная кусочно-линейная кривая ==&lt;br /&gt;
&lt;br /&gt;
Всё зрение сводится к одной геометрической цели — построить функцию&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tau:\; t_{\text{реф}} \;\longmapsto\; t_{\text{дубль}},&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
которая каждому моменту таймлайна референса сопоставляет момент в дубле, откуда брать звук. У этой функции жёсткая физическая структура:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;кусочно-линейная&#039;&#039;&#039; — внутри монтажного блока скорость постоянна (дрейф = наклон);&lt;br /&gt;
* &#039;&#039;&#039;монотонно неубывающая&#039;&#039;&#039; — время не идёт вспять (контент не переставляется);&lt;br /&gt;
* &#039;&#039;&#039;с вертикальными разрывами на монтажных стыках&#039;&#039;&#039; — вырез/вставка = ступень.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; t_дубль&lt;br /&gt;
   ▲&lt;br /&gt;
   │                                        ╱ (наклон ≈ fps_реф/fps_дубль)&lt;br /&gt;
   │                                   ╱╱╱&lt;br /&gt;
   │            вставка ↓         ╱╱╱&lt;br /&gt;
   │                        ┌────╱           ← разрыв вниз: в дубле лишний кусок&lt;br /&gt;
   │                        ┊&lt;br /&gt;
   │                   ╱╱╱╱╱┘&lt;br /&gt;
   │              ╱╱╱╱&lt;br /&gt;
   │   вырез ↑   ╱&lt;br /&gt;
   │         ┌──┘                            ← разрыв вверх: в дубле кусок вырезан&lt;br /&gt;
   │      ╱╱╱┊&lt;br /&gt;
   │  ╱╱╱╱   ┊&lt;br /&gt;
   └──┴──────┴──────────────────────────────►  t_реф&amp;lt;/pre&amp;gt;&lt;br /&gt;
Задача зрения — &#039;&#039;&#039;восстановить эту ломаную из зашумлённых измерений соответствия кадров&#039;&#039;&#039;. Шум двух видов: (а) ложные соответствия (двойки, лого, тёмные сцены), (б) пропуски (вырезы/вставки рвут непрерывность). Поэтому метод строится в три эшелона надёжности: грубая монотонная цепочка (§3) → точное выравнивание с пропусками (§4) → робастная ломаная поверх (§6).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;2-srm-отпечаток-кадра&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 2. SRM-отпечаток кадра ==&lt;br /&gt;
&lt;br /&gt;
Чтобы сравнивать кадры рефа и дубля, каждый кадр сворачивается в вектор-отпечаток так, чтобы &#039;&#039;&#039;схожесть контента&#039;&#039;&#039; была устойчива к различиям источника (BD vs Web, разный кодек, яркость, логотип студии). Наивный путь — сравнивать пиксели яркости — ломается на первом же логотипе и перепаде гаммы. Решение — &#039;&#039;&#039;SRM (Spatial Rich Model): отпечаток не яркости, а остаточного высокочастотного сигнала&#039;&#039;&#039; (текстуры/краёв).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: что такое «отпечаток кадра» и SRM.&#039;&#039;&#039; Отпечаток (дескриптор) — это короткий числовой слепок картинки: вектор чисел, по которому два кадра можно сравнить, не сличая их попиксельно. Хороший отпечаток одинаков у одного и того же кадра в разном качестве и не сбивается от логотипа или яркости. &#039;&#039;&#039;SRM (Spatial Rich Model)&#039;&#039;&#039; — приём из цифровой криминалистики изображений: вместо самой картинки берут её &#039;&#039;высокочастотный остаток&#039;&#039; — то, что остаётся после вычитания плавных перепадов яркости (контуры, мелкая текстура). Именно остаток устойчив к перекодированию и смене источника.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;21-конвейер-построения-featurespybuild_srm&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.1. Конвейер построения (&amp;lt;code&amp;gt;features.py::build_srm&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    A[Кадр ffmpeg&amp;lt;br/&amp;gt;-vsync 0] --&amp;gt; B[Серый 128×72]&lt;br /&gt;
    B --&amp;gt; C1[Свёртка KB&amp;lt;br/&amp;gt;лапласиан]&lt;br /&gt;
    B --&amp;gt; C2[Свёртка D1&amp;lt;br/&amp;gt;градиент x]&lt;br /&gt;
    C1 --&amp;gt; D1[clip ±3]&lt;br /&gt;
    C2 --&amp;gt; D2[clip ±3]&lt;br /&gt;
    D1 --&amp;gt; E1[L2-норма канала]&lt;br /&gt;
    D2 --&amp;gt; E2[L2-норма канала]&lt;br /&gt;
    E1 --&amp;gt; F[concat × 1/√2&amp;lt;br/&amp;gt;float16, dim=18432]&lt;br /&gt;
    E2 --&amp;gt; F&lt;br /&gt;
}}&lt;br /&gt;
Пошагово. Кадр приводится к серому &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;g&amp;lt;/math&amp;gt; размера &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;128\times72&amp;lt;/math&amp;gt; (&amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: свёртка ядром.&#039;&#039;&#039; Свёртка — это проход маленькой матрицей-окном по всем пикселям: в каждой точке берётся взвешенная сумма соседних пикселей. Подбором весов окно настраивают на нужный признак — перепад яркости, край, текстуру. Сумма весов, равная нулю, означает, что окно «не замечает» ровной заливки и реагирует только на изменения.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Здесь два фиксированных ядра свёртки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
K_B=\begin{pmatrix}-1&amp;amp;2&amp;amp;-1\\ 2&amp;amp;-4&amp;amp;2\\ -1&amp;amp;2&amp;amp;-1\end{pmatrix},&lt;br /&gt;
\qquad&lt;br /&gt;
D_1=\begin{pmatrix}0&amp;amp;0&amp;amp;0\\ 0&amp;amp;-1&amp;amp;1\\ 0&amp;amp;0&amp;amp;0\end{pmatrix}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; — дискретный оператор второго порядка (лапласиан-подобный, сумма коэффициентов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0&amp;lt;/math&amp;gt;). Он &#039;&#039;&#039;зануляет постоянную и линейную составляющую&#039;&#039;&#039; яркости и реагирует на &#039;&#039;кривизну&#039;&#039; — текстуру, контуры, мелкие детали. Именно эта высокочастотная «подпись» одинакова у одного и того же кадра в разных рипах.&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; — первая горизонтальная разность (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\partial/\partial x&amp;lt;/math&amp;gt;), ловит вертикальные края и направление градиента.&lt;br /&gt;
&lt;br /&gt;
Свёртка идёт с краевым режимом &amp;lt;code&amp;gt;reflect&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_K = (g * K_B)&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_D = (g * D_1)&amp;lt;/math&amp;gt;. Далее — обрезка выбросов и нормировка каждого канала по отдельности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tilde r_K=\frac{\operatorname{clip}(r_K,-3,3)}{\lVert\operatorname{clip}(r_K,-3,3)\rVert_2+\varepsilon},&lt;br /&gt;
\qquad&lt;br /&gt;
\tilde r_D=\frac{\operatorname{clip}(r_D,-3,3)}{\lVert\operatorname{clip}(r_D,-3,3)\rVert_2+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обрезка &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm3&amp;lt;/math&amp;gt; давит редкие яркие выбросы (блики, субтитры), L2-нормировка убирает зависимость от общего контраста кадра. Итоговый вектор кадра — конкатенация обоих каналов, масштабированная на &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v=\tfrac{1}{\sqrt2}\,[\,\tilde r_K \;\Vert\; \tilde r_D\,]\in\mathbb{R}^{18432},\qquad&lt;br /&gt;
18432 = 2\cdot128\cdot72 .&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;22-почему-cos--v_1cdot-v_2-напрямую&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.2. Почему &amp;lt;code&amp;gt;cos = v_1\cdot v_2&amp;lt;/code&amp;gt; напрямую ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: косинусная близость.&#039;&#039;&#039; Если каждый кадр представить стрелкой (вектором) в многомерном пространстве, то мерой их похожести служит угол между стрелками: сонаправленные (один контент) дают косинус ≈ 1, перпендикулярные (разный контент) ≈ 0. Косинус зависит только от направления, не от длины стрелок, — поэтому он устойчив к общей яркости и контрасту.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Множитель &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt; выбран так, что &#039;&#039;&#039;готовый вектор уже нормирован&#039;&#039;&#039;: поскольку каждый канал имеет единичную норму,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\lVert v\rVert_2^2=\tfrac12\big(\lVert\tilde r_K\rVert_2^2+\lVert\tilde r_D\rVert_2^2\big)=\tfrac12(1+1)=1.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поэтому скалярное произведение двух отпечатков &#039;&#039;&#039;сразу равно косинусной близости&#039;&#039;&#039;, и она же — среднее канальных косинусов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v_1\cdot v_2=\tfrac12\big(\cos_{K_B}+\cos_{D_1}\big)=\cos(v_1,v_2)\in[-1,1].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это ключ к скорости всего зрения: матрица всех попарных схожестей — одно матричное умножение &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G = C_s C_r^{\top}&amp;lt;/math&amp;gt; (косинусы без отдельной нормировки), а стоимость для Drop-DTW — просто &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C = 1 - \cos&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Инженерные детали, ломающие бит-в-бит совпадение, если их нарушить&#039;&#039;&#039; (&amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;): ffmpeg &amp;lt;code&amp;gt;-vsync 0&amp;lt;/code&amp;gt; (passthrough — индекс кадра = позиция во времени), &amp;lt;code&amp;gt;float16&amp;lt;/code&amp;gt;, порядок &amp;lt;code&amp;gt;clip → norm → concat&amp;lt;/code&amp;gt;. Декод потоковый, при &amp;lt;code&amp;gt;low_mem&amp;lt;/code&amp;gt; фичи пишутся в memmap, значения идентичны in-RAM пути.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 2.1 — «Что видит SRM».&#039;&#039;&#039; Три колонки: (1) исходный кадр видео с логотипом студии в углу; (2) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; (контуры/текстура, лого почти не выделяется — оно гладкое); (3) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; (вертикальные края). Подпись: «Отпечаток строится по высокочастотному остатку → устойчив к яркости и логотипу».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;3-грубый-проход-матрица-косинусов-надёжные-якоря-lis&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 3. Грубый проход: матрица косинусов, надёжные якоря, LIS ==&lt;br /&gt;
&lt;br /&gt;
Цель первого эшелона — получить &#039;&#039;&#039;грубую, но монотонную&#039;&#039;&#039; оценку сдвига &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;j&amp;lt;/math&amp;gt;, чтобы потом узкая полоса Drop-DTW искала точное соответствие не во всём рефе, а в коридоре вокруг этой линии. Файл &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;31-надёжные-якоря-_anchors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.1. Надёжные якоря (&amp;lt;code&amp;gt;_anchors&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кадры прореживаются с шагом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K=8&amp;lt;/math&amp;gt; (грубому проходу субкадровая точность не нужна). Строится матрица косинусов всех прореженных synth × ref:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
G = C_s\,C_r^{\top},\qquad G_{m,r}=\cos\big(v^{\text{syn}}_m, v^{\text{ref}}_r\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;m&amp;lt;/math&amp;gt; берётся лучший партнёр и его близость &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{best}_m=\arg\max_r G_{m,r}&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{bestv}_m=\max_r G_{m,r}&amp;lt;/math&amp;gt;. Затем окрестность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm W&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;W=80&amp;lt;/math&amp;gt; прореженных кадров) этого пика &#039;&#039;&#039;зануляется&#039;&#039;&#039;, и берётся второй максимум &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{second}_m&amp;lt;/math&amp;gt;. Кадр становится &#039;&#039;&#039;надёжным якорем&#039;&#039;&#039; только если выполнены оба условия:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\underbrace{\text{bestv}_m &amp;gt; V_{HI}}_{\text{пик высокий}}\quad\wedge\quad&lt;br /&gt;
\underbrace{\text{bestv}_m-\text{second}_m &amp;gt; C_{MIN}}_{\text{пик одинок (различим)}},\qquad&lt;br /&gt;
V_{HI}=0.60,\; C_{MIN}=0.12.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первое условие отсекает мусорные сцены (тьма, переход), второе — &#039;&#039;&#039;двойки и повторы&#039;&#039;&#039;: если в рефе есть второй почти такой же кадр, отрыв мал и кадр НЕ берётся в якоря. Это прямой ответ на главную проблему повторяющегося контента.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;32-монотонная-цепочка-через-lis-lis_nd-coarse_robust&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.2. Монотонная цепочка через LIS (&amp;lt;code&amp;gt;lis_nd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;coarse_robust&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: зачем «длиннейшая неубывающая подпоследовательность» (LIS).&#039;&#039;&#039; Среди якорей есть верные и единичные ложные. Верные обязаны идти «по возрастанию»: чем позже кадр в дубле, тем позже его партнёр в рефе (время не отматывается назад). LIS — классический алгоритм, который из разбросанных точек вытаскивает самую длинную цепочку, идущую только вверх; всё, что в неё не уложилось, почти наверняка ложные пары. Так костяк соответствия очищается без порогов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Надёжные якоря ещё могут содержать одиночные ложные пары. Истинное соответствие &#039;&#039;&#039;монотонно&#039;&#039;&#039; по рефу, поэтому из набора якорей &amp;lt;code&amp;gt;(synth_кадр → ref_кадр)&amp;lt;/code&amp;gt; извлекается &#039;&#039;&#039;длиннейшая неубывающая по ref подпоследовательность&#039;&#039;&#039; (Longest Non-Decreasing Subsequence, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(n\log n)&amp;lt;/math&amp;gt; через хвостовые индексы + бинарный поиск). Всё, что не легло в монотонную цепочку, отбрасывается как шум. По выжившим якорям линейной интерполяцией строится &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для всех кадров:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}(j)=\operatorname{interp}\big(j;\ \{a_k\},\ \{r_k-a_k\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; ref                     LIS оставляет монотонный костяк, шум выпадает&lt;br /&gt;
  ▲        ✗(ложный)&lt;br /&gt;
  │   ●───●        ✗&lt;br /&gt;
  │  ╱       ●───●───●&lt;br /&gt;
  │ ●                   ●───●     ← возрастающая цепочка = реальное соответствие&lt;br /&gt;
  │╱            ✗&lt;br /&gt;
  └───────────────────────────►  synth (кадр дубля)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;33-оконный-вариант-для-длинных-файлов-coarse_windowed&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.3. Оконный вариант для длинных файлов (&amp;lt;code&amp;gt;coarse_windowed&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Полная матрица &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G&amp;lt;/math&amp;gt; имеет размер &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; — для серии это гигабайты. Оконный проход режет дубль на окна по &amp;lt;code&amp;gt;CWIN=8000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;COV=2000&amp;lt;/code&amp;gt;; &#039;&#039;&#039;seed-offset перетекает из окна в окно&#039;&#039;&#039;, и в каждом окне ищется ref только в полосе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;CSR=4000&amp;lt;/code&amp;gt; вокруг seed. Память и время ограничены окном (не растут с длиной файла), а результат &#039;&#039;&#039;бит-в-бит совпал&#039;&#039;&#039; с полным проходом на 26 кейсах.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;4-drop-dtw-выравнивание-с-пропусками&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 4. Drop-DTW: выравнивание с пропусками ==&lt;br /&gt;
&lt;br /&gt;
Второй эшелон — точное соответствие кадров. Обычный DTW (Dynamic Time Warping) &#039;&#039;&#039;обязан&#039;&#039;&#039; сопоставить каждый кадр — он не умеет сказать «этого куска в рефе нет, это вставка». А у нас именно вырезы и вставки. Решение — &#039;&#039;&#039;Drop-DTW&#039;&#039;&#039;: DTW, которому разрешено &#039;&#039;выбрасывать&#039;&#039; кадры с обеих сторон за фиксированный штраф. Это ровно как &amp;lt;code&amp;gt;diff&amp;lt;/code&amp;gt; для текста или выравнивание последовательностей ДНК: ищем общий костяк, непарные куски помечаем как «вставка»/«удаление». Файл &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;41-стоимость-и-базовая-рекуррента-drop_dtw&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.1. Стоимость и базовая рекуррента (&amp;lt;code&amp;gt;drop_dtw&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Стоимость сопоставить кадр рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt; с кадром дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
C_{i,k}=1-\cos\!\big(v^{\text{ref}}_i,v^{\text{syn}}_k\big)\in[0,2].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами.&#039;&#039;&#039; Задача — перебрать все способы «растянуть / сжать / пропустить» кадры так, чтобы суммарная непохожесть сопоставленных кадров была минимальной, и сделать это не наивным перебором (астрономически долго), а накоплением лучшего ответа клетка за клеткой — это и есть динамическое программирование. Ниже — правило, по которому заполняется таблица лучших частичных ответов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Динамика заполняет таблицу &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_{i,k}&amp;lt;/math&amp;gt; — минимальную накопленную стоимость пути, пришедшего в клетку &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(i,k)&amp;lt;/math&amp;gt;. На каждом шаге доступны &#039;&#039;&#039;пять&#039;&#039;&#039; ходов (диагональ, два варпа, два выброса); старт — отдельная инициализация (свободный левый конец):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_{i,k}=\min\begin{cases}&lt;br /&gt;
D_{i-1,k-1}+C_{i,k} &amp;amp; \text{диагональ (1:1 совпадение)}\\&lt;br /&gt;
D_{i-1,k}+C_{i,k} &amp;amp; \text{вертикальный варп (ref тянется)}\\&lt;br /&gt;
D_{i,k-1}+C_{i,k} &amp;amp; \text{горизонтальный варп (дубль тянется)}\\&lt;br /&gt;
D_{i,k-1}+\text{DROP} &amp;amp; \text{выброс кадра дубля (вставка)}\\&lt;br /&gt;
D_{i-1,k}+\text{DROP} &amp;amp; \text{выброс кадра рефа (вырез)}\\&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с штрафом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{DROP}=0.20&amp;lt;/math&amp;gt;. Backpointer &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;B_{i,k}&amp;lt;/math&amp;gt; хранит, какой ход выбран, для восстановления пути. &#039;&#039;&#039;Свободные концы&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;свободный старт&#039;&#039;&#039; — &amp;lt;code&amp;gt;D[i,0] = C[i,0]&amp;lt;/code&amp;gt; для всех &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt;: путь может начаться с любой строки рефа (дубль не обязан стартовать с самого начала рефа);&lt;br /&gt;
* &#039;&#039;&#039;свободный конец&#039;&#039;&#039; — обратный ход начинается с &amp;lt;code&amp;gt;argmin&amp;lt;/code&amp;gt; последнего столбца: путь может закончиться на любой строке рефа.&lt;br /&gt;
&lt;br /&gt;
Восстановление пути (&amp;lt;code&amp;gt;backtrack&amp;lt;/code&amp;gt;) даёт три выхода: &amp;lt;code&amp;gt;pred[k]&amp;lt;/code&amp;gt; (для кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — его кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;drop_syn&amp;lt;/code&amp;gt; (маска выброшенных кадров дубля = вставки), &amp;lt;code&amp;gt;drop_ref&amp;lt;/code&amp;gt; (список выброшенных кадров рефа = вырезы).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 4.1 — «Путь по матрице Drop-DTW».&#039;&#039;&#039; Тепловая карта стоимости &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C_{i,k}&amp;lt;/math&amp;gt; (тёмное = похоже), ось X = кадры дубля, ось Y = кадры рефа. Поверх — оптимальный путь: диагональные участки (совпадение), один горизонтальный «провал» (выброс кадров дубля = вставка) и один вертикальный «прыжок» (выброс кадров рефа = вырез). Сбоку — легенда шести ходов. Подпись: «Зелёная линия — карта; разрывы = монтажные стыки».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;42-аффинный-штраф-острый-шов-вместо-размазни-drop_dtw_affine&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.2. Аффинный штраф: острый шов вместо размазни (&amp;lt;code&amp;gt;drop_dtw_affine&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Если вырез длинный, выбрасывать его «по одному кадру» дорого и нестабильно — путь норовит размазать выброс или убежать на логотипе. Аффинная (two-state) динамика берёт &#039;&#039;&#039;открытие&#039;&#039;&#039; прогона выброса дороже, чем его &#039;&#039;&#039;продолжение&#039;&#039;&#039; (как gap-open / gap-extend в биоинформатике):&lt;br /&gt;
&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; — «матч/варп» (платит &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C&amp;lt;/math&amp;gt;);&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; — «внутри выброса рефа»: открыть из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;OPEN=0.20&amp;lt;/code&amp;gt;, продлить из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;EXT=0.02&amp;lt;/code&amp;gt;;&lt;br /&gt;
* выброс кадра дубля (вставка) — плоская цена &amp;lt;code&amp;gt;DSYN=0.30&amp;lt;/code&amp;gt; внутри &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_r[i,k]=\min\big(M[i-1,k]+\text{OPEN},\; D_r[i-1,k]+\text{EXT}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дёшево продлевать (0.02) → длинный вырез схлопывается в один чистый шов, а не в рваную лесенку. На ровных логотипных участках состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; не открывается → путь не уходит в сторону.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;43-guard-нельзя-выбросить-кадр-с-хорошим-матчем-drop_dtw_affine_guard&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.3. Guard: нельзя выбросить кадр с хорошим матчем (&amp;lt;code&amp;gt;drop_dtw_affine_guard&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Без защиты прогон выброса склонен захватывать хвосты совпадающих кадров (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos=1&amp;lt;/math&amp;gt;), раздувая вставку. Guard заранее считает для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; лучший достижимый матч по столбцу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{colmin}_k=\min_i C_{i,k}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выброс этого кадра (ход drop-syn) разрешён &#039;&#039;&#039;только если&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{colmin}_k&amp;gt;\text{MATCH}\_\text{THR}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0.30&amp;lt;/math&amp;gt;); иначе цена выброса &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=\infty&amp;lt;/math&amp;gt; — кадр с хорошим матчем защищён от выбрасывания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;44-free-start-заставка-начала-выпадает-сама&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.4. Free-start: заставка начала выпадает сама ===&lt;br /&gt;
&lt;br /&gt;
Реклама/заставка в начале дубля не имеет пары в рефе. Free-start даёт &#039;&#039;&#039;свободный левый конец по дублю&#039;&#039;&#039;: путь может стартовать с любой клетки, выбросив ведущие кадры &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0..k-1]&amp;lt;/math&amp;gt; по цене вставки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k\cdot\text{DSYN}&amp;lt;/math&amp;gt;. Штраф растёт с &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — поэтому короткая заставка выпадает, но путь не «улетает» далеко.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{старт в } (i,k):\quad \text{cost}=C_{i,k}+k\cdot\text{DSYN}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;45-следящая-полоса-band_align&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.5. Следящая полоса (&amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Drop-DTW на полной матрице &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; невозможен по памяти. Поэтому он считается в &#039;&#039;&#039;узкой полосе&#039;&#039;&#039; вокруг грубой линии &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; (§3): дубль режется на куски по &amp;lt;code&amp;gt;CHUNK=4000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;OVERLAP=700&amp;lt;/code&amp;gt;; для каждого куска ref-окно берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[\min(\text{refk})-\text{MARG},\ \max(\text{refk})+\text{MARG}]&amp;lt;/math&amp;gt;, полуширина &amp;lt;code&amp;gt;MARG=120&amp;lt;/code&amp;gt; кадров (≈5 с — покрывает обычную правку). На стыках кусков для каждого кадра берётся ответ из того куска, где кадр &#039;&#039;&#039;дальше от края&#039;&#039;&#039; (надёжнее):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; кусок A:  ├──────────────────┤&lt;br /&gt;
 кусок B:              ├──────────────────┤&lt;br /&gt;
                       └ overlap ┘&lt;br /&gt;
 кадр в overlap → берём из того куска, где он ГЛУБЖЕ (центр надёжнее краёв)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    OFF[Грубая off из coarse] --&amp;gt; WIN[Окно ref ±MARG=120&amp;lt;br/&amp;gt;вокруг линии]&lt;br /&gt;
    CHUNK[Кусок дубля 4000&amp;lt;br/&amp;gt;overlap 700] --&amp;gt; WIN&lt;br /&gt;
    WIN --&amp;gt; C[&amp;quot;C = 1 − cos&amp;quot;]&lt;br /&gt;
    C --&amp;gt; DD[drop_dtw_affine_guard&amp;lt;br/&amp;gt;OPEN .20 / EXT .02 / DSYN .30&amp;lt;br/&amp;gt;MATCH_THR .30 / free_start]&lt;br /&gt;
    DD --&amp;gt; BT[backtrack → pred куска]&lt;br /&gt;
    BT --&amp;gt; MERGE[Склейка по «глубине» кадра]&lt;br /&gt;
}}&lt;br /&gt;
Итог слоя §4 — массив &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; длины &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s&amp;lt;/math&amp;gt;: для каждого кадра дубля либо кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; (выброшен).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;5-разбор-правок-вырезы-вставки-налипания&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 5. Разбор правок: вырезы, вставки, налипания ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; сырой: в нём вперемешку настоящие монтажные правки и артефакты (слепые зоны зрения, налипания). Их разбирает &#039;&#039;&#039;единое физическое правило&#039;&#039;&#039; в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;, а не набор порогов под контент.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;51-правка-реальна-только-при-стойком-сдвиге-уровня-offset-_level_decide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.1. Правка реальна только при стойком сдвиге УРОВНЯ offset (&amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Ключевая идея: считаем поканальный offset на сопоставленных кадрах &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{off}_{\text{asg}}=\text{pred}[\text{asg}]-\text{asg}&amp;lt;/math&amp;gt;. Для каждого выброшенного блока (drop-syn) сравниваем &#039;&#039;&#039;медиану уровня offset до и после&#039;&#039;&#039; блока (окно &amp;lt;code&amp;gt;LEVEL_WIN_S=4&amp;lt;/code&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\Delta=\big|\operatorname{median}(\text{off}_{\text{после}})-\operatorname{median}(\text{off}_{\text{до}})\big|.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta\le&amp;lt;/math&amp;gt; &amp;lt;code&amp;gt;LEVEL_EDIT_S&amp;lt;/code&amp;gt; (1 с) &#039;&#039;&#039;или&#039;&#039;&#039; блок короче &amp;lt;code&amp;gt;LEVEL_MIN_INS_S&amp;lt;/code&amp;gt; (2 с) → &#039;&#039;&#039;слепая зона / дрожь&#039;&#039;&#039; → восстановить интерполяцией (заодно перекрывает парный пробел рефа). Ложное восстановление безвредно по построению.&lt;br /&gt;
* иначе → &#039;&#039;&#039;реальная вставка&#039;&#039;&#039; → оставить выброшенной.&lt;br /&gt;
&lt;br /&gt;
Любой оставшийся пробел рефа при непрерывном дубле &amp;amp;gt; &amp;lt;code&amp;gt;CUT_MIN_S&amp;lt;/code&amp;gt; (0.3 с) — это &#039;&#039;&#039;реальный вырез&#039;&#039;&#039;. Доказано: пробел рефа при непрерывном дубле всегда сдвигает уровень ⇒ слепых вырезов не остаётся.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;52-фикс-пилы-вырез-только-при-устойчивом-сдвиге-а-не-при-выбросе-на-статике&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.2. Фикс «пилы»: вырез только при устойчивом сдвиге, а не при выбросе на статике ===&lt;br /&gt;
&lt;br /&gt;
На статике (длинный неподвижный план) зрение может дать &#039;&#039;временный&#039;&#039; выброс offset, который &#039;&#039;&#039;возвращается&#039;&#039;&#039; — это не вырез, а артефакт. Соседние пробелы склеиваются в кластер (&amp;lt;code&amp;gt;LEVEL_CUT_COALESCE_S=3&amp;lt;/code&amp;gt; с); если в пределах &amp;lt;code&amp;gt;LEVEL_CUT_RECOVER_S&amp;lt;/code&amp;gt; (8 с) уровень &#039;&#039;&#039;возвращается&#039;&#039;&#039; к доврезному — кластер восстанавливается (пила убрана), если держится — схлопывается в &#039;&#039;&#039;один&#039;&#039;&#039; интервал выреза (а не лесенку коротких тишин).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;53-детектор-налипания-_creep_drop-кейс-case04&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.3. Детектор «налипания» (&amp;lt;code&amp;gt;_creep_drop&amp;lt;/code&amp;gt;, кейс case/04) ===&lt;br /&gt;
&lt;br /&gt;
Особый дефект: вставка-повтор своего же контента с наложенным текстом присваивается Drop-DTW «ползучим ходом» вместо выброса. Сигнал кричащий: присвоенные кадры по содержимому &#039;&#039;&#039;чужие&#039;&#039;&#039; своим реф-партнёрам — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\approx0.00\text{–}0.02&amp;lt;/math&amp;gt; на много секунд подряд, тогда как честные матчи даже в тёмных сценах держат &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\ge0.22&amp;lt;/math&amp;gt;. Механика: скользящая медиана &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt; присвоенных кадров (окно ~1 с); связные зоны ниже &amp;lt;code&amp;gt;CREEP_COS=0.15&amp;lt;/code&amp;gt; длиннее &amp;lt;code&amp;gt;CREEP_MIN_S=3&amp;lt;/code&amp;gt; с → &amp;lt;code&amp;gt;pred=-1&amp;lt;/code&amp;gt;. Дальнейшую судьбу решает &amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt; (§5.1) — ложное срабатывание безвредно.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;54-возврат-краёв-_recover_edges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.4. Возврат краёв (&amp;lt;code&amp;gt;_recover_edges&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Free-start мог выкинуть &#039;&#039;&#039;совпадающее&#039;&#039;&#039; начало/конец (общий опенинг при разном вступлении BD↔WEB). Берём выброшенный кусок дубля против непокрытого куска рефа, гоняем &amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt; на подзадаче и вписываем вернувшиеся матчи (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;gt;&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;EDGE_COS_MIN=0.5&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;6-укладка-ломаной-ow--детект-ступеней--кусочно-линейная-карта&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 6. Укладка ломаной: (o,w) → детект ступеней → кусочно-линейная карта ==&lt;br /&gt;
&lt;br /&gt;
Финал зрения (&amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;) — превратить дискретный &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в &#039;&#039;&#039;гладкую кусочно-линейную карту&#039;&#039;&#039; с явными разрывами на резах. Это перенос сильного аудио-аппарата (&amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt;) на выход зрения: зрение видит структуру сдвига точнее аудио, а прежний «скат» (&amp;lt;code&amp;gt;maximum.accumulate&amp;lt;/code&amp;gt;) её подавлял.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;61-из-pred-в-узлы-сетки-o-w-vision_ow&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.1. Из &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в узлы сетки &amp;lt;code&amp;gt;(o, w)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;vision_ow&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для сопоставленных кадров считаем время в рефе и сдвиг в кадрах:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
t^{\text{ref}}_k=\frac{\text{pred}[k]}{\text{fps}_{\text{ref}}},\qquad&lt;br /&gt;
\text{sh}_k=\Big(\frac{k}{\text{fps}_{\text{dub}}}-t^{\text{ref}}_k\Big)\cdot\text{fps}_{\text{ref}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На каждом узле сетки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t\in T&amp;lt;/math&amp;gt; в окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt; (0.3 с) берём &#039;&#039;&#039;взвешенную медиану&#039;&#039;&#039; сдвига (вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;), и оцениваем уверенность как медиану &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;, умноженную на &#039;&#039;&#039;долю согласных&#039;&#039;&#039; (anchor-«agree») — кадров, чей сдвиг в пределах &amp;lt;code&amp;gt;tol_fr=2&amp;lt;/code&amp;gt; кадра от медианы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o[t]=\operatorname{wmedian}\big(\text{sh},\ \cos\big),\qquad&lt;br /&gt;
w[t]=\operatorname{median}(\cos)\cdot\underbrace{\tfrac{1}{n}\#\{|\text{sh}-o|\le2\}}_{\text{agree}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Взвешенная медиана — робастная статистика без порогов: сортируем по значению, идём по кумулятивному весу до половины. Пустые узлы интерполируются (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w=0&amp;lt;/math&amp;gt;), веса нормируются на медиану положительных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;62-детект-ступеней-dp-сегментация-ломаными-detectwarp_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.2. Детект ступеней: DP-сегментация ломаными (&amp;lt;code&amp;gt;detect.warp_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кривую &amp;lt;code&amp;gt;o(t)&amp;lt;/code&amp;gt; режем на сегменты, в каждом — &#039;&#039;&#039;одна взвешенная прямая&#039;&#039;&#039;; стыки сегментов = кандидаты в резы. Это DP по префиксным суммам.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: робастная прямая (IRLS).&#039;&#039;&#039; Обычная прямая по методу наименьших квадратов легко уводится одной выбросной точкой. IRLS (итеративно перевзвешенный МНК) лечит это: проводим прямую, смотрим, кто далеко отклонился, уменьшаем таким точкам вес и проводим заново. Через пару итераций прямая опирается на основную массу точек и игнорирует выбросы.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Взвешенная прямая (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, IRLS).&#039;&#039;&#039; Для точек &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t,y)&amp;lt;/math&amp;gt; с весами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; оценка наклона &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;a&amp;lt;/math&amp;gt; и сдвига &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;b&amp;lt;/math&amp;gt; — обычная взвешенная регрессия, но в &#039;&#039;&#039;2 итерации с даунвейтом выбросов&#039;&#039;&#039; (Iteratively Reweighted Least Squares):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\frac{\sum w\,(t-\bar t)(y-\bar y)}{\sum w\,(t-\bar t)^2},\quad b=\bar y-a\bar t,\quad&lt;br /&gt;
w\leftarrow w\cdot\exp\!\Big[-\big(\tfrac{r}{2.5\,s}\big)^2\Big],&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r=y-(at+b)&amp;lt;/math&amp;gt; — остаток, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;s&amp;lt;/math&amp;gt; — взвешенный СКО. Второй проход гасит выбросы → прямая не ведётся за единичными ложными узлами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Сегментация (&amp;lt;code&amp;gt;_wsegment&amp;lt;/code&amp;gt;).&#039;&#039;&#039; Стоимость сегмента &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[i,j)&amp;lt;/math&amp;gt; — взвешенная SSE наилучшей прямой (берётся за &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(1)&amp;lt;/math&amp;gt; из шести префиксных сумм &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sum w,\sum wt,\sum wy,\sum wt^2,&lt;br /&gt;
\sum wty,\sum wy^2&amp;lt;/math&amp;gt;). DP минимизирует суммарную ошибку плюс штраф &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt; за каждый сегмент (чем больше PEN, тем меньше резов), при минимальной длине сегмента &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{opt}[j]=\min_{i\le j-\text{msize}}\big(\text{opt}[i]+\text{SSE}(i,j)+\text{PEN}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Наклон каждого сегмента ограничен потолком &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt; (физика дрейфа ≤~2%). Рез ставится на стыке, если разрыв прямых &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\ge&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt; (3 кадра ≈ 120 мс).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;63-фильтр-резов-устойчивость-уровня-на-большом-окне-video_cut_filter&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.3. Фильтр резов: устойчивость уровня на большом окне (&amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
DP-сегментация склонна давать избыточные стыки; настоящий рез отличается тем, что уровень offset &#039;&#039;&#039;устойчиво&#039;&#039;&#039; разный слева и справа на &#039;&#039;&#039;большом&#039;&#039;&#039; окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt; (80 с), и считать его надо по &#039;&#039;&#039;надёжным&#039;&#039;&#039; узлам. Берём взвешенную медиану уровня слева/справа (робастна к «холмику» в окне — холмик у слепой зоны зрения возвращается, и медиана его игнорирует):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{рез реален}\iff \big|\operatorname{wmedian}(o_R,w^3)-\operatorname{wmedian}(o_L,w^3)\big|\ge\text{VC}\_\text{THR}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два важных нюанса:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;вес-гейт&#039;&#039;&#039; &amp;lt;code&amp;gt;VC_GATE=0.55&amp;lt;/code&amp;gt;: в окне берутся только узлы с весом выше квантиля 0.55 (&amp;lt;code&amp;gt;_gmask&amp;lt;/code&amp;gt;) — всплески у слепых зон отбрасываются;&lt;br /&gt;
* &#039;&#039;&#039;окно ограничено соседними резами&#039;&#039;&#039; (как &amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt; в аудио): на узком сегменте между двумя близкими резами окно &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm80&amp;lt;/math&amp;gt; с не должно перехлёстывать через соседний рез, иначе поймает чужой уровень и &#039;&#039;&#039;ложно подтвердит&#039;&#039;&#039; рез. Регрессия 0/36 дорожек.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;64-кусочно-линейная-укладка-piecewise_lines&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.4. Кусочно-линейная укладка (&amp;lt;code&amp;gt;piecewise_lines&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Между резами кладём &#039;&#039;&#039;одну робастную прямую&#039;&#039;&#039; (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;=0.45 к/с), изломы только на резах. Это усредняет весь кусок и потому нечувствительно к мелкой ряби &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;. Итог — кривая сдвига на сетке T.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;65-сборка-карты-build_map&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.5. Сборка карты (&amp;lt;code&amp;gt;build_map&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{shift}(t)=\operatorname{interp}(t;\ T,\ \text{curve}),\qquad&lt;br /&gt;
\tau(t)=t+\text{shift}(t)\cdot\frac{\text{FRAME}}{1000}\ \text{[с]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На резах в &amp;lt;code&amp;gt;shift&amp;lt;/code&amp;gt; ступень (разрыв) — её перекрывает &#039;&#039;&#039;тишина&#039;&#039;&#039; (вызывающий разносит контент на монтажном стыке). Между резами карта монотонна (наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;). Выход: &amp;lt;code&amp;gt;(grid, tg_s, cuts, o, w, curve)&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;tg_s&amp;lt;/code&amp;gt; идёт в ресэмпл аудио, &amp;lt;code&amp;gt;o/w/curve&amp;lt;/code&amp;gt; — в графики, &amp;lt;code&amp;gt;cuts&amp;lt;/code&amp;gt; — в тишину.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    PRED[pred / asg / cos] --&amp;gt; OW[&amp;quot;vision_ow → (o, w) на сетке T&amp;quot;]&lt;br /&gt;
    OW --&amp;gt; WC[warp_curve: DP-сегментация ломаными]&lt;br /&gt;
    WC --&amp;gt; VCF[video_cut_filter:&amp;lt;br/&amp;gt;устойчивость уровня ±80с, вес-гейт,&amp;lt;br/&amp;gt;окно по соседним резам]&lt;br /&gt;
    OW --&amp;gt; PL[piecewise_lines:&amp;lt;br/&amp;gt;робастная прямая между резами]&lt;br /&gt;
    VCF --&amp;gt; PL&lt;br /&gt;
    PL --&amp;gt; MAP[&amp;quot;build_map → tg_s + cuts&amp;quot;]&lt;br /&gt;
    MAP --&amp;gt; SIL[Тишина в резах]&lt;br /&gt;
    MAP --&amp;gt; RES[Ресэмпл аудио дубля на сетку рефа]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части I.&#039;&#039;&#039; Зрение превращает два видеофайла в карту &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\tau(t)&amp;lt;/math&amp;gt; «время рефа → время дубля» — монотонную кусочно-линейную, с явными монтажными резами, устойчивую к двойкам, лого и слепым зонам. По этой карте аудио дубля ресэмплится на таймлайн рефа; в резах ставится тишина. Дальше — слой звука.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-ii-звук--доводка-bandmuq-поверх-зрения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть II. Звук — доводка band/muq поверх зрения =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt; (оркестровка), &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt; (метрика), &amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt; (общий аппарат детекта, тот же, что у зрения), &amp;lt;code&amp;gt;anchor/assemble.py&amp;lt;/code&amp;gt;. Метод по умолчанию — &#039;&#039;&#039;band&#039;&#039;&#039; (DSP, 48 полос, без модели/лицензии); альтернатива &#039;&#039;&#039;muq&#039;&#039;&#039; (GPU-эмбеддинги, та же сборка).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;7-зачем-аудио-после-видео&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 7. Зачем аудио после видео ==&lt;br /&gt;
&lt;br /&gt;
Зрение положило дубль по картинке. Но между &#039;&#039;&#039;видео и аудио внутри самого файла&#039;&#039;&#039; бывает сдвиг, которого на картинке не видно:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;AV-sync delta&#039;&#039;&#039; — разный AAC priming / encoder delay (в JoJo измерено стабильные +89 мс между файлами, &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §4.4&amp;lt;/code&amp;gt;);&lt;br /&gt;
* &#039;&#039;&#039;студийный сдвиг M&amp;amp;amp;E&#039;&#039;&#039; — студия подвинула фон/музыку в миксе относительно своего видео;&lt;br /&gt;
* &#039;&#039;&#039;двойки&#039;&#039;&#039; — видео физически не различает соседние одинаковые кадры (предел ±83 мс), аудио снимает это ограничение.&lt;br /&gt;
&lt;br /&gt;
Поэтому второй слой меряет &#039;&#039;&#039;остаточный&#039;&#039;&#039; сдвиг аудио дубля (уже лежащего по видео-карте) относительно аудио рефа и доводит его. Важно: аудио-слой работает &#039;&#039;&#039;после&#039;&#039;&#039; простановки тишины в монтажных резах — он должен видеть реф/тишину в вырезе (как боевой эталон), а не сырой несинхронный дубль, иначе на стыке зрение↔аудио появляется ложный краевой рез.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;8-принцип-зрячих-свидетелей&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 8. Принцип зрячих свидетелей ==&lt;br /&gt;
&lt;br /&gt;
Отчёт &amp;lt;code&amp;gt;METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt; фиксирует намертво: &#039;&#039;&#039;наивная корреляция звука слепа&#039;&#039;&#039;, и это легко принять за «аудио измерить нельзя». Озвучка = чужой голос поверх той же музыки и эффектов (M&amp;amp;amp;E). Прямой NCC сырого сигнала ловит несовпадение голоса и возвращает шум. Лечится &#039;&#039;&#039;не другим инструментом, а изоляцией инварианта ПЕРЕД корреляцией&#039;&#039;&#039;. У каждого искажения дубляжа есть то, что оно не трогает:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Искажение дубляжа&lt;br /&gt;
! Что губит&lt;br /&gt;
! Инвариант (что не трогает)&lt;br /&gt;
! Как изолируем&lt;br /&gt;
|-&lt;br /&gt;
| Чужой голос диктора&lt;br /&gt;
| часть частотных полос&lt;br /&gt;
| чистые полосы M&amp;amp;amp;E&lt;br /&gt;
| &#039;&#039;&#039;48 лог-полос + взвеш. медиана&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Другой мастеринг/тембр&lt;br /&gt;
| тонкую структуру волны&lt;br /&gt;
| динамику громкости&lt;br /&gt;
| &#039;&#039;&#039;лог-огибающая RMS&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Повтор музыкального такта&lt;br /&gt;
| однозначность пика&lt;br /&gt;
| окна с одиночным пиком&lt;br /&gt;
| &#039;&#039;&#039;comb-veto&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Свидетели разной физики теряют чувствительность в &#039;&#039;&#039;разных&#039;&#039;&#039; местах → их сумма покрывает всю длину. Ниже подробно про основной свидетель боевого band — &#039;&#039;&#039;многополосную метрику&#039;&#039;&#039; (она же несёт оба первых инварианта: полосы против голоса, лог-энергия против мастеринга).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 8.1 — «Почему полосы видят сквозь голос».&#039;&#039;&#039; Спектрограмма окна озвучки: горизонтальными лентами выделены 48 лог-полос; полосы, занятые голосом диктора (≈300–3000 Гц), подсвечены красным («низкий контраст, малый вес»), полосы чистой музыки/эффектов — зелёным («высокий контраст, голосуют»). Снизу — та же сцена в рефе. Подпись: «Взвешенная медиана по полосам игнорирует испорченные голосом полосы — без знания, где голос».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;9-band-метрика-48-полос&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 9. band-метрика: 48 полос ==&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt;. Рецепт &amp;lt;code&amp;gt;NB48&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;sr=16000&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;NB=48&amp;lt;/math&amp;gt; лог-полос в диапазоне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;50\text{–}14000&amp;lt;/math&amp;gt; Гц, окно &amp;lt;code&amp;gt;win=5&amp;lt;/code&amp;gt; с, STFT &amp;lt;code&amp;gt;nfft=2048&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hop=256&amp;lt;/code&amp;gt; (⇒ кадровый темп огибающей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{fps}=sr/hop=62.5&amp;lt;/math&amp;gt; Гц), агрегация &amp;lt;code&amp;gt;wmedian&amp;lt;/code&amp;gt;, качество &amp;lt;code&amp;gt;agree&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;91-полосовые-огибающие-_benv&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.1. Полосовые огибающие (&amp;lt;code&amp;gt;_benv&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: STFT и полосы.&#039;&#039;&#039; STFT (кратковременное преобразование Фурье) режет звук на короткие кусочки и для каждого показывает, сколько в нём энергии на каждой частоте, — получается спектрограмма «время × частота». Мы группируем частоты в 48 логарифмических полос (как деления на эквалайзере) и следим за громкостью каждой полосы во времени — это и есть «огибающая полосы». Лог-шкала частот ближе к слуху: низкие частоты дробятся мельче, высокие — крупнее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Для окна сигнала &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;x&amp;lt;/math&amp;gt; берём STFT и мощность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;P=|Z|^2&amp;lt;/math&amp;gt;. Лог-частотная &#039;&#039;&#039;полосовая маска&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;BM\in\{0,1\}^{NB\times(nfft/2+1)}&amp;lt;/math&amp;gt; (границы — &amp;lt;code&amp;gt;logspace(50,14000,49)&amp;lt;/code&amp;gt;) суммирует мощность по полосам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
E_{b,t}=\sum_f BM_{b,f}\,P_{f,t},\qquad&lt;br /&gt;
\tilde E_{b,t}=\log(1+E_{b,t}).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждая полоса z-нормируется по времени (убираем общий уровень и масштаб — это инвариант к мастерингу):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\hat E_{b,t}=\frac{\tilde E_{b,t}-\operatorname{mean}_t \tilde E_b}{\operatorname{std}_t \tilde E_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;log1p&amp;lt;/code&amp;gt; + z-score — это и есть «лог-огибающая громкости полосы»: динамика, общая у дубля и рефа, остаётся; тембр выкидывается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;92-по-полосный-лаг-через-кросс-корреляцию-_om_core&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.2. По-полосный лаг через кросс-корреляцию (&amp;lt;code&amp;gt;_om_core&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для каждой полосы независимо считаем кросс-корреляцию огибающих рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{r}_b&amp;lt;/math&amp;gt; и дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{d}_b&amp;lt;/math&amp;gt; по времени (через FFT, знак &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;E_d\cdot\overline{E_r}&amp;lt;/math&amp;gt; даёт «правее=+»):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{cc}_{b}[\ell]=\big(\hat E^{d}_b \star \hat E^{r}_b\big)[\ell],\qquad&lt;br /&gt;
\ell\in[-M_f,\,M_f],\ \ M_f=\text{maxlag}\cdot\text{fps}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выраженность пика полосы (насколько он торчит над фоном):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{prom}_b=\max_\ell \text{cc}_b[\ell]-\operatorname{median}_\ell \text{cc}_b[\ell].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;93-агрегация-взвешенная-медиана-по-полосам-aggwmedian&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.3. Агрегация: взвешенная медиана по полосам (&amp;lt;code&amp;gt;agg=&amp;amp;quot;wmedian&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Вместо суммы корреляций (её подавляет голос) — &#039;&#039;&#039;по-полосный аргмакс-лаг&#039;&#039;&#039; и &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; этих лагов по 48 полосам, вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{prom}_b^{\text{promp}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{promp}=1&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{bsh}_b=\arg\max_\ell \text{cc}_b[\ell],\qquad&lt;br /&gt;
\hat\ell=\operatorname{wmedian}\big(\{\text{bsh}_b\},\ \{\text{prom}_b\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Медиана робастна: даже если голос испортил треть полос и они показывают «не туда», медиана держится за большинство чистых. Финальный сдвиг уточняется &#039;&#039;&#039;параболической интерполяцией&#039;&#039;&#039; вокруг &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat\ell&amp;lt;/math&amp;gt; по агрегированной поверхности и переводится в кадры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}=\frac{\hat\ell-\tfrac12\frac{y_{+}-y_{-}}{y_{+}-2y_0+y_{-}}}{\text{fps}}\cdot\frac{1000}{\text{FRAME}}\ \text{[кадры]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;94-качество--согласие-полос-qualityagree&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.4. Качество = согласие полос (&amp;lt;code&amp;gt;quality=&amp;amp;quot;agree&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Уверенность узла — &#039;&#039;&#039;доля полос, чей собственный аргмакс-лаг согласен&#039;&#039;&#039; с итоговым сдвигом (в пределах &amp;lt;code&amp;gt;tol=2&amp;lt;/code&amp;gt; кадра), взвешенная по выраженности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
w=\frac{\sum_b \text{prom}_b\cdot\mathbf{1}\big[|\text{bsh}_b-\text{off}|\le 2\big]}{\sum_b \text{prom}_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это самопроверка без внешней истины: если полосы единодушны — узлу можно верить; если разбрелись (повтор/тишина/голос везде) — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; мал, и детект/укладка такой узел не слушают.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    X[Окно 5с ref и dub] --&amp;gt; ST[STFT 2048/256]&lt;br /&gt;
    ST --&amp;gt; BM[48 лог-полос&amp;lt;br/&amp;gt;log1p + z-score по времени]&lt;br /&gt;
    BM --&amp;gt; CC[По-полосная кросс-корр&amp;lt;br/&amp;gt;cc_b lag]&lt;br /&gt;
    CC --&amp;gt; PR[prom_b = max − median]&lt;br /&gt;
    CC --&amp;gt; BSH[bsh_b = argmax lag]&lt;br /&gt;
    BSH --&amp;gt; WM[&amp;quot;wmedian по полосам (вес prom)&amp;quot;]&lt;br /&gt;
    PR --&amp;gt; WM&lt;br /&gt;
    WM --&amp;gt; OFF[off: сдвиг кадры + парабола]&lt;br /&gt;
    BSH --&amp;gt; AG[&amp;quot;agree: доля согласных полос → w&amp;quot;]&lt;br /&gt;
    OFF --&amp;gt; AG&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;muq как альтернатива.&#039;&#039;&#039; Метод &amp;lt;code&amp;gt;muq&amp;lt;/code&amp;gt; заменяет полосовую огибающую на GPU-эмбеддинги музыкальной модели, но &#039;&#039;&#039;дальше всё то же&#039;&#039;&#039; — та же поверхность &amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt;, тот же детект &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt;, та же сборка &amp;lt;code&amp;gt;assemble.py&amp;lt;/code&amp;gt;. band не требует новых зависимостей (только torch/numpy) и потому выбран дефолтом.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;10-грубая-off0--следящий-band&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 10. Грубая off0 + следящий band ==&lt;br /&gt;
&lt;br /&gt;
Рабочее окно band узкое — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MAXLAG=0.7&amp;lt;/code&amp;gt; с (точность ценой диапазона). Но сдвиг опенинга бывает крупнее. Поэтому, как и в зрении, две стадии: грубая «тропа» + точное слежение (&amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;101-грубая-off0-_coarse_off0&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.1. Грубая off0 (&amp;lt;code&amp;gt;_coarse_off0&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Та же band-метрика, но с &#039;&#039;&#039;широким&#039;&#039;&#039; окном лага &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;COARSE_LAG_S=2.5&amp;lt;/code&amp;gt; с — она видит большой сдвиг. Робастность только статистикой, без порогов под контент:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;страж края&#039;&#039;&#039; — если аргмакс на границе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, это «нет пика», вес обнуляется;&lt;br /&gt;
# скользящая &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; (окно &amp;lt;code&amp;gt;med_win_s=5&amp;lt;/code&amp;gt; с) сдвига;&lt;br /&gt;
# &#039;&#039;&#039;гейт согласия соседей&#039;&#039;&#039; — узел стабилен, если &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|o-\text{med}|\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;stab_tol=4&amp;lt;/code&amp;gt; кадра;&lt;br /&gt;
# повторная медиана только по стабильным + интерполяция → непрерывная &amp;lt;code&amp;gt;off0(t)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;102-слежение-bandbuild_arr-07с&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.2. Слежение (&amp;lt;code&amp;gt;band.build_arr ±0.7с&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль предварительно варпится по &amp;lt;code&amp;gt;off0&amp;lt;/code&amp;gt; (грубая коррекция), и band с &#039;&#039;&#039;узким&#039;&#039;&#039; окном &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.7&amp;lt;/math&amp;gt; с меряет &#039;&#039;&#039;остаток&#039;&#039;&#039; на пред-варпленном дубле. Полный сдвиг — сумма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o(t)=\text{off0}(t)+o_{\text{ост}}(t).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так узкое точное окно никогда не «теряет» крупный сдвиг — он уже снят грубой тропой, а band доводит лишь малый остаток. (Урок &amp;lt;code&amp;gt;EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;: GCC в грубом проходе подавляется голосом, band-грубый с медианой по полосам — нет.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;11-детект-ступеней-и-кривая-дрейфа&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 11. Детект ступеней и кривая дрейфа ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;111-детект--тот-же-аппарат-что-у-зрения-detectdetect&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.1. Детект — тот же аппарат, что у зрения (&amp;lt;code&amp;gt;detect.detect&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt; идёт в &#039;&#039;&#039;тот же&#039;&#039;&#039; &amp;lt;code&amp;gt;warp_curve&amp;lt;/code&amp;gt; (DP-сегментация взвешенными ломаными, §6.2), затем:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt;&#039;&#039;&#039; — рез реален только при устойчивом сдвиге &#039;&#039;&#039;хвоста&#039;&#039;&#039;: медиана уровня до/после на окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;win=25&amp;lt;/code&amp;gt; с, ограниченном соседними резами, с вес-гейтом; величина реза = это смещение. Аналог &amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt; из зрения, но для аудио.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;_edge_refine&amp;lt;/code&amp;gt;&#039;&#039;&#039; — отдельно выделяет рез в первом/последнем сегменте (краевая зона, короче &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;), где обычная сегментация его не выделит.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;112-кривая-дрейфа-надараяуотсон--потолок-скорости-_smooth_drift_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.2. Кривая дрейфа: Надарая–Уотсон + потолок скорости (&amp;lt;code&amp;gt;_smooth_drift_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: сглаживание Надарая–Уотсона.&#039;&#039;&#039; Это «скользящее среднее с приоритетом надёжным точкам»: значение кривой в каждый момент — среднее соседних измерений, но уверенные точки (большой вес &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt;) тянут сильнее, а шумные почти не влияют. Ширина «гауссова окна» &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma&amp;lt;/math&amp;gt; задаёт, насколько далёких соседей ещё учитывать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Внутри куска между резами строим гладкую кривую, которая &#039;&#039;&#039;следит за горками&#039;&#039;&#039; (плавный дрейф) и &#039;&#039;&#039;стоит на шуме&#039;&#039;&#039;. Это ядерное сглаживание Надарая–Уотсона с весом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w^{\text{qpow}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{qpow}=3&amp;lt;/math&amp;gt;, гауссово ядро &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma=3&amp;lt;/math&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{sm}(t)=\frac{\big(o\cdot w^{3}\big)\!*\!G_\sigma}{w^{3}\!*\!G_\sigma+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поверх — &#039;&#039;&#039;потолок скорости изменения сдвига&#039;&#039;&#039;: между соседними узлами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta\text{sm}|&amp;lt;/math&amp;gt; не больше &amp;lt;code&amp;gt;lim&amp;lt;/code&amp;gt;, что соответствует ускорению &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt; (1.25 %/с = &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{lim}=\frac{\text{max}\_\text{pct}\_\text{s}}{100}\cdot\text{STEP}\cdot\frac{1000}{\text{FRAME}}\approx0.15\ \text{кадра/узел}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Потолок не даёт кривой прыгнуть за единичными яркими выбросами в слепой зоне.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;113-опора-после-реза-фикс-2026-06-18&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.3. Опора после реза (фикс 2026-06-18) ===&lt;br /&gt;
&lt;br /&gt;
Тонкость: в куске, &#039;&#039;&#039;который начинается с реза&#039;&#039;&#039;, band теряет чувствительность на самом монтажном стыке (тусклые выбросы сразу за резом). Если вести потолок скорости от левого края куска, он занижает старт, и кривая ~35 с медленно вытягивается к плато. Идея пользователя: расходиться в &#039;&#039;&#039;обе стороны от самого уверенного якоря&#039;&#039;&#039; в первых &amp;lt;code&amp;gt;anchor_lookback_s=30&amp;lt;/code&amp;gt; с куска (= начало плато):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;   сдвиг&lt;br /&gt;
     ▲                  ● ● ● ● ● ● ●  ← плато (уверенные якоря)&lt;br /&gt;
     │      опора ↑ argmax w в первых 30с&lt;br /&gt;
     │     ╱   ╲        clamp влево к резу + вправо вдоль плато&lt;br /&gt;
     │  ✗ ╱     ╲&lt;br /&gt;
     │ слепой    &lt;br /&gt;
     │ старт      &lt;br /&gt;
     ┼──┊─────────────────────────────►  t&lt;br /&gt;
        рез&amp;lt;/pre&amp;gt;&lt;br /&gt;
Слепой старт подтягивается &#039;&#039;&#039;к&#039;&#039;&#039; плато, а не плато к старту. Первый кусок (без реза слева) — по-старому: левый край = опора. Приёмка: 47/55 дорожек не тронуты, изменения = только исправления (память &amp;lt;code&amp;gt;project_band_postcut_anchor_fix&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;12-варп-тишина-в-резах-заполнение-рефом&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 12. Варп, тишина в резах, заполнение рефом ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;121-кусочный-варп-_warp_piecewise&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.1. Кусочный варп (&amp;lt;code&amp;gt;_warp_piecewise&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль ресэмплится по кускам между резами: внутри куска — гладкая &amp;lt;code&amp;gt;wcurve&amp;lt;/code&amp;gt; (горки), на резе — резкий стык. Для выходного отсчёта &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;lt;/math&amp;gt; источник в дубле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{src}(t)=\Big(t+\text{wcurve}(t)\cdot\tfrac{\text{FRAME}}{1000}\Big)\cdot sr,\qquad&lt;br /&gt;
\text{out}[t]=\operatorname{interp}\big(\text{src}(t)\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;122-тишина-в-резах&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.2. Тишина в резах ===&lt;br /&gt;
&lt;br /&gt;
Непрерывный варп на монтажном стыке «переиграл» бы звук дважды, поэтому в резах ставится тишина (контент разносится):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;lt;0&amp;lt;/math&amp;gt; (нехватка дубля) → тишина &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta|&amp;lt;/math&amp;gt; по центру реза;&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;gt;0&amp;lt;/math&amp;gt; (лишнее вырезано) → узкий шов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.15&amp;lt;/math&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;123-заполнение-тишины-синхронным-рефом-_fill_silence_from_ref&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.3. Заполнение тишины синхронным рефом (&amp;lt;code&amp;gt;_fill_silence_from_ref&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Финальный проход — &#039;&#039;&#039;только после&#039;&#039;&#039; полного band/muq, когда дорожка уже синхронна рефу. Там, где озвучка молчит, а реф звучит, подставляем реф с кроссфейдом. Порог тишины измерен:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{RMS}_{\text{dBFS}}=20\log_{10}\!\Big(\tfrac{\text{RMS}}{32768}+\varepsilon\Big),\qquad&lt;br /&gt;
\text{fill}\iff \text{RMS}_{\text{дубль}}&amp;lt;-90\ \text{дБ}\ \wedge\ \text{RMS}_{\text{реф}}\ge-90\ \text{дБ}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порог &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-90&amp;lt;/math&amp;gt; дБ — в центре измеренного плато настоящих дыр (ниже &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-80&amp;lt;/math&amp;gt;), далеко от обрыва тихого контента (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-40\ldots-60&amp;lt;/math&amp;gt;), окно RMS 20 мс, мин. длина зоны 0.15 с, кроссфейд 30 мс. Где у обоих тишина — остаётся тишина; озвучка не теряется (трогаем только пустоты).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;Запрет (намертво).&#039;&#039;&#039; &amp;lt;code&amp;gt;fill_cuts&amp;lt;/code&amp;gt; — заливка вырезов рефом &#039;&#039;&#039;по видео, до&#039;&#039;&#039; анализа — запрещён навсегда: вставка в ещё несинхронную дорожку плодит мнимый рассинхрон. Разрешено только &amp;lt;code&amp;gt;fill_silence&amp;lt;/code&amp;gt; — заполнение &#039;&#039;&#039;после&#039;&#039;&#039; выравнивания, по синхронной дорожке (память &amp;lt;code&amp;gt;feedback_no_audio_replacement_fill_cuts&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;124-кросс-модальный-детектор-студийного-av-десинка-detect_av_desync&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.4. Кросс-модальный детектор студийного A/V-десинка (&amp;lt;code&amp;gt;detect_av_desync&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Read-only диагностика (на wav не влияет). После укладки &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; уже на сетке рефа; широким PHAT-окном (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, полоса 50–13500 Гц) меряем остаточный лаг M&amp;amp;amp;E к реф-аудио по окнам. Если он &#039;&#039;&#039;большой и устойчивый&#039;&#039;&#039; (медиана велика, MAD мал) — аудио источника смещено относительно его же видео (студийный дефект, надёжно не чинится) → дорожка помечается &#039;&#039;&#039;красным&#039;&#039;&#039;. Паттерн валидирован: AniLibria ep01 kamennyj-okean медиана −40к / MAD 2.2к против ~0/~0 у 10 здоровых.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    SIL[Дубль на сетке рефа + тишина в резах] --&amp;gt; OFF0[_coarse_off0&amp;lt;br/&amp;gt;band ±2.5с, медиана по полосам]&lt;br /&gt;
    OFF0 --&amp;gt; PRE[Пред-варп по off0]&lt;br /&gt;
    PRE --&amp;gt; BAND[&amp;quot;band ±0.7с → o_ост, w&amp;quot;]&lt;br /&gt;
    BAND --&amp;gt; SUM[&amp;quot;o = off0 + o_ост&amp;quot;]&lt;br /&gt;
    SUM --&amp;gt; DET[detect.detect&amp;lt;br/&amp;gt;warp_curve + tail_filter + edge_refine]&lt;br /&gt;
    DET --&amp;gt; DRIFT[_smooth_drift_curve&amp;lt;br/&amp;gt;Надарая–Уотсон + потолок 1.25%/с + опора-после-реза]&lt;br /&gt;
    DRIFT --&amp;gt; WP[_warp_piecewise + тишина в резах]&lt;br /&gt;
    WP --&amp;gt; FILL[_fill_silence_from_ref −90дБ]&lt;br /&gt;
    FILL --&amp;gt; AV[detect_av_desync&amp;lt;br/&amp;gt;красный флаг студийного десинка]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части II.&#039;&#039;&#039; Поверх видео-карты звук band/muq снимает остаточный сдвиг аудио, который видео не видит: многополосная метрика делает корреляцию зрячей сквозь голос и мастеринг, грубая off0 ловит крупный сдвиг, следящий band доводит остаток, общий аппарат &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt; ставит резы, кривая дрейфа с потолком скорости и опорой-после-реза кладёт гладко, тишина заполняется синхронным рефом. Дальше — как всё это читается на графиках.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-iii-графики--диагноз-с-одного-взгляда&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть III. Графики — диагноз с одного взгляда =&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/plots_unified.py&amp;lt;/code&amp;gt;. Проектная установка: алгоритмы доведены до состояния, где &#039;&#039;&#039;качество читается по графику, без ручной сверки в Premiere&#039;&#039;&#039;. Беглый взгляд на любой график даёт чёткий ответ «всё в порядке или нет». Поэтому график — не украшение, а главный инструмент приёмки и отладки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;13-зачем-графики-и-что-на-них-видно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 13. Зачем графики и что на них видно ==&lt;br /&gt;
&lt;br /&gt;
Единый график строится &#039;&#039;&#039;на каждую пару&#039;&#039;&#039; и показывает оба слоя сразу: &#039;&#039;&#039;верхняя панель — зрение&#039;&#039;&#039; (видео-укладка, всегда), &#039;&#039;&#039;нижняя — звук&#039;&#039;&#039; (доводка band/muq, если включена), на общей оси времени с синхронным зумом и ховером. Один взгляд отвечает на четыре вопроса:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Вопрос&lt;br /&gt;
! Где смотреть&lt;br /&gt;
|-&lt;br /&gt;
| Держится ли укладка за &#039;&#039;&#039;надёжные&#039;&#039;&#039; данные?&lt;br /&gt;
| цвет точек (уверенность): линия должна идти по ярким, не по тусклым&lt;br /&gt;
|-&lt;br /&gt;
| Есть ли необъяснённый &#039;&#039;&#039;перекос/увод&#039;&#039;&#039;?&lt;br /&gt;
| отклонение линии от облака точек&lt;br /&gt;
|-&lt;br /&gt;
| Верно ли найдены &#039;&#039;&#039;монтажные резы&#039;&#039;&#039;?&lt;br /&gt;
| вертикальные пунктиры и их подписи (мс)&lt;br /&gt;
|-&lt;br /&gt;
| Согласны ли &#039;&#039;&#039;зрение и звук&#039;&#039;&#039;?&lt;br /&gt;
| сравнение двух панелей в одной зоне&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;14-дрейф-форма-и-робастная-база-тейласена&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 14. Дрейф-форма и робастная база Тейла–Сена ==&lt;br /&gt;
&lt;br /&gt;
Сырой сдвиг по всей серии может быть сотни кадров (крупный масштаб оси), и на нём не видно ни плато, ни ступеней в десяток кадров. Поэтому обе панели рисуются в &#039;&#039;&#039;дрейф-форме&#039;&#039;&#039; — вычитается робастная линейная база, и всё разворачивается вокруг нуля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: база Тейла–Сена.&#039;&#039;&#039; Чтобы провести «среднюю линию» через облако точек и не дать единичным выбросам её перекосить, берут наклон между каждой парой точек и выбирают медианный — половина пар «за», половина «против». Это устойчивее обычной прямой и не требует никаких порогов отбраковки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
База оценивается &#039;&#039;&#039;методом Тейла–Сена&#039;&#039;&#039; (&amp;lt;code&amp;gt;_baseline&amp;lt;/code&amp;gt;) — он устойчив к выбросам, в отличие от МНК: на выборке точек (до 400) берутся все попарные наклоны, и базовый наклон — их медиана; сдвиг — медиана остатков:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\operatorname{median}_{i&amp;lt;j}\frac{y_j-y_i}{t_j-t_i},\qquad&lt;br /&gt;
b=\operatorname{median}_k\big(y_k-a\,t_k\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
База считается по &#039;&#039;&#039;телу&#039;&#039;&#039; дорожки (исключая голову/заставку: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;gt;\text{head}+5&amp;lt;/math&amp;gt; с). Затем:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{dev}_{\text{якорь}}=\text{shift}-(a\,t+b),\qquad&lt;br /&gt;
\text{dev}_{\text{кривая}}=\text{curve}-(a\,T+b),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а предел оси берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\max(12,\ p_{99}(|\text{dev}|)+4)&amp;lt;/math&amp;gt; кадров — авто-масштаб под реальный разброс. На резах в линию вставляется &amp;lt;code&amp;gt;NaN&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;_breaks&amp;lt;/code&amp;gt;), чтобы она &#039;&#039;&#039;не соединяла&#039;&#039;&#039; куски через разрыв — ступень видна как обрыв, а не как вертикаль.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 14.1 — «Сырой сдвиг vs дрейф-форма».&#039;&#039;&#039; Два графика друг над другом для одной дорожки: сверху сырой &amp;lt;code&amp;gt;shift(t)&amp;lt;/code&amp;gt; (линия уходит на −600 кадров, мелкие детали не видны); снизу та же дорожка в дрейф-форме (база вычтена, всё колеблется в ±15 кадров вокруг 0, видны и плато, и ступенька реза). Подпись: «Дрейф-форма вытаскивает структуру, которую крупный масштаб прячет».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;15-слои-цветокодирование-разбор-реальных-кейсов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 15. Слои, цветокодирование, разбор реальных кейсов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;151-слои-единого-графика&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.1. Слои единого графика ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхняя панель (ЗРЕНИЕ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки якорей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t_{\text{реф}},\ \text{dev}_{\text{якорь}})&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;cos&#039;&#039;&#039; (viridis, 0…1) — надёжность каждого видео-якоря;&lt;br /&gt;
* оранжевая линия — &#039;&#039;&#039;карта зрения&#039;&#039;&#039; в дрейф-форме, с разрывами на резах;&lt;br /&gt;
* crimson-пунктиры — &#039;&#039;&#039;резы зрения&#039;&#039;&#039; с подписью величины (мс);&lt;br /&gt;
* заголовок несёт абсолютную медиану сдвига.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Нижняя панель (ЗВУК):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(T,\ o)&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;w&#039;&#039;&#039; (cividis, 0…1.2) — уверенность band;&lt;br /&gt;
* серые точки — &#039;&#039;&#039;gcc-свидетель&#039;&#039;&#039; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с), справочно;&lt;br /&gt;
* зелёная линия — &#039;&#039;&#039;кривая band&#039;&#039;&#039; (дрейф), с разрывами на резах;&lt;br /&gt;
* purple-пунктиры — &#039;&#039;&#039;резы детекта&#039;&#039;&#039; аудио.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;gcc — НЕ судья.&#039;&#039;&#039; Серая gcc-линия на контенте проекта (закадр+музыка) почти слепа и скачет ±40к = шум; её нельзя брать за подтверждение укладки. Судьи сходимости — сам band (48 полос) / muq + уши/глаза на наложенных дорожках (память &amp;lt;code&amp;gt;feedback_gcc_ncc_blind_judge&amp;lt;/code&amp;gt;). gcc на графике — фон, не вердикт.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;152-как-цвет-читается-мгновенно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.2. Как цвет читается мгновенно ===&lt;br /&gt;
&lt;br /&gt;
Цвет точки = уверенность. Тёмные (фиолетовые/синие) — шумные, ярких (жёлтые/зелёные) — надёжные. &#039;&#039;&#039;Правильная линия идёт по ярким точкам.&#039;&#039;&#039; Если линия отклонилась, а рядом плотное облако ярких — это сигнал проблемы: либо увод укладки, либо линия последовала за тусклыми выбросами на краю. Это и есть «диагноз с одного взгляда».&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.1 — «Эталон единого графика (здоровая дорожка)».&#039;&#039;&#039; Реальный скрин двухпанельного графика: вверху зрение — плотное жёлтое облако якорей точно на 0, оранжевая линия плоская, резов нет; внизу звук — узкое облако вокруг 0, зелёная линия плоская. Подпись: «Так выглядит ОК: линии по ярким точкам, ноль отклонения, нет резов». &#039;&#039;(Скрин взять из &amp;lt;code&amp;gt;&amp;amp;lt;серия&amp;amp;gt;/_plots/&amp;amp;lt;stem&amp;amp;gt;__track.png&amp;lt;/code&amp;gt; любой здоровой пары.)&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.2 — «Перекос укладки (до фикса опоры-после-реза)».&#039;&#039;&#039; Скрин зоны 180–220 с AniDUB: после реза линия band провалилась вниз за парой тусклых (тёмных) якорей у края, хотя выше плотное плато ярких якорей на другом уровне. Рядом — тот же участок после фикса: линия держится за плато (опора = увереннейший якорь начала плато), слепой старт подтянут к плато. Подпись: «Линия не должна вестись за тусклыми единицами на краю».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.3 — «Ложный рез из-за слепоты зрения (ep05 Amazing, 950–979 с)».&#039;&#039;&#039; Скрин зоны с двумя близкими резами: до фикса между ними узкий клин и лишний рез (окно проверки ±80 с перехлестнуло через соседний рез и поймало чужой уровень); после фикса (окно ограничено соседними резами) лишний рез убран. Подпись: «Окно проверки уровня ограничивается соседними резами — иначе чужой уровень ложно подтверждает рез».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;153-носители-png-превью-и-интерактивный-html&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.3. Носители: PNG-превью и интерактивный HTML ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PNG&#039;&#039;&#039; (&amp;lt;code&amp;gt;matplotlib&amp;lt;/code&amp;gt;, Agg) — статичная миниатюра (1 или 2 панели) для быстрого взгляда в панели/отчёте.&lt;br /&gt;
* &#039;&#039;&#039;HTML&#039;&#039;&#039; (&amp;lt;code&amp;gt;plotly&amp;lt;/code&amp;gt;, self-contained, открывается офлайн) — для детального разбора. Точки и линия в WebGL (&amp;lt;code&amp;gt;scattergl&amp;lt;/code&amp;gt;), чтобы линия рисовалась поверх точек независимо от порядка; ховер &amp;lt;code&amp;gt;x unified&amp;lt;/code&amp;gt;, &#039;&#039;&#039;зум любой зоны вручную&#039;&#039;&#039;. Отдельные PNG каждого реза не создаём — HTML позволяет приблизить любой стык (решение 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
Read-only по контракту: падение рендера не роняет conform (ловит вызывающий).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Приложения =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-а-константы-и-их-обоснования&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение А. Константы и их обоснования ==&lt;br /&gt;
&lt;br /&gt;
Все ключевые числа калиброваны не «на глаз», а по &#039;&#039;&#039;плато нечувствительности&#039;&#039;&#039;: на свипе по диапазону результат (резы по датасету) не меняется — значит подстройки под контент нет.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;зрение&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Зрение ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;&lt;br /&gt;
| 128×72&lt;br /&gt;
| features&lt;br /&gt;
| размер серого кадра под SRM&lt;br /&gt;
|-&lt;br /&gt;
| clip &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| ±3&lt;br /&gt;
| features&lt;br /&gt;
| обрезка выбросов отклика фильтров&lt;br /&gt;
|-&lt;br /&gt;
| dim&lt;br /&gt;
| 18432&lt;br /&gt;
| features&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;2\cdot128\cdot72&amp;lt;/math&amp;gt;, два канала&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;K&amp;lt;/code&amp;gt; (coarse)&lt;br /&gt;
| 8&lt;br /&gt;
| coarse&lt;br /&gt;
| прорежение грубого прохода&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| coarse&lt;br /&gt;
| полуокно зануления при поиске 2-го пика&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VHI&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.60&lt;br /&gt;
| coarse&lt;br /&gt;
| порог «пик высокий» (надёжный якорь)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CMIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.12&lt;br /&gt;
| coarse&lt;br /&gt;
| порог различимости (пик одинок → не двойка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20&lt;br /&gt;
| dropdtw&lt;br /&gt;
| штраф выброса (базовый Drop-DTW)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;OPEN/EXT&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20 / 0.02&lt;br /&gt;
| dropdtw&lt;br /&gt;
| gap-open / gap-extend (острый шов)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DSYN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| плоская цена выброса кадра дубля (вставка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MATCH_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| guard: кадр с матчем лучше этого нельзя выбросить&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MARG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 120&lt;br /&gt;
| band_align&lt;br /&gt;
| полуширина следящей полосы (≈5 с)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CHUNK/OVERLAP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 4000 / 700&lt;br /&gt;
| band_align&lt;br /&gt;
| кусок Drop-DTW и перекрытие&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно агрегации якорей (плато 0.2–0.5)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 800&lt;br /&gt;
| vision_detect&lt;br /&gt;
| штраф DP-сегментации (плато 200–3000)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| vision_detect&lt;br /&gt;
| степень веса = (cos·agree)³ (плато 2–3)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.45&lt;br /&gt;
| vision_detect&lt;br /&gt;
| потолок наклона прямых, к/с (плато 0.3–0.6)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_MSIZE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30&lt;br /&gt;
| vision_detect&lt;br /&gt;
| мин. длина сегмента, с (плато 8–60)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно проверки устойчивости уровня (плато 45–120)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_GATE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.55&lt;br /&gt;
| vision_detect&lt;br /&gt;
| вес-гейт надёжных узлов (плато 0.3–0.8)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 200 мс&lt;br /&gt;
| vision_detect&lt;br /&gt;
| порог величины реза (плато 60–400 мс)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;звук&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Звук ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;sr&amp;lt;/code&amp;gt;&lt;br /&gt;
| 16000&lt;br /&gt;
| band&lt;br /&gt;
| частота анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NB&amp;lt;/code&amp;gt;&lt;br /&gt;
| 48&lt;br /&gt;
| band&lt;br /&gt;
| число лог-полос (50–14000 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;nfft/hop&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2048 / 256&lt;br /&gt;
| band&lt;br /&gt;
| STFT (темп огибающей 62.5 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;win&amp;lt;/code&amp;gt;&lt;br /&gt;
| 5 с&lt;br /&gt;
| band&lt;br /&gt;
| окно анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAXLAG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.7 с&lt;br /&gt;
| band&lt;br /&gt;
| рабочее окно лага (точность)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;COARSE_LAG_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2.5 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно грубой off0 (видит сдвиг опенинга)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tol&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2 кадра&lt;br /&gt;
| band&lt;br /&gt;
| допуск согласия полос (agree)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| 41.708 мс&lt;br /&gt;
| params&lt;br /&gt;
| мс/кадр (23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.5 с&lt;br /&gt;
| params&lt;br /&gt;
| шаг сетки T&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3 (120 мс)&lt;br /&gt;
| params&lt;br /&gt;
| порог величины реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 700&lt;br /&gt;
| params&lt;br /&gt;
| штраф DP-сегментации (детект)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| params&lt;br /&gt;
| степень веса якоря&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| params&lt;br /&gt;
| макс. наклон сегмента (≤~2%)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MSIZE_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 20 с&lt;br /&gt;
| params&lt;br /&gt;
| мин. длина сегмента&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 1.25 %/с&lt;br /&gt;
| apply&lt;br /&gt;
| потолок скорости кривой дрейфа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;anchor_lookback_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно поиска опоры-после-реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIL_FILL_DB&amp;lt;/code&amp;gt;&lt;br /&gt;
| −90 дБ&lt;br /&gt;
| align&lt;br /&gt;
| порог тишины озвучки (центр плато дыр)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;XFADE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 мс&lt;br /&gt;
| align&lt;br /&gt;
| кроссфейд заполнения рефом&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⏱ &#039;&#039;&#039;Правило прогонов&#039;&#039;&#039; (память &amp;lt;code&amp;gt;feedback_warn_long_runs&amp;lt;/code&amp;gt;): любой тест ≤10 мин — параллелить (клипы &amp;lt;code&amp;gt;--workers 8&amp;lt;/code&amp;gt;, конформы ×6 пар, синтетика &amp;lt;code&amp;gt;--workers 24&amp;lt;/code&amp;gt;) или резать объём; дольше 10 мин — только с явного согласия пользователя, оценку времени — заранее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-б-карта-файлов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение Б. Карта файлов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;src/track_muxer/conform/&lt;br /&gt;
├── features.py              SRM-отпечатки кадров (KB/D1, L2, dim 18432)&lt;br /&gt;
├── kernel/&lt;br /&gt;
│   ├── coarse.py            грубый проход: G=cs·crᵀ, якоря, LIS, оконный&lt;br /&gt;
│   ├── dropdtw.py           Drop-DTW (base/affine/guard/free_start)&lt;br /&gt;
│   └── band_align.py        следящая полоса + склейка кусков&lt;br /&gt;
├── vision_detect.py         УКЛАДКА зрения: (o,w) → детект → ломаная (ЕДИНСТВЕННАЯ карта)&lt;br /&gt;
├── align.py                 оркестровка пары: pred → разбор правок → ресэмпл → звук → график&lt;br /&gt;
└── anchor/                  АУДИО-доводка band/muq поверх зрения&lt;br /&gt;
    ├── apply.py             coarse_off0, следящий band, smooth_drift_curve, варп, fill&lt;br /&gt;
    ├── detect.py            ОБЩИЙ аппарат: wfit, warp_curve, tail_filter, edge_refine&lt;br /&gt;
    ├── assemble.py          freeze + сборка варпа&lt;br /&gt;
    ├── params.py            FRAME, T, STEP, PEN, QPOW, SMAX, MSIZE_S …&lt;br /&gt;
    ├── maps/band.py         48-полосная DSP-метрика (без модели) — дефолт&lt;br /&gt;
    ├── maps/muq.py          GPU-эмбеддинги (альтернатива)&lt;br /&gt;
    ├── plots_unified.py     ЕДИНЫЙ график зрение+звук (PNG + HTML)&lt;br /&gt;
    └── plots_html.py        отдельный HTML band (легаси-путь)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Тема&lt;br /&gt;
! Отчёт (суть)&lt;br /&gt;
|-&lt;br /&gt;
| Прорыв видео-синхрона&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_align/BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Методика зрячих свидетелей аудио&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Грубый→band-follow&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Аудио-аанкер (band/muq)&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_audio_auto.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Архитектура conform&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/conform_architecture_audit.md&amp;lt;/code&amp;gt; §14&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Версия документа:&#039;&#039;&#039; 2026-06-18, под архитектуру «анализатор зрения — единственная видео-карта; band/muq — доводка поверх». Числа и формулы сверены с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt;. При расхождении кода и документа — приоритет у кода, документ актуализировать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1010</id>
		<title>Track Muxer</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1010"/>
		<updated>2026-06-18T20:09:37Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Фикс формул: подчёркивание в \text{} (MATCH_THR/VC_THR/max_pct_s)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Назначение документа.&#039;&#039;&#039; Полное инженерное описание двух алгоритмов выравнивания, на которых стоит модуль &amp;lt;code&amp;gt;conform&amp;lt;/code&amp;gt;: &#039;&#039;&#039;анализатора зрения&#039;&#039;&#039; (строит карту таймлайна по видеоряду) и &#039;&#039;&#039;аудио-доводки band/muq&#039;&#039;&#039; (правит остаточный сдвиг звука поверх зрения). Документ самодостаточен: математика, геометрия, рекурренты, точные константы и их обоснования, схемы пайплайнов и разбор реальных диагностических графиков. Читатель — инженер, который при желании сможет воспроизвести метод.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Конвенция знаков (сквозная).&#039;&#039;&#039; Сдвиг измеряем в &#039;&#039;кадрах рефа&#039;&#039;; &#039;&#039;&#039;правее (дубль отстаёт) = +&#039;&#039;&#039;, левее (спешит) = −. Один кадр = &#039;&#039;&#039;41.708 мс&#039;&#039;&#039; (23.976 fps, &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; в &amp;lt;code&amp;gt;anchor/params.py&amp;lt;/code&amp;gt;). Сетка анализа звука &amp;lt;code&amp;gt;T = arange(2.5, 1400, 0.5)&amp;lt;/code&amp;gt; с, шаг &amp;lt;code&amp;gt;STEP = 0.5&amp;lt;/code&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Источники.&#039;&#039;&#039; Всё, что ниже, сверено с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt; и отчётами &amp;lt;code&amp;gt;doc/reports/**&amp;lt;/code&amp;gt;. Ключевые файлы перечислены в Приложении Б.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;0-постановка-задачи-и-общая-архитектура&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 0. Постановка задачи и общая архитектура ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;01-что-мы-выравниваем&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.1. Что мы выравниваем ===&lt;br /&gt;
&lt;br /&gt;
Дан &#039;&#039;&#039;эпизод сериала&#039;&#039;&#039; (или отдельный фильм) в виде набора файлов разных озвучек (студий). Все они содержат &#039;&#039;&#039;один и тот же видеоряд&#039;&#039;&#039; (с точностью до монтажных отличий: разные заставки, вырезы, вставки, иногда другой fps), но &#039;&#039;&#039;разные аудиодорожки&#039;&#039;&#039;. Нужно для каждой пары &amp;lt;code&amp;gt;(референс, дубль)&amp;lt;/code&amp;gt; так преобразовать аудио дубля во времени, чтобы оно &#039;&#039;&#039;синхронно&#039;&#039;&#039; легло на таймлайн референса — и потом смуксировать все дорожки в один MKV.&lt;br /&gt;
&lt;br /&gt;
Главная трудность — это &#039;&#039;&#039;не «найти один сдвиг»&#039;&#039;&#039;. Сдвиг переменный по всей длине:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Источник рассинхрона&lt;br /&gt;
! Как выглядит на карте &amp;lt;code&amp;gt;(t_реф → t_дубль)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Постоянный сдвиг (priming, encoder delay)&lt;br /&gt;
| вертикальный параллельный перенос&lt;br /&gt;
|-&lt;br /&gt;
| Дрейф скорости (разный fps, PAL/NTSC)&lt;br /&gt;
| наклон кривой ≠ 1&lt;br /&gt;
|-&lt;br /&gt;
| Монтажный &#039;&#039;&#039;вырез&#039;&#039;&#039; в дубле (короче рефа)&lt;br /&gt;
| ступень вверх (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Монтажная &#039;&#039;&#039;вставка&#039;&#039;&#039; в дубле (длиннее рефа)&lt;br /&gt;
| ступень вниз (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Заставка/реклама в начале&lt;br /&gt;
| свободный левый конец&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И всё это — на контенте, где прямое применение обоих слоёв теряет чувствительность: видеоряд может идти «на двойках» (соседние кадры идентичны — анимация на 2 кадра, статичные планы), музыка повторяется (корреляция фиксируется на такт), у дубля чужой голос поверх общей музыки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 0.1 — «Проблема в одной картинке».&#039;&#039;&#039; Слева: две киноленты (реф и дубль) друг под другом, на дубле — вырезанный кусок (короче) и вставленная заставка в начале (длиннее), стрелки соответствия кадров между лентами расходятся (не параллельны). Справа: те же соответствия как точки в осях &amp;lt;code&amp;gt;t_реф&amp;lt;/code&amp;gt;(X) / &amp;lt;code&amp;gt;t_дубль&amp;lt;/code&amp;gt;(Y) — складываются в ломаную линию с наклоном и двумя вертикальными ступенями. Подпись: «Выравнивание = восстановить эту ломаную».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;02-двухслойная-архитектура-зрение-строит-карту-звук-доводит&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.2. Двухслойная архитектура: зрение строит карту, звук доводит ===&lt;br /&gt;
&lt;br /&gt;
Фундаментальное решение проекта (отчёт &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;): &#039;&#039;&#039;видеоряд — общий инвариант&#039;&#039;&#039; всех озвучек, поэтому грубую и структурную карту строим по видео, а аудио используем лишь как &#039;&#039;&#039;тонкую доводку остатка&#039;&#039;&#039;, который видео физически не видит (аудио источника бывает смещено относительно своего же видео).&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    subgraph IN[Вход]&lt;br /&gt;
        REF[Референс mp4/mkv]&lt;br /&gt;
        DUB[Дубль mp4/mkv]&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph VIS[&amp;quot;СЛОЙ 1 · ЗРЕНИЕ (видео-карта)&amp;quot;]&lt;br /&gt;
        SRM[SRM-отпечатки кадров&amp;lt;br/&amp;gt;features.py]&lt;br /&gt;
        CO[Грубый проход · LIS&amp;lt;br/&amp;gt;kernel/coarse.py]&lt;br /&gt;
        DD[Drop-DTW в полосе&amp;lt;br/&amp;gt;kernel/dropdtw + band_align]&lt;br /&gt;
        LV[Разбор правок&amp;lt;br/&amp;gt;level_decide / creep_drop]&lt;br /&gt;
        PW[Укладка ломаной + детект резов&amp;lt;br/&amp;gt;vision_detect.py]&lt;br /&gt;
        SRM --&amp;gt; CO --&amp;gt; DD --&amp;gt; LV --&amp;gt; PW&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph RES[Ресэмпл аудио дубля]&lt;br /&gt;
        MAP[Карта t_реф → t_дубль]&lt;br /&gt;
        SIL1[Тишина в монтажных резах]&lt;br /&gt;
        MAP --&amp;gt; SIL1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph AUD[&amp;quot;СЛОЙ 2 · ЗВУК (доводка)&amp;quot;]&lt;br /&gt;
        OFF0[Грубая off0 · band ±2.5с]&lt;br /&gt;
        FOL[band ±0.7с следит за off0]&lt;br /&gt;
        DET[Детект ступеней · detect.py]&lt;br /&gt;
        DRIFT[Кривая дрейфа + потолок скорости]&lt;br /&gt;
        WARP[Варп по кускам + тишина]&lt;br /&gt;
        OFF0 --&amp;gt; FOL --&amp;gt; DET --&amp;gt; DRIFT --&amp;gt; WARP&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    FILL[Заполнение тишины синхронным рефом&amp;lt;br/&amp;gt;fill_silence]&lt;br /&gt;
    PLOTS[Единый график зрение+звук&amp;lt;br/&amp;gt;plots_unified.py]&lt;br /&gt;
    OUT[Выровненный WAV → mux]&lt;br /&gt;
&lt;br /&gt;
    REF --&amp;gt; SRM&lt;br /&gt;
    DUB --&amp;gt; SRM&lt;br /&gt;
    PW --&amp;gt; MAP&lt;br /&gt;
    SIL1 --&amp;gt; OFF0&lt;br /&gt;
    WARP --&amp;gt; FILL --&amp;gt; PLOTS --&amp;gt; OUT&lt;br /&gt;
}}&lt;br /&gt;
Почему именно так, а не «всё по аудио» или «всё по видео»:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Только аудио&#039;&#039;&#039; упирается в потолок: 5 разных аудио-методов (PHAT, MFCC, 2D-спектр) дают &#039;&#039;одни и те же промахи&#039;&#039; на повторяющемся контенте — это не алгоритмическая, а &#039;&#039;информационная&#039;&#039; нехватка в одном аудиоканале (&amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §2&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Только видео&#039;&#039;&#039; не видит сдвиг аудио относительно видео внутри самого источника (AV-sync delta, студийный сдвиг M&amp;amp;amp;E) — его видит лишь аудио.&lt;br /&gt;
* Поэтому: &#039;&#039;&#039;видео даёт структуру&#039;&#039;&#039; (где вырез, где вставка, какой дрейф), &#039;&#039;&#039;аудио доводит&#039;&#039;&#039; субкадровый остаток там, где видео слепо по физике (двойки) или где аудио смещено относительно картинки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;03-сквозные-величины-и-обозначения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.3. Сквозные величины и обозначения ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Обозначение&lt;br /&gt;
! Смысл&lt;br /&gt;
! Значение / где&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| мс на кадр&lt;br /&gt;
| 41.708 (≈23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| сетка анализа звука&lt;br /&gt;
| &amp;lt;code&amp;gt;arange(2.5, 1400, 0.5)&amp;lt;/code&amp;gt; с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| шаг сетки&lt;br /&gt;
| 0.5 с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;o[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| измеренный сдвиг в точке t&lt;br /&gt;
| кадры рефа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;w[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| уверенность измерения&lt;br /&gt;
| норм. по медиане&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pred[j]&amp;lt;/code&amp;gt;&lt;br /&gt;
| для кадра дубля j — кадр рефа (или −1)&lt;br /&gt;
| целое&lt;br /&gt;
|-&lt;br /&gt;
| cos&lt;br /&gt;
| мера схожести двух кадров&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\in[-1,1]&amp;lt;/math&amp;gt;, на практике &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0,1]&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Дальше — два алгоритма по слоям.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-i-зрение--построитель-видео-карты&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть I. Зрение — построитель видео-карты =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/band_align.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;, разбор правок в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;. Это &#039;&#039;&#039;единственный&#039;&#039;&#039; построитель видео-карты в conform (прежний «скат» &amp;lt;code&amp;gt;_build_map_linear&amp;lt;/code&amp;gt; удалён 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;1-геометрия-задачи&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;1-геометрия-задачи-карта-как-монотонная-кусочно-линейная-кривая&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 1. Геометрия задачи: карта как монотонная кусочно-линейная кривая ==&lt;br /&gt;
&lt;br /&gt;
Всё зрение сводится к одной геометрической цели — построить функцию&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tau:\; t_{\text{реф}} \;\longmapsto\; t_{\text{дубль}},&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
которая каждому моменту таймлайна референса сопоставляет момент в дубле, откуда брать звук. У этой функции жёсткая физическая структура:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;кусочно-линейная&#039;&#039;&#039; — внутри монтажного блока скорость постоянна (дрейф = наклон);&lt;br /&gt;
* &#039;&#039;&#039;монотонно неубывающая&#039;&#039;&#039; — время не идёт вспять (контент не переставляется);&lt;br /&gt;
* &#039;&#039;&#039;с вертикальными разрывами на монтажных стыках&#039;&#039;&#039; — вырез/вставка = ступень.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; t_дубль&lt;br /&gt;
   ▲&lt;br /&gt;
   │                                        ╱ (наклон ≈ fps_реф/fps_дубль)&lt;br /&gt;
   │                                   ╱╱╱&lt;br /&gt;
   │            вставка ↓         ╱╱╱&lt;br /&gt;
   │                        ┌────╱           ← разрыв вниз: в дубле лишний кусок&lt;br /&gt;
   │                        ┊&lt;br /&gt;
   │                   ╱╱╱╱╱┘&lt;br /&gt;
   │              ╱╱╱╱&lt;br /&gt;
   │   вырез ↑   ╱&lt;br /&gt;
   │         ┌──┘                            ← разрыв вверх: в дубле кусок вырезан&lt;br /&gt;
   │      ╱╱╱┊&lt;br /&gt;
   │  ╱╱╱╱   ┊&lt;br /&gt;
   └──┴──────┴──────────────────────────────►  t_реф&amp;lt;/pre&amp;gt;&lt;br /&gt;
Задача зрения — &#039;&#039;&#039;восстановить эту ломаную из зашумлённых измерений соответствия кадров&#039;&#039;&#039;. Шум двух видов: (а) ложные соответствия (двойки, лого, тёмные сцены), (б) пропуски (вырезы/вставки рвут непрерывность). Поэтому метод строится в три эшелона надёжности: грубая монотонная цепочка (§3) → точное выравнивание с пропусками (§4) → робастная ломаная поверх (§6).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;2-srm-отпечаток-кадра&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;2-srm-отпечаток-кадра&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 2. SRM-отпечаток кадра ==&lt;br /&gt;
&lt;br /&gt;
Чтобы сравнивать кадры рефа и дубля, каждый кадр сворачивается в вектор-отпечаток так, чтобы &#039;&#039;&#039;схожесть контента&#039;&#039;&#039; была устойчива к различиям источника (BD vs Web, разный кодек, яркость, логотип студии). Наивный путь — сравнивать пиксели яркости — ломается на первом же логотипе и перепаде гаммы. Решение — &#039;&#039;&#039;SRM (Spatial Rich Model): отпечаток не яркости, а остаточного высокочастотного сигнала&#039;&#039;&#039; (текстуры/краёв).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: что такое «отпечаток кадра» и SRM.&#039;&#039;&#039; Отпечаток (дескриптор) — это короткий числовой слепок картинки: вектор чисел, по которому два кадра можно сравнить, не сличая их попиксельно. Хороший отпечаток одинаков у одного и того же кадра в разном качестве и не сбивается от логотипа или яркости. &#039;&#039;&#039;SRM (Spatial Rich Model)&#039;&#039;&#039; — приём из цифровой криминалистики изображений: вместо самой картинки берут её &#039;&#039;высокочастотный остаток&#039;&#039; — то, что остаётся после вычитания плавных перепадов яркости (контуры, мелкая текстура). Именно остаток устойчив к перекодированию и смене источника.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;21-конвейер-построения-featurespybuild_srm&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.1. Конвейер построения (&amp;lt;code&amp;gt;features.py::build_srm&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    A[Кадр ffmpeg&amp;lt;br/&amp;gt;-vsync 0] --&amp;gt; B[Серый 128×72]&lt;br /&gt;
    B --&amp;gt; C1[Свёртка KB&amp;lt;br/&amp;gt;лапласиан]&lt;br /&gt;
    B --&amp;gt; C2[Свёртка D1&amp;lt;br/&amp;gt;градиент x]&lt;br /&gt;
    C1 --&amp;gt; D1[clip ±3]&lt;br /&gt;
    C2 --&amp;gt; D2[clip ±3]&lt;br /&gt;
    D1 --&amp;gt; E1[L2-норма канала]&lt;br /&gt;
    D2 --&amp;gt; E2[L2-норма канала]&lt;br /&gt;
    E1 --&amp;gt; F[concat × 1/√2&amp;lt;br/&amp;gt;float16, dim=18432]&lt;br /&gt;
    E2 --&amp;gt; F&lt;br /&gt;
}}&lt;br /&gt;
Пошагово. Кадр приводится к серому &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;g&amp;lt;/math&amp;gt; размера &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;128\times72&amp;lt;/math&amp;gt; (&amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: свёртка ядром.&#039;&#039;&#039; Свёртка — это проход маленькой матрицей-окном по всем пикселям: в каждой точке берётся взвешенная сумма соседних пикселей. Подбором весов окно настраивают на нужный признак — перепад яркости, край, текстуру. Сумма весов, равная нулю, означает, что окно «не замечает» ровной заливки и реагирует только на изменения.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Здесь два фиксированных ядра свёртки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
K_B=\begin{pmatrix}-1&amp;amp;2&amp;amp;-1\\ 2&amp;amp;-4&amp;amp;2\\ -1&amp;amp;2&amp;amp;-1\end{pmatrix},&lt;br /&gt;
\qquad&lt;br /&gt;
D_1=\begin{pmatrix}0&amp;amp;0&amp;amp;0\\ 0&amp;amp;-1&amp;amp;1\\ 0&amp;amp;0&amp;amp;0\end{pmatrix}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; — дискретный оператор второго порядка (лапласиан-подобный, сумма коэффициентов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0&amp;lt;/math&amp;gt;). Он &#039;&#039;&#039;зануляет постоянную и линейную составляющую&#039;&#039;&#039; яркости и реагирует на &#039;&#039;кривизну&#039;&#039; — текстуру, контуры, мелкие детали. Именно эта высокочастотная «подпись» одинакова у одного и того же кадра в разных рипах.&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; — первая горизонтальная разность (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\partial/\partial x&amp;lt;/math&amp;gt;), ловит вертикальные края и направление градиента.&lt;br /&gt;
&lt;br /&gt;
Свёртка идёт с краевым режимом &amp;lt;code&amp;gt;reflect&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_K = (g * K_B)&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_D = (g * D_1)&amp;lt;/math&amp;gt;. Далее — обрезка выбросов и нормировка каждого канала по отдельности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tilde r_K=\frac{\operatorname{clip}(r_K,-3,3)}{\lVert\operatorname{clip}(r_K,-3,3)\rVert_2+\varepsilon},&lt;br /&gt;
\qquad&lt;br /&gt;
\tilde r_D=\frac{\operatorname{clip}(r_D,-3,3)}{\lVert\operatorname{clip}(r_D,-3,3)\rVert_2+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обрезка &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm3&amp;lt;/math&amp;gt; давит редкие яркие выбросы (блики, субтитры), L2-нормировка убирает зависимость от общего контраста кадра. Итоговый вектор кадра — конкатенация обоих каналов, масштабированная на &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v=\tfrac{1}{\sqrt2}\,[\,\tilde r_K \;\Vert\; \tilde r_D\,]\in\mathbb{R}^{18432},\qquad&lt;br /&gt;
18432 = 2\cdot128\cdot72 .&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;22-почему-cos--v_1cdot-v_2-напрямую&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.2. Почему &amp;lt;code&amp;gt;cos = v_1\cdot v_2&amp;lt;/code&amp;gt; напрямую ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: косинусная близость.&#039;&#039;&#039; Если каждый кадр представить стрелкой (вектором) в многомерном пространстве, то мерой их похожести служит угол между стрелками: сонаправленные (один контент) дают косинус ≈ 1, перпендикулярные (разный контент) ≈ 0. Косинус зависит только от направления, не от длины стрелок, — поэтому он устойчив к общей яркости и контрасту.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Множитель &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt; выбран так, что &#039;&#039;&#039;готовый вектор уже нормирован&#039;&#039;&#039;: поскольку каждый канал имеет единичную норму,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\lVert v\rVert_2^2=\tfrac12\big(\lVert\tilde r_K\rVert_2^2+\lVert\tilde r_D\rVert_2^2\big)=\tfrac12(1+1)=1.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поэтому скалярное произведение двух отпечатков &#039;&#039;&#039;сразу равно косинусной близости&#039;&#039;&#039;, и она же — среднее канальных косинусов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v_1\cdot v_2=\tfrac12\big(\cos_{K_B}+\cos_{D_1}\big)=\cos(v_1,v_2)\in[-1,1].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это ключ к скорости всего зрения: матрица всех попарных схожестей — одно матричное умножение &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G = C_s C_r^{\top}&amp;lt;/math&amp;gt; (косинусы без отдельной нормировки), а стоимость для Drop-DTW — просто &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C = 1 - \cos&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Инженерные детали, ломающие бит-в-бит совпадение, если их нарушить&#039;&#039;&#039; (&amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;): ffmpeg &amp;lt;code&amp;gt;-vsync 0&amp;lt;/code&amp;gt; (passthrough — индекс кадра = позиция во времени), &amp;lt;code&amp;gt;float16&amp;lt;/code&amp;gt;, порядок &amp;lt;code&amp;gt;clip → norm → concat&amp;lt;/code&amp;gt;. Декод потоковый, при &amp;lt;code&amp;gt;low_mem&amp;lt;/code&amp;gt; фичи пишутся в memmap, значения идентичны in-RAM пути.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 2.1 — «Что видит SRM».&#039;&#039;&#039; Три колонки: (1) исходный кадр видео с логотипом студии в углу; (2) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; (контуры/текстура, лого почти не выделяется — оно гладкое); (3) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; (вертикальные края). Подпись: «Отпечаток строится по высокочастотному остатку → устойчив к яркости и логотипу».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;3-грубый-проход&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;3-грубый-проход-матрица-косинусов-надёжные-якоря-lis&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 3. Грубый проход: матрица косинусов, надёжные якоря, LIS ==&lt;br /&gt;
&lt;br /&gt;
Цель первого эшелона — получить &#039;&#039;&#039;грубую, но монотонную&#039;&#039;&#039; оценку сдвига &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;j&amp;lt;/math&amp;gt;, чтобы потом узкая полоса Drop-DTW искала точное соответствие не во всём рефе, а в коридоре вокруг этой линии. Файл &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;31-надёжные-якоря-_anchors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.1. Надёжные якоря (&amp;lt;code&amp;gt;_anchors&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кадры прореживаются с шагом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K=8&amp;lt;/math&amp;gt; (грубому проходу субкадровая точность не нужна). Строится матрица косинусов всех прореженных synth × ref:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
G = C_s\,C_r^{\top},\qquad G_{m,r}=\cos\big(v^{\text{syn}}_m, v^{\text{ref}}_r\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;m&amp;lt;/math&amp;gt; берётся лучший партнёр и его близость &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{best}_m=\arg\max_r G_{m,r}&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{bestv}_m=\max_r G_{m,r}&amp;lt;/math&amp;gt;. Затем окрестность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm W&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;W=80&amp;lt;/math&amp;gt; прореженных кадров) этого пика &#039;&#039;&#039;зануляется&#039;&#039;&#039;, и берётся второй максимум &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{second}_m&amp;lt;/math&amp;gt;. Кадр становится &#039;&#039;&#039;надёжным якорем&#039;&#039;&#039; только если выполнены оба условия:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\underbrace{\text{bestv}_m &amp;gt; V_{HI}}_{\text{пик высокий}}\quad\wedge\quad&lt;br /&gt;
\underbrace{\text{bestv}_m-\text{second}_m &amp;gt; C_{MIN}}_{\text{пик одинок (различим)}},\qquad&lt;br /&gt;
V_{HI}=0.60,\; C_{MIN}=0.12.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первое условие отсекает мусорные сцены (тьма, переход), второе — &#039;&#039;&#039;двойки и повторы&#039;&#039;&#039;: если в рефе есть второй почти такой же кадр, отрыв мал и кадр НЕ берётся в якоря. Это прямой ответ на главную проблему повторяющегося контента.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;32-монотонная-цепочка-через-lis-lis_nd-coarse_robust&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.2. Монотонная цепочка через LIS (&amp;lt;code&amp;gt;lis_nd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;coarse_robust&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: зачем «длиннейшая неубывающая подпоследовательность» (LIS).&#039;&#039;&#039; Среди якорей есть верные и единичные ложные. Верные обязаны идти «по возрастанию»: чем позже кадр в дубле, тем позже его партнёр в рефе (время не отматывается назад). LIS — классический алгоритм, который из разбросанных точек вытаскивает самую длинную цепочку, идущую только вверх; всё, что в неё не уложилось, почти наверняка ложные пары. Так костяк соответствия очищается без порогов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Надёжные якоря ещё могут содержать одиночные ложные пары. Истинное соответствие &#039;&#039;&#039;монотонно&#039;&#039;&#039; по рефу, поэтому из набора якорей &amp;lt;code&amp;gt;(synth_кадр → ref_кадр)&amp;lt;/code&amp;gt; извлекается &#039;&#039;&#039;длиннейшая неубывающая по ref подпоследовательность&#039;&#039;&#039; (Longest Non-Decreasing Subsequence, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(n\log n)&amp;lt;/math&amp;gt; через хвостовые индексы + бинарный поиск). Всё, что не легло в монотонную цепочку, отбрасывается как шум. По выжившим якорям линейной интерполяцией строится &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для всех кадров:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}(j)=\operatorname{interp}\big(j;\ \{a_k\},\ \{r_k-a_k\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; ref                     LIS оставляет монотонный костяк, шум выпадает&lt;br /&gt;
  ▲        ✗(ложный)&lt;br /&gt;
  │   ●───●        ✗&lt;br /&gt;
  │  ╱       ●───●───●&lt;br /&gt;
  │ ●                   ●───●     ← возрастающая цепочка = реальное соответствие&lt;br /&gt;
  │╱            ✗&lt;br /&gt;
  └───────────────────────────►  synth (кадр дубля)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;33-оконный-вариант-для-длинных-файлов-coarse_windowed&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.3. Оконный вариант для длинных файлов (&amp;lt;code&amp;gt;coarse_windowed&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Полная матрица &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G&amp;lt;/math&amp;gt; имеет размер &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; — для серии это гигабайты. Оконный проход режет дубль на окна по &amp;lt;code&amp;gt;CWIN=8000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;COV=2000&amp;lt;/code&amp;gt;; &#039;&#039;&#039;seed-offset перетекает из окна в окно&#039;&#039;&#039;, и в каждом окне ищется ref только в полосе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;CSR=4000&amp;lt;/code&amp;gt; вокруг seed. Память и время ограничены окном (не растут с длиной файла), а результат &#039;&#039;&#039;бит-в-бит совпал&#039;&#039;&#039; с полным проходом на 26 кейсах.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;4-drop-dtw-выравнивание-с-пропусками&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;4-drop-dtw-выравнивание-с-пропусками&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 4. Drop-DTW: выравнивание с пропусками ==&lt;br /&gt;
&lt;br /&gt;
Второй эшелон — точное соответствие кадров. Обычный DTW (Dynamic Time Warping) &#039;&#039;&#039;обязан&#039;&#039;&#039; сопоставить каждый кадр — он не умеет сказать «этого куска в рефе нет, это вставка». А у нас именно вырезы и вставки. Решение — &#039;&#039;&#039;Drop-DTW&#039;&#039;&#039;: DTW, которому разрешено &#039;&#039;выбрасывать&#039;&#039; кадры с обеих сторон за фиксированный штраф. Это ровно как &amp;lt;code&amp;gt;diff&amp;lt;/code&amp;gt; для текста или выравнивание последовательностей ДНК: ищем общий костяк, непарные куски помечаем как «вставка»/«удаление». Файл &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;41-стоимость-и-базовая-рекуррента-drop_dtw&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.1. Стоимость и базовая рекуррента (&amp;lt;code&amp;gt;drop_dtw&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Стоимость сопоставить кадр рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt; с кадром дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
C_{i,k}=1-\cos\!\big(v^{\text{ref}}_i,v^{\text{syn}}_k\big)\in[0,2].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами.&#039;&#039;&#039; Задача — перебрать все способы «растянуть / сжать / пропустить» кадры так, чтобы суммарная непохожесть сопоставленных кадров была минимальной, и сделать это не наивным перебором (астрономически долго), а накоплением лучшего ответа клетка за клеткой — это и есть динамическое программирование. Ниже — правило, по которому заполняется таблица лучших частичных ответов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Динамика заполняет таблицу &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_{i,k}&amp;lt;/math&amp;gt; — минимальную накопленную стоимость пути, пришедшего в клетку &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(i,k)&amp;lt;/math&amp;gt;. На каждом шаге доступны &#039;&#039;&#039;пять&#039;&#039;&#039; ходов (диагональ, два варпа, два выброса); старт — отдельная инициализация (свободный левый конец):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_{i,k}=\min\begin{cases}&lt;br /&gt;
D_{i-1,k-1}+C_{i,k} &amp;amp; \text{диагональ (1:1 совпадение)}\\&lt;br /&gt;
D_{i-1,k}+C_{i,k} &amp;amp; \text{вертикальный варп (ref тянется)}\\&lt;br /&gt;
D_{i,k-1}+C_{i,k} &amp;amp; \text{горизонтальный варп (дубль тянется)}\\&lt;br /&gt;
D_{i,k-1}+\text{DROP} &amp;amp; \text{выброс кадра дубля (вставка)}\\&lt;br /&gt;
D_{i-1,k}+\text{DROP} &amp;amp; \text{выброс кадра рефа (вырез)}\\&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с штрафом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{DROP}=0.20&amp;lt;/math&amp;gt;. Backpointer &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;B_{i,k}&amp;lt;/math&amp;gt; хранит, какой ход выбран, для восстановления пути. &#039;&#039;&#039;Свободные концы&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;свободный старт&#039;&#039;&#039; — &amp;lt;code&amp;gt;D[i,0] = C[i,0]&amp;lt;/code&amp;gt; для всех &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt;: путь может начаться с любой строки рефа (дубль не обязан стартовать с самого начала рефа);&lt;br /&gt;
* &#039;&#039;&#039;свободный конец&#039;&#039;&#039; — обратный ход начинается с &amp;lt;code&amp;gt;argmin&amp;lt;/code&amp;gt; последнего столбца: путь может закончиться на любой строке рефа.&lt;br /&gt;
&lt;br /&gt;
Восстановление пути (&amp;lt;code&amp;gt;backtrack&amp;lt;/code&amp;gt;) даёт три выхода: &amp;lt;code&amp;gt;pred[k]&amp;lt;/code&amp;gt; (для кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — его кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;drop_syn&amp;lt;/code&amp;gt; (маска выброшенных кадров дубля = вставки), &amp;lt;code&amp;gt;drop_ref&amp;lt;/code&amp;gt; (список выброшенных кадров рефа = вырезы).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 4.1 — «Путь по матрице Drop-DTW».&#039;&#039;&#039; Тепловая карта стоимости &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C_{i,k}&amp;lt;/math&amp;gt; (тёмное = похоже), ось X = кадры дубля, ось Y = кадры рефа. Поверх — оптимальный путь: диагональные участки (совпадение), один горизонтальный «провал» (выброс кадров дубля = вставка) и один вертикальный «прыжок» (выброс кадров рефа = вырез). Сбоку — легенда шести ходов. Подпись: «Зелёная линия — карта; разрывы = монтажные стыки».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;42-аффинный-штраф-острый-шов-вместо-размазни-drop_dtw_affine&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.2. Аффинный штраф: острый шов вместо размазни (&amp;lt;code&amp;gt;drop_dtw_affine&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Если вырез длинный, выбрасывать его «по одному кадру» дорого и нестабильно — путь норовит размазать выброс или убежать на логотипе. Аффинная (two-state) динамика берёт &#039;&#039;&#039;открытие&#039;&#039;&#039; прогона выброса дороже, чем его &#039;&#039;&#039;продолжение&#039;&#039;&#039; (как gap-open / gap-extend в биоинформатике):&lt;br /&gt;
&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; — «матч/варп» (платит &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C&amp;lt;/math&amp;gt;);&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; — «внутри выброса рефа»: открыть из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;OPEN=0.20&amp;lt;/code&amp;gt;, продлить из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;EXT=0.02&amp;lt;/code&amp;gt;;&lt;br /&gt;
* выброс кадра дубля (вставка) — плоская цена &amp;lt;code&amp;gt;DSYN=0.30&amp;lt;/code&amp;gt; внутри &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_r[i,k]=\min\big(M[i-1,k]+\text{OPEN},\; D_r[i-1,k]+\text{EXT}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дёшево продлевать (0.02) → длинный вырез схлопывается в один чистый шов, а не в рваную лесенку. На ровных логотипных участках состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; не открывается → путь не уходит в сторону.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;43-guard-нельзя-выбросить-кадр-с-хорошим-матчем-drop_dtw_affine_guard&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.3. Guard: нельзя выбросить кадр с хорошим матчем (&amp;lt;code&amp;gt;drop_dtw_affine_guard&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Без защиты прогон выброса склонен захватывать хвосты совпадающих кадров (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos=1&amp;lt;/math&amp;gt;), раздувая вставку. Guard заранее считает для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; лучший достижимый матч по столбцу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{colmin}_k=\min_i C_{i,k}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выброс этого кадра (ход drop-syn) разрешён &#039;&#039;&#039;только если&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{colmin}_k&amp;gt;\text{MATCH}\_\text{THR}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0.30&amp;lt;/math&amp;gt;); иначе цена выброса &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=\infty&amp;lt;/math&amp;gt; — кадр с хорошим матчем защищён от выбрасывания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;44-free-start-заставка-начала-выпадает-сама&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.4. Free-start: заставка начала выпадает сама ===&lt;br /&gt;
&lt;br /&gt;
Реклама/заставка в начале дубля не имеет пары в рефе. Free-start даёт &#039;&#039;&#039;свободный левый конец по дублю&#039;&#039;&#039;: путь может стартовать с любой клетки, выбросив ведущие кадры &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0..k-1]&amp;lt;/math&amp;gt; по цене вставки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k\cdot\text{DSYN}&amp;lt;/math&amp;gt;. Штраф растёт с &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — поэтому короткая заставка выпадает, но путь не «улетает» далеко.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{старт в } (i,k):\quad \text{cost}=C_{i,k}+k\cdot\text{DSYN}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;45-следящая-полоса-band_align&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.5. Следящая полоса (&amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Drop-DTW на полной матрице &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; невозможен по памяти. Поэтому он считается в &#039;&#039;&#039;узкой полосе&#039;&#039;&#039; вокруг грубой линии &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; (§3): дубль режется на куски по &amp;lt;code&amp;gt;CHUNK=4000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;OVERLAP=700&amp;lt;/code&amp;gt;; для каждого куска ref-окно берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[\min(\text{refk})-\text{MARG},\ \max(\text{refk})+\text{MARG}]&amp;lt;/math&amp;gt;, полуширина &amp;lt;code&amp;gt;MARG=120&amp;lt;/code&amp;gt; кадров (≈5 с — покрывает обычную правку). На стыках кусков для каждого кадра берётся ответ из того куска, где кадр &#039;&#039;&#039;дальше от края&#039;&#039;&#039; (надёжнее):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; кусок A:  ├──────────────────┤&lt;br /&gt;
 кусок B:              ├──────────────────┤&lt;br /&gt;
                       └ overlap ┘&lt;br /&gt;
 кадр в overlap → берём из того куска, где он ГЛУБЖЕ (центр надёжнее краёв)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    OFF[Грубая off из coarse] --&amp;gt; WIN[Окно ref ±MARG=120&amp;lt;br/&amp;gt;вокруг линии]&lt;br /&gt;
    CHUNK[Кусок дубля 4000&amp;lt;br/&amp;gt;overlap 700] --&amp;gt; WIN&lt;br /&gt;
    WIN --&amp;gt; C[&amp;quot;C = 1 − cos&amp;quot;]&lt;br /&gt;
    C --&amp;gt; DD[drop_dtw_affine_guard&amp;lt;br/&amp;gt;OPEN .20 / EXT .02 / DSYN .30&amp;lt;br/&amp;gt;MATCH_THR .30 / free_start]&lt;br /&gt;
    DD --&amp;gt; BT[backtrack → pred куска]&lt;br /&gt;
    BT --&amp;gt; MERGE[Склейка по «глубине» кадра]&lt;br /&gt;
}}&lt;br /&gt;
Итог слоя §4 — массив &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; длины &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s&amp;lt;/math&amp;gt;: для каждого кадра дубля либо кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; (выброшен).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;5-разбор-правок&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;5-разбор-правок-вырезы-вставки-налипания&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 5. Разбор правок: вырезы, вставки, налипания ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; сырой: в нём вперемешку настоящие монтажные правки и артефакты (слепые зоны зрения, налипания). Их разбирает &#039;&#039;&#039;единое физическое правило&#039;&#039;&#039; в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;, а не набор порогов под контент.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;51-правка-реальна-только-при-стойком-сдвиге-уровня-offset-_level_decide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.1. Правка реальна только при стойком сдвиге УРОВНЯ offset (&amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Ключевая идея: считаем поканальный offset на сопоставленных кадрах &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{off}_{\text{asg}}=\text{pred}[\text{asg}]-\text{asg}&amp;lt;/math&amp;gt;. Для каждого выброшенного блока (drop-syn) сравниваем &#039;&#039;&#039;медиану уровня offset до и после&#039;&#039;&#039; блока (окно &amp;lt;code&amp;gt;LEVEL_WIN_S=4&amp;lt;/code&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\Delta=\big|\operatorname{median}(\text{off}_{\text{после}})-\operatorname{median}(\text{off}_{\text{до}})\big|.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta\le&amp;lt;/math&amp;gt; &amp;lt;code&amp;gt;LEVEL_EDIT_S&amp;lt;/code&amp;gt; (1 с) &#039;&#039;&#039;или&#039;&#039;&#039; блок короче &amp;lt;code&amp;gt;LEVEL_MIN_INS_S&amp;lt;/code&amp;gt; (2 с) → &#039;&#039;&#039;слепая зона / дрожь&#039;&#039;&#039; → восстановить интерполяцией (заодно перекрывает парный пробел рефа). Ложное восстановление безвредно по построению.&lt;br /&gt;
* иначе → &#039;&#039;&#039;реальная вставка&#039;&#039;&#039; → оставить выброшенной.&lt;br /&gt;
&lt;br /&gt;
Любой оставшийся пробел рефа при непрерывном дубле &amp;amp;gt; &amp;lt;code&amp;gt;CUT_MIN_S&amp;lt;/code&amp;gt; (0.3 с) — это &#039;&#039;&#039;реальный вырез&#039;&#039;&#039;. Доказано: пробел рефа при непрерывном дубле всегда сдвигает уровень ⇒ слепых вырезов не остаётся.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;52-фикс-пилы-вырез-только-при-устойчивом-сдвиге-а-не-при-выбросе-на-статике&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.2. Фикс «пилы»: вырез только при устойчивом сдвиге, а не при выбросе на статике ===&lt;br /&gt;
&lt;br /&gt;
На статике (длинный неподвижный план) зрение может дать &#039;&#039;временный&#039;&#039; выброс offset, который &#039;&#039;&#039;возвращается&#039;&#039;&#039; — это не вырез, а артефакт. Соседние пробелы склеиваются в кластер (&amp;lt;code&amp;gt;LEVEL_CUT_COALESCE_S=3&amp;lt;/code&amp;gt; с); если в пределах &amp;lt;code&amp;gt;LEVEL_CUT_RECOVER_S&amp;lt;/code&amp;gt; (8 с) уровень &#039;&#039;&#039;возвращается&#039;&#039;&#039; к доврезному — кластер восстанавливается (пила убрана), если держится — схлопывается в &#039;&#039;&#039;один&#039;&#039;&#039; интервал выреза (а не лесенку коротких тишин).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;53-детектор-налипания-_creep_drop-кейс-case04&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.3. Детектор «налипания» (&amp;lt;code&amp;gt;_creep_drop&amp;lt;/code&amp;gt;, кейс case/04) ===&lt;br /&gt;
&lt;br /&gt;
Особый дефект: вставка-повтор своего же контента с наложенным текстом присваивается Drop-DTW «ползучим ходом» вместо выброса. Сигнал кричащий: присвоенные кадры по содержимому &#039;&#039;&#039;чужие&#039;&#039;&#039; своим реф-партнёрам — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\approx0.00\text{–}0.02&amp;lt;/math&amp;gt; на много секунд подряд, тогда как честные матчи даже в тёмных сценах держат &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\ge0.22&amp;lt;/math&amp;gt;. Механика: скользящая медиана &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt; присвоенных кадров (окно ~1 с); связные зоны ниже &amp;lt;code&amp;gt;CREEP_COS=0.15&amp;lt;/code&amp;gt; длиннее &amp;lt;code&amp;gt;CREEP_MIN_S=3&amp;lt;/code&amp;gt; с → &amp;lt;code&amp;gt;pred=-1&amp;lt;/code&amp;gt;. Дальнейшую судьбу решает &amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt; (§5.1) — ложное срабатывание безвредно.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;54-возврат-краёв-_recover_edges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.4. Возврат краёв (&amp;lt;code&amp;gt;_recover_edges&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Free-start мог выкинуть &#039;&#039;&#039;совпадающее&#039;&#039;&#039; начало/конец (общий опенинг при разном вступлении BD↔WEB). Берём выброшенный кусок дубля против непокрытого куска рефа, гоняем &amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt; на подзадаче и вписываем вернувшиеся матчи (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;gt;&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;EDGE_COS_MIN=0.5&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;6-укладка-ломаной&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;6-укладка-ломаной-ow--детект-ступеней--кусочно-линейная-карта&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 6. Укладка ломаной: (o,w) → детект ступеней → кусочно-линейная карта ==&lt;br /&gt;
&lt;br /&gt;
Финал зрения (&amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;) — превратить дискретный &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в &#039;&#039;&#039;гладкую кусочно-линейную карту&#039;&#039;&#039; с явными разрывами на резах. Это перенос сильного аудио-аппарата (&amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt;) на выход зрения: зрение видит структуру сдвига точнее аудио, а прежний «скат» (&amp;lt;code&amp;gt;maximum.accumulate&amp;lt;/code&amp;gt;) её подавлял.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;61-из-pred-в-узлы-сетки-o-w-vision_ow&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.1. Из &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в узлы сетки &amp;lt;code&amp;gt;(o, w)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;vision_ow&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для сопоставленных кадров считаем время в рефе и сдвиг в кадрах:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
t^{\text{ref}}_k=\frac{\text{pred}[k]}{\text{fps}_{\text{ref}}},\qquad&lt;br /&gt;
\text{sh}_k=\Big(\frac{k}{\text{fps}_{\text{dub}}}-t^{\text{ref}}_k\Big)\cdot\text{fps}_{\text{ref}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На каждом узле сетки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t\in T&amp;lt;/math&amp;gt; в окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt; (0.3 с) берём &#039;&#039;&#039;взвешенную медиану&#039;&#039;&#039; сдвига (вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;), и оцениваем уверенность как медиану &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;, умноженную на &#039;&#039;&#039;долю согласных&#039;&#039;&#039; (anchor-«agree») — кадров, чей сдвиг в пределах &amp;lt;code&amp;gt;tol_fr=2&amp;lt;/code&amp;gt; кадра от медианы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o[t]=\operatorname{wmedian}\big(\text{sh},\ \cos\big),\qquad&lt;br /&gt;
w[t]=\operatorname{median}(\cos)\cdot\underbrace{\tfrac{1}{n}\#\{|\text{sh}-o|\le2\}}_{\text{agree}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Взвешенная медиана — робастная статистика без порогов: сортируем по значению, идём по кумулятивному весу до половины. Пустые узлы интерполируются (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w=0&amp;lt;/math&amp;gt;), веса нормируются на медиану положительных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;62-детект-ступеней-dp-сегментация-ломаными-detectwarp_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.2. Детект ступеней: DP-сегментация ломаными (&amp;lt;code&amp;gt;detect.warp_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кривую &amp;lt;code&amp;gt;o(t)&amp;lt;/code&amp;gt; режем на сегменты, в каждом — &#039;&#039;&#039;одна взвешенная прямая&#039;&#039;&#039;; стыки сегментов = кандидаты в резы. Это DP по префиксным суммам.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: робастная прямая (IRLS).&#039;&#039;&#039; Обычная прямая по методу наименьших квадратов легко уводится одной выбросной точкой. IRLS (итеративно перевзвешенный МНК) лечит это: проводим прямую, смотрим, кто далеко отклонился, уменьшаем таким точкам вес и проводим заново. Через пару итераций прямая опирается на основную массу точек и игнорирует выбросы.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Взвешенная прямая (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, IRLS).&#039;&#039;&#039; Для точек &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t,y)&amp;lt;/math&amp;gt; с весами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; оценка наклона &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;a&amp;lt;/math&amp;gt; и сдвига &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;b&amp;lt;/math&amp;gt; — обычная взвешенная регрессия, но в &#039;&#039;&#039;2 итерации с даунвейтом выбросов&#039;&#039;&#039; (Iteratively Reweighted Least Squares):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\frac{\sum w\,(t-\bar t)(y-\bar y)}{\sum w\,(t-\bar t)^2},\quad b=\bar y-a\bar t,\quad&lt;br /&gt;
w\leftarrow w\cdot\exp\!\Big[-\big(\tfrac{r}{2.5\,s}\big)^2\Big],&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r=y-(at+b)&amp;lt;/math&amp;gt; — остаток, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;s&amp;lt;/math&amp;gt; — взвешенный СКО. Второй проход гасит выбросы → прямая не ведётся за единичными ложными узлами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Сегментация (&amp;lt;code&amp;gt;_wsegment&amp;lt;/code&amp;gt;).&#039;&#039;&#039; Стоимость сегмента &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[i,j)&amp;lt;/math&amp;gt; — взвешенная SSE наилучшей прямой (берётся за &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(1)&amp;lt;/math&amp;gt; из шести префиксных сумм &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sum w,\sum wt,\sum wy,\sum wt^2,&lt;br /&gt;
\sum wty,\sum wy^2&amp;lt;/math&amp;gt;). DP минимизирует суммарную ошибку плюс штраф &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt; за каждый сегмент (чем больше PEN, тем меньше резов), при минимальной длине сегмента &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{opt}[j]=\min_{i\le j-\text{msize}}\big(\text{opt}[i]+\text{SSE}(i,j)+\text{PEN}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Наклон каждого сегмента ограничен потолком &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt; (физика дрейфа ≤~2%). Рез ставится на стыке, если разрыв прямых &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\ge&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt; (3 кадра ≈ 120 мс).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;63-фильтр-резов-устойчивость-уровня-на-большом-окне-video_cut_filter&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.3. Фильтр резов: устойчивость уровня на большом окне (&amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
DP-сегментация склонна давать избыточные стыки; настоящий рез отличается тем, что уровень offset &#039;&#039;&#039;устойчиво&#039;&#039;&#039; разный слева и справа на &#039;&#039;&#039;большом&#039;&#039;&#039; окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt; (80 с), и считать его надо по &#039;&#039;&#039;надёжным&#039;&#039;&#039; узлам. Берём взвешенную медиану уровня слева/справа (робастна к «холмику» в окне — холмик у слепой зоны зрения возвращается, и медиана его игнорирует):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{рез реален}\iff \big|\operatorname{wmedian}(o_R,w^3)-\operatorname{wmedian}(o_L,w^3)\big|\ge\text{VC}\_\text{THR}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два важных нюанса:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;вес-гейт&#039;&#039;&#039; &amp;lt;code&amp;gt;VC_GATE=0.55&amp;lt;/code&amp;gt;: в окне берутся только узлы с весом выше квантиля 0.55 (&amp;lt;code&amp;gt;_gmask&amp;lt;/code&amp;gt;) — всплески у слепых зон отбрасываются;&lt;br /&gt;
* &#039;&#039;&#039;окно ограничено соседними резами&#039;&#039;&#039; (как &amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt; в аудио): на узком сегменте между двумя близкими резами окно &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm80&amp;lt;/math&amp;gt; с не должно перехлёстывать через соседний рез, иначе поймает чужой уровень и &#039;&#039;&#039;ложно подтвердит&#039;&#039;&#039; рез. Регрессия 0/36 дорожек.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;64-кусочно-линейная-укладка-piecewise_lines&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.4. Кусочно-линейная укладка (&amp;lt;code&amp;gt;piecewise_lines&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Между резами кладём &#039;&#039;&#039;одну робастную прямую&#039;&#039;&#039; (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;=0.45 к/с), изломы только на резах. Это усредняет весь кусок и потому нечувствительно к мелкой ряби &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;. Итог — кривая сдвига на сетке T.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;65-сборка-карты-build_map&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.5. Сборка карты (&amp;lt;code&amp;gt;build_map&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{shift}(t)=\operatorname{interp}(t;\ T,\ \text{curve}),\qquad&lt;br /&gt;
\tau(t)=t+\text{shift}(t)\cdot\frac{\text{FRAME}}{1000}\ \text{[с]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На резах в &amp;lt;code&amp;gt;shift&amp;lt;/code&amp;gt; ступень (разрыв) — её перекрывает &#039;&#039;&#039;тишина&#039;&#039;&#039; (вызывающий разносит контент на монтажном стыке). Между резами карта монотонна (наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;). Выход: &amp;lt;code&amp;gt;(grid, tg_s, cuts, o, w, curve)&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;tg_s&amp;lt;/code&amp;gt; идёт в ресэмпл аудио, &amp;lt;code&amp;gt;o/w/curve&amp;lt;/code&amp;gt; — в графики, &amp;lt;code&amp;gt;cuts&amp;lt;/code&amp;gt; — в тишину.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    PRED[pred / asg / cos] --&amp;gt; OW[&amp;quot;vision_ow → (o, w) на сетке T&amp;quot;]&lt;br /&gt;
    OW --&amp;gt; WC[warp_curve: DP-сегментация ломаными]&lt;br /&gt;
    WC --&amp;gt; VCF[video_cut_filter:&amp;lt;br/&amp;gt;устойчивость уровня ±80с, вес-гейт,&amp;lt;br/&amp;gt;окно по соседним резам]&lt;br /&gt;
    OW --&amp;gt; PL[piecewise_lines:&amp;lt;br/&amp;gt;робастная прямая между резами]&lt;br /&gt;
    VCF --&amp;gt; PL&lt;br /&gt;
    PL --&amp;gt; MAP[&amp;quot;build_map → tg_s + cuts&amp;quot;]&lt;br /&gt;
    MAP --&amp;gt; SIL[Тишина в резах]&lt;br /&gt;
    MAP --&amp;gt; RES[Ресэмпл аудио дубля на сетку рефа]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части I.&#039;&#039;&#039; Зрение превращает два видеофайла в карту &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\tau(t)&amp;lt;/math&amp;gt; «время рефа → время дубля» — монотонную кусочно-линейную, с явными монтажными резами, устойчивую к двойкам, лого и слепым зонам. По этой карте аудио дубля ресэмплится на таймлайн рефа; в резах ставится тишина. Дальше — слой звука.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-ii-звук--доводка-bandmuq-поверх-зрения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть II. Звук — доводка band/muq поверх зрения =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt; (оркестровка), &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt; (метрика), &amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt; (общий аппарат детекта, тот же, что у зрения), &amp;lt;code&amp;gt;anchor/assemble.py&amp;lt;/code&amp;gt;. Метод по умолчанию — &#039;&#039;&#039;band&#039;&#039;&#039; (DSP, 48 полос, без модели/лицензии); альтернатива &#039;&#039;&#039;muq&#039;&#039;&#039; (GPU-эмбеддинги, та же сборка).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;7-зачем-аудио-после-видео&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;7-зачем-аудио-после-видео&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 7. Зачем аудио после видео ==&lt;br /&gt;
&lt;br /&gt;
Зрение положило дубль по картинке. Но между &#039;&#039;&#039;видео и аудио внутри самого файла&#039;&#039;&#039; бывает сдвиг, которого на картинке не видно:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;AV-sync delta&#039;&#039;&#039; — разный AAC priming / encoder delay (в JoJo измерено стабильные +89 мс между файлами, &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §4.4&amp;lt;/code&amp;gt;);&lt;br /&gt;
* &#039;&#039;&#039;студийный сдвиг M&amp;amp;amp;E&#039;&#039;&#039; — студия подвинула фон/музыку в миксе относительно своего видео;&lt;br /&gt;
* &#039;&#039;&#039;двойки&#039;&#039;&#039; — видео физически не различает соседние одинаковые кадры (предел ±83 мс), аудио снимает это ограничение.&lt;br /&gt;
&lt;br /&gt;
Поэтому второй слой меряет &#039;&#039;&#039;остаточный&#039;&#039;&#039; сдвиг аудио дубля (уже лежащего по видео-карте) относительно аудио рефа и доводит его. Важно: аудио-слой работает &#039;&#039;&#039;после&#039;&#039;&#039; простановки тишины в монтажных резах — он должен видеть реф/тишину в вырезе (как боевой эталон), а не сырой несинхронный дубль, иначе на стыке зрение↔аудио появляется ложный краевой рез.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;8-принцип-зрячих-свидетелей&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;8-принцип-зрячих-свидетелей&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 8. Принцип зрячих свидетелей ==&lt;br /&gt;
&lt;br /&gt;
Отчёт &amp;lt;code&amp;gt;METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt; фиксирует намертво: &#039;&#039;&#039;наивная корреляция звука слепа&#039;&#039;&#039;, и это легко принять за «аудио измерить нельзя». Озвучка = чужой голос поверх той же музыки и эффектов (M&amp;amp;amp;E). Прямой NCC сырого сигнала ловит несовпадение голоса и возвращает шум. Лечится &#039;&#039;&#039;не другим инструментом, а изоляцией инварианта ПЕРЕД корреляцией&#039;&#039;&#039;. У каждого искажения дубляжа есть то, что оно не трогает:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Искажение дубляжа&lt;br /&gt;
! Что губит&lt;br /&gt;
! Инвариант (что не трогает)&lt;br /&gt;
! Как изолируем&lt;br /&gt;
|-&lt;br /&gt;
| Чужой голос диктора&lt;br /&gt;
| часть частотных полос&lt;br /&gt;
| чистые полосы M&amp;amp;amp;E&lt;br /&gt;
| &#039;&#039;&#039;48 лог-полос + взвеш. медиана&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Другой мастеринг/тембр&lt;br /&gt;
| тонкую структуру волны&lt;br /&gt;
| динамику громкости&lt;br /&gt;
| &#039;&#039;&#039;лог-огибающая RMS&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Повтор музыкального такта&lt;br /&gt;
| однозначность пика&lt;br /&gt;
| окна с одиночным пиком&lt;br /&gt;
| &#039;&#039;&#039;comb-veto&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Свидетели разной физики теряют чувствительность в &#039;&#039;&#039;разных&#039;&#039;&#039; местах → их сумма покрывает всю длину. Ниже подробно про основной свидетель боевого band — &#039;&#039;&#039;многополосную метрику&#039;&#039;&#039; (она же несёт оба первых инварианта: полосы против голоса, лог-энергия против мастеринга).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 8.1 — «Почему полосы видят сквозь голос».&#039;&#039;&#039; Спектрограмма окна озвучки: горизонтальными лентами выделены 48 лог-полос; полосы, занятые голосом диктора (≈300–3000 Гц), подсвечены красным («низкий контраст, малый вес»), полосы чистой музыки/эффектов — зелёным («высокий контраст, голосуют»). Снизу — та же сцена в рефе. Подпись: «Взвешенная медиана по полосам игнорирует испорченные голосом полосы — без знания, где голос».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;9-band-метрика-48-полос&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;9-band-метрика-48-полос&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 9. band-метрика: 48 полос ==&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt;. Рецепт &amp;lt;code&amp;gt;NB48&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;sr=16000&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;NB=48&amp;lt;/math&amp;gt; лог-полос в диапазоне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;50\text{–}14000&amp;lt;/math&amp;gt; Гц, окно &amp;lt;code&amp;gt;win=5&amp;lt;/code&amp;gt; с, STFT &amp;lt;code&amp;gt;nfft=2048&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hop=256&amp;lt;/code&amp;gt; (⇒ кадровый темп огибающей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{fps}=sr/hop=62.5&amp;lt;/math&amp;gt; Гц), агрегация &amp;lt;code&amp;gt;wmedian&amp;lt;/code&amp;gt;, качество &amp;lt;code&amp;gt;agree&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;91-полосовые-огибающие-_benv&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.1. Полосовые огибающие (&amp;lt;code&amp;gt;_benv&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: STFT и полосы.&#039;&#039;&#039; STFT (кратковременное преобразование Фурье) режет звук на короткие кусочки и для каждого показывает, сколько в нём энергии на каждой частоте, — получается спектрограмма «время × частота». Мы группируем частоты в 48 логарифмических полос (как деления на эквалайзере) и следим за громкостью каждой полосы во времени — это и есть «огибающая полосы». Лог-шкала частот ближе к слуху: низкие частоты дробятся мельче, высокие — крупнее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Для окна сигнала &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;x&amp;lt;/math&amp;gt; берём STFT и мощность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;P=|Z|^2&amp;lt;/math&amp;gt;. Лог-частотная &#039;&#039;&#039;полосовая маска&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;BM\in\{0,1\}^{NB\times(nfft/2+1)}&amp;lt;/math&amp;gt; (границы — &amp;lt;code&amp;gt;logspace(50,14000,49)&amp;lt;/code&amp;gt;) суммирует мощность по полосам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
E_{b,t}=\sum_f BM_{b,f}\,P_{f,t},\qquad&lt;br /&gt;
\tilde E_{b,t}=\log(1+E_{b,t}).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждая полоса z-нормируется по времени (убираем общий уровень и масштаб — это инвариант к мастерингу):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\hat E_{b,t}=\frac{\tilde E_{b,t}-\operatorname{mean}_t \tilde E_b}{\operatorname{std}_t \tilde E_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;log1p&amp;lt;/code&amp;gt; + z-score — это и есть «лог-огибающая громкости полосы»: динамика, общая у дубля и рефа, остаётся; тембр выкидывается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;92-по-полосный-лаг-через-кросс-корреляцию-_om_core&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.2. По-полосный лаг через кросс-корреляцию (&amp;lt;code&amp;gt;_om_core&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для каждой полосы независимо считаем кросс-корреляцию огибающих рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{r}_b&amp;lt;/math&amp;gt; и дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{d}_b&amp;lt;/math&amp;gt; по времени (через FFT, знак &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;E_d\cdot\overline{E_r}&amp;lt;/math&amp;gt; даёт «правее=+»):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{cc}_{b}[\ell]=\big(\hat E^{d}_b \star \hat E^{r}_b\big)[\ell],\qquad&lt;br /&gt;
\ell\in[-M_f,\,M_f],\ \ M_f=\text{maxlag}\cdot\text{fps}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выраженность пика полосы (насколько он торчит над фоном):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{prom}_b=\max_\ell \text{cc}_b[\ell]-\operatorname{median}_\ell \text{cc}_b[\ell].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;93-агрегация-взвешенная-медиана-по-полосам-aggwmedian&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.3. Агрегация: взвешенная медиана по полосам (&amp;lt;code&amp;gt;agg=&amp;amp;quot;wmedian&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Вместо суммы корреляций (её подавляет голос) — &#039;&#039;&#039;по-полосный аргмакс-лаг&#039;&#039;&#039; и &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; этих лагов по 48 полосам, вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{prom}_b^{\text{promp}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{promp}=1&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{bsh}_b=\arg\max_\ell \text{cc}_b[\ell],\qquad&lt;br /&gt;
\hat\ell=\operatorname{wmedian}\big(\{\text{bsh}_b\},\ \{\text{prom}_b\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Медиана робастна: даже если голос испортил треть полос и они показывают «не туда», медиана держится за большинство чистых. Финальный сдвиг уточняется &#039;&#039;&#039;параболической интерполяцией&#039;&#039;&#039; вокруг &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat\ell&amp;lt;/math&amp;gt; по агрегированной поверхности и переводится в кадры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}=\frac{\hat\ell-\tfrac12\frac{y_{+}-y_{-}}{y_{+}-2y_0+y_{-}}}{\text{fps}}\cdot\frac{1000}{\text{FRAME}}\ \text{[кадры]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;94-качество--согласие-полос-qualityagree&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.4. Качество = согласие полос (&amp;lt;code&amp;gt;quality=&amp;amp;quot;agree&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Уверенность узла — &#039;&#039;&#039;доля полос, чей собственный аргмакс-лаг согласен&#039;&#039;&#039; с итоговым сдвигом (в пределах &amp;lt;code&amp;gt;tol=2&amp;lt;/code&amp;gt; кадра), взвешенная по выраженности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
w=\frac{\sum_b \text{prom}_b\cdot\mathbf{1}\big[|\text{bsh}_b-\text{off}|\le 2\big]}{\sum_b \text{prom}_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это самопроверка без внешней истины: если полосы единодушны — узлу можно верить; если разбрелись (повтор/тишина/голос везде) — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; мал, и детект/укладка такой узел не слушают.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    X[Окно 5с ref и dub] --&amp;gt; ST[STFT 2048/256]&lt;br /&gt;
    ST --&amp;gt; BM[48 лог-полос&amp;lt;br/&amp;gt;log1p + z-score по времени]&lt;br /&gt;
    BM --&amp;gt; CC[По-полосная кросс-корр&amp;lt;br/&amp;gt;cc_b lag]&lt;br /&gt;
    CC --&amp;gt; PR[prom_b = max − median]&lt;br /&gt;
    CC --&amp;gt; BSH[bsh_b = argmax lag]&lt;br /&gt;
    BSH --&amp;gt; WM[&amp;quot;wmedian по полосам (вес prom)&amp;quot;]&lt;br /&gt;
    PR --&amp;gt; WM&lt;br /&gt;
    WM --&amp;gt; OFF[off: сдвиг кадры + парабола]&lt;br /&gt;
    BSH --&amp;gt; AG[&amp;quot;agree: доля согласных полос → w&amp;quot;]&lt;br /&gt;
    OFF --&amp;gt; AG&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;muq как альтернатива.&#039;&#039;&#039; Метод &amp;lt;code&amp;gt;muq&amp;lt;/code&amp;gt; заменяет полосовую огибающую на GPU-эмбеддинги музыкальной модели, но &#039;&#039;&#039;дальше всё то же&#039;&#039;&#039; — та же поверхность &amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt;, тот же детект &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt;, та же сборка &amp;lt;code&amp;gt;assemble.py&amp;lt;/code&amp;gt;. band не требует новых зависимостей (только torch/numpy) и потому выбран дефолтом.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;10-грубая-off0--следящий-band&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;10-грубая-off0--следящий-band&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 10. Грубая off0 + следящий band ==&lt;br /&gt;
&lt;br /&gt;
Рабочее окно band узкое — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MAXLAG=0.7&amp;lt;/code&amp;gt; с (точность ценой диапазона). Но сдвиг опенинга бывает крупнее. Поэтому, как и в зрении, две стадии: грубая «тропа» + точное слежение (&amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;101-грубая-off0-_coarse_off0&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.1. Грубая off0 (&amp;lt;code&amp;gt;_coarse_off0&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Та же band-метрика, но с &#039;&#039;&#039;широким&#039;&#039;&#039; окном лага &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;COARSE_LAG_S=2.5&amp;lt;/code&amp;gt; с — она видит большой сдвиг. Робастность только статистикой, без порогов под контент:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;страж края&#039;&#039;&#039; — если аргмакс на границе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, это «нет пика», вес обнуляется;&lt;br /&gt;
# скользящая &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; (окно &amp;lt;code&amp;gt;med_win_s=5&amp;lt;/code&amp;gt; с) сдвига;&lt;br /&gt;
# &#039;&#039;&#039;гейт согласия соседей&#039;&#039;&#039; — узел стабилен, если &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|o-\text{med}|\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;stab_tol=4&amp;lt;/code&amp;gt; кадра;&lt;br /&gt;
# повторная медиана только по стабильным + интерполяция → непрерывная &amp;lt;code&amp;gt;off0(t)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;102-слежение-bandbuild_arr-07с&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.2. Слежение (&amp;lt;code&amp;gt;band.build_arr ±0.7с&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль предварительно варпится по &amp;lt;code&amp;gt;off0&amp;lt;/code&amp;gt; (грубая коррекция), и band с &#039;&#039;&#039;узким&#039;&#039;&#039; окном &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.7&amp;lt;/math&amp;gt; с меряет &#039;&#039;&#039;остаток&#039;&#039;&#039; на пред-варпленном дубле. Полный сдвиг — сумма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o(t)=\text{off0}(t)+o_{\text{ост}}(t).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так узкое точное окно никогда не «теряет» крупный сдвиг — он уже снят грубой тропой, а band доводит лишь малый остаток. (Урок &amp;lt;code&amp;gt;EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;: GCC в грубом проходе подавляется голосом, band-грубый с медианой по полосам — нет.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;11-детект-ступеней-и-кривая-дрейфа&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;11-детект-ступеней-и-кривая-дрейфа&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 11. Детект ступеней и кривая дрейфа ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;111-детект--тот-же-аппарат-что-у-зрения-detectdetect&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.1. Детект — тот же аппарат, что у зрения (&amp;lt;code&amp;gt;detect.detect&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt; идёт в &#039;&#039;&#039;тот же&#039;&#039;&#039; &amp;lt;code&amp;gt;warp_curve&amp;lt;/code&amp;gt; (DP-сегментация взвешенными ломаными, §6.2), затем:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt;&#039;&#039;&#039; — рез реален только при устойчивом сдвиге &#039;&#039;&#039;хвоста&#039;&#039;&#039;: медиана уровня до/после на окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;win=25&amp;lt;/code&amp;gt; с, ограниченном соседними резами, с вес-гейтом; величина реза = это смещение. Аналог &amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt; из зрения, но для аудио.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;_edge_refine&amp;lt;/code&amp;gt;&#039;&#039;&#039; — отдельно выделяет рез в первом/последнем сегменте (краевая зона, короче &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;), где обычная сегментация его не выделит.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;112-кривая-дрейфа-надараяуотсон--потолок-скорости-_smooth_drift_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.2. Кривая дрейфа: Надарая–Уотсон + потолок скорости (&amp;lt;code&amp;gt;_smooth_drift_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: сглаживание Надарая–Уотсона.&#039;&#039;&#039; Это «скользящее среднее с приоритетом надёжным точкам»: значение кривой в каждый момент — среднее соседних измерений, но уверенные точки (большой вес &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt;) тянут сильнее, а шумные почти не влияют. Ширина «гауссова окна» &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma&amp;lt;/math&amp;gt; задаёт, насколько далёких соседей ещё учитывать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Внутри куска между резами строим гладкую кривую, которая &#039;&#039;&#039;следит за горками&#039;&#039;&#039; (плавный дрейф) и &#039;&#039;&#039;стоит на шуме&#039;&#039;&#039;. Это ядерное сглаживание Надарая–Уотсона с весом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w^{\text{qpow}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{qpow}=3&amp;lt;/math&amp;gt;, гауссово ядро &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma=3&amp;lt;/math&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{sm}(t)=\frac{\big(o\cdot w^{3}\big)\!*\!G_\sigma}{w^{3}\!*\!G_\sigma+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поверх — &#039;&#039;&#039;потолок скорости изменения сдвига&#039;&#039;&#039;: между соседними узлами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta\text{sm}|&amp;lt;/math&amp;gt; не больше &amp;lt;code&amp;gt;lim&amp;lt;/code&amp;gt;, что соответствует ускорению &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt; (1.25 %/с = &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{lim}=\frac{\text{max}\_\text{pct}\_\text{s}}{100}\cdot\text{STEP}\cdot\frac{1000}{\text{FRAME}}\approx0.15\ \text{кадра/узел}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Потолок не даёт кривой прыгнуть за единичными яркими выбросами в слепой зоне.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;113-опора-после-реза-фикс-2026-06-18&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.3. Опора после реза (фикс 2026-06-18) ===&lt;br /&gt;
&lt;br /&gt;
Тонкость: в куске, &#039;&#039;&#039;который начинается с реза&#039;&#039;&#039;, band теряет чувствительность на самом монтажном стыке (тусклые выбросы сразу за резом). Если вести потолок скорости от левого края куска, он занижает старт, и кривая ~35 с медленно вытягивается к плато. Идея пользователя: расходиться в &#039;&#039;&#039;обе стороны от самого уверенного якоря&#039;&#039;&#039; в первых &amp;lt;code&amp;gt;anchor_lookback_s=30&amp;lt;/code&amp;gt; с куска (= начало плато):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;   сдвиг&lt;br /&gt;
     ▲                  ● ● ● ● ● ● ●  ← плато (уверенные якоря)&lt;br /&gt;
     │      опора ↑ argmax w в первых 30с&lt;br /&gt;
     │     ╱   ╲        clamp влево к резу + вправо вдоль плато&lt;br /&gt;
     │  ✗ ╱     ╲&lt;br /&gt;
     │ слепой    &lt;br /&gt;
     │ старт      &lt;br /&gt;
     ┼──┊─────────────────────────────►  t&lt;br /&gt;
        рез&amp;lt;/pre&amp;gt;&lt;br /&gt;
Слепой старт подтягивается &#039;&#039;&#039;к&#039;&#039;&#039; плато, а не плато к старту. Первый кусок (без реза слева) — по-старому: левый край = опора. Приёмка: 47/55 дорожек не тронуты, изменения = только исправления (память &amp;lt;code&amp;gt;project_band_postcut_anchor_fix&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;12-варп-тишина-в-резах-заполнение-рефом&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;12-варп-тишина-в-резах-заполнение-рефом&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 12. Варп, тишина в резах, заполнение рефом ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;121-кусочный-варп-_warp_piecewise&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.1. Кусочный варп (&amp;lt;code&amp;gt;_warp_piecewise&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль ресэмплится по кускам между резами: внутри куска — гладкая &amp;lt;code&amp;gt;wcurve&amp;lt;/code&amp;gt; (горки), на резе — резкий стык. Для выходного отсчёта &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;lt;/math&amp;gt; источник в дубле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{src}(t)=\Big(t+\text{wcurve}(t)\cdot\tfrac{\text{FRAME}}{1000}\Big)\cdot sr,\qquad&lt;br /&gt;
\text{out}[t]=\operatorname{interp}\big(\text{src}(t)\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;122-тишина-в-резах&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.2. Тишина в резах ===&lt;br /&gt;
&lt;br /&gt;
Непрерывный варп на монтажном стыке «переиграл» бы звук дважды, поэтому в резах ставится тишина (контент разносится):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;lt;0&amp;lt;/math&amp;gt; (нехватка дубля) → тишина &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta|&amp;lt;/math&amp;gt; по центру реза;&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;gt;0&amp;lt;/math&amp;gt; (лишнее вырезано) → узкий шов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.15&amp;lt;/math&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;123-заполнение-тишины-синхронным-рефом-_fill_silence_from_ref&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.3. Заполнение тишины синхронным рефом (&amp;lt;code&amp;gt;_fill_silence_from_ref&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Финальный проход — &#039;&#039;&#039;только после&#039;&#039;&#039; полного band/muq, когда дорожка уже синхронна рефу. Там, где озвучка молчит, а реф звучит, подставляем реф с кроссфейдом. Порог тишины измерен:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{RMS}_{\text{dBFS}}=20\log_{10}\!\Big(\tfrac{\text{RMS}}{32768}+\varepsilon\Big),\qquad&lt;br /&gt;
\text{fill}\iff \text{RMS}_{\text{дубль}}&amp;lt;-90\ \text{дБ}\ \wedge\ \text{RMS}_{\text{реф}}\ge-90\ \text{дБ}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порог &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-90&amp;lt;/math&amp;gt; дБ — в центре измеренного плато настоящих дыр (ниже &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-80&amp;lt;/math&amp;gt;), далеко от обрыва тихого контента (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-40\ldots-60&amp;lt;/math&amp;gt;), окно RMS 20 мс, мин. длина зоны 0.15 с, кроссфейд 30 мс. Где у обоих тишина — остаётся тишина; озвучка не теряется (трогаем только пустоты).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;Запрет (намертво).&#039;&#039;&#039; &amp;lt;code&amp;gt;fill_cuts&amp;lt;/code&amp;gt; — заливка вырезов рефом &#039;&#039;&#039;по видео, до&#039;&#039;&#039; анализа — запрещён навсегда: вставка в ещё несинхронную дорожку плодит мнимый рассинхрон. Разрешено только &amp;lt;code&amp;gt;fill_silence&amp;lt;/code&amp;gt; — заполнение &#039;&#039;&#039;после&#039;&#039;&#039; выравнивания, по синхронной дорожке (память &amp;lt;code&amp;gt;feedback_no_audio_replacement_fill_cuts&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;124-кросс-модальный-детектор-студийного-av-десинка-detect_av_desync&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.4. Кросс-модальный детектор студийного A/V-десинка (&amp;lt;code&amp;gt;detect_av_desync&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Read-only диагностика (на wav не влияет). После укладки &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; уже на сетке рефа; широким PHAT-окном (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, полоса 50–13500 Гц) меряем остаточный лаг M&amp;amp;amp;E к реф-аудио по окнам. Если он &#039;&#039;&#039;большой и устойчивый&#039;&#039;&#039; (медиана велика, MAD мал) — аудио источника смещено относительно его же видео (студийный дефект, надёжно не чинится) → дорожка помечается &#039;&#039;&#039;красным&#039;&#039;&#039;. Паттерн валидирован: AniLibria ep01 kamennyj-okean медиана −40к / MAD 2.2к против ~0/~0 у 10 здоровых.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    SIL[Дубль на сетке рефа + тишина в резах] --&amp;gt; OFF0[_coarse_off0&amp;lt;br/&amp;gt;band ±2.5с, медиана по полосам]&lt;br /&gt;
    OFF0 --&amp;gt; PRE[Пред-варп по off0]&lt;br /&gt;
    PRE --&amp;gt; BAND[&amp;quot;band ±0.7с → o_ост, w&amp;quot;]&lt;br /&gt;
    BAND --&amp;gt; SUM[&amp;quot;o = off0 + o_ост&amp;quot;]&lt;br /&gt;
    SUM --&amp;gt; DET[detect.detect&amp;lt;br/&amp;gt;warp_curve + tail_filter + edge_refine]&lt;br /&gt;
    DET --&amp;gt; DRIFT[_smooth_drift_curve&amp;lt;br/&amp;gt;Надарая–Уотсон + потолок 1.25%/с + опора-после-реза]&lt;br /&gt;
    DRIFT --&amp;gt; WP[_warp_piecewise + тишина в резах]&lt;br /&gt;
    WP --&amp;gt; FILL[_fill_silence_from_ref −90дБ]&lt;br /&gt;
    FILL --&amp;gt; AV[detect_av_desync&amp;lt;br/&amp;gt;красный флаг студийного десинка]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части II.&#039;&#039;&#039; Поверх видео-карты звук band/muq снимает остаточный сдвиг аудио, который видео не видит: многополосная метрика делает корреляцию зрячей сквозь голос и мастеринг, грубая off0 ловит крупный сдвиг, следящий band доводит остаток, общий аппарат &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt; ставит резы, кривая дрейфа с потолком скорости и опорой-после-реза кладёт гладко, тишина заполняется синхронным рефом. Дальше — как всё это читается на графиках.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-iii-графики--диагноз-с-одного-взгляда&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть III. Графики — диагноз с одного взгляда =&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/plots_unified.py&amp;lt;/code&amp;gt;. Проектная установка: алгоритмы доведены до состояния, где &#039;&#039;&#039;качество читается по графику, без ручной сверки в Premiere&#039;&#039;&#039;. Беглый взгляд на любой график даёт чёткий ответ «всё в порядке или нет». Поэтому график — не украшение, а главный инструмент приёмки и отладки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;13-зачем-графики&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;13-зачем-графики-и-что-на-них-видно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 13. Зачем графики и что на них видно ==&lt;br /&gt;
&lt;br /&gt;
Единый график строится &#039;&#039;&#039;на каждую пару&#039;&#039;&#039; и показывает оба слоя сразу: &#039;&#039;&#039;верхняя панель — зрение&#039;&#039;&#039; (видео-укладка, всегда), &#039;&#039;&#039;нижняя — звук&#039;&#039;&#039; (доводка band/muq, если включена), на общей оси времени с синхронным зумом и ховером. Один взгляд отвечает на четыре вопроса:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Вопрос&lt;br /&gt;
! Где смотреть&lt;br /&gt;
|-&lt;br /&gt;
| Держится ли укладка за &#039;&#039;&#039;надёжные&#039;&#039;&#039; данные?&lt;br /&gt;
| цвет точек (уверенность): линия должна идти по ярким, не по тусклым&lt;br /&gt;
|-&lt;br /&gt;
| Есть ли необъяснённый &#039;&#039;&#039;перекос/увод&#039;&#039;&#039;?&lt;br /&gt;
| отклонение линии от облака точек&lt;br /&gt;
|-&lt;br /&gt;
| Верно ли найдены &#039;&#039;&#039;монтажные резы&#039;&#039;&#039;?&lt;br /&gt;
| вертикальные пунктиры и их подписи (мс)&lt;br /&gt;
|-&lt;br /&gt;
| Согласны ли &#039;&#039;&#039;зрение и звук&#039;&#039;&#039;?&lt;br /&gt;
| сравнение двух панелей в одной зоне&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;14-дрейф-форма&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;14-дрейф-форма-и-робастная-база-тейласена&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 14. Дрейф-форма и робастная база Тейла–Сена ==&lt;br /&gt;
&lt;br /&gt;
Сырой сдвиг по всей серии может быть сотни кадров (крупный масштаб оси), и на нём не видно ни плато, ни ступеней в десяток кадров. Поэтому обе панели рисуются в &#039;&#039;&#039;дрейф-форме&#039;&#039;&#039; — вычитается робастная линейная база, и всё разворачивается вокруг нуля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: база Тейла–Сена.&#039;&#039;&#039; Чтобы провести «среднюю линию» через облако точек и не дать единичным выбросам её перекосить, берут наклон между каждой парой точек и выбирают медианный — половина пар «за», половина «против». Это устойчивее обычной прямой и не требует никаких порогов отбраковки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
База оценивается &#039;&#039;&#039;методом Тейла–Сена&#039;&#039;&#039; (&amp;lt;code&amp;gt;_baseline&amp;lt;/code&amp;gt;) — он устойчив к выбросам, в отличие от МНК: на выборке точек (до 400) берутся все попарные наклоны, и базовый наклон — их медиана; сдвиг — медиана остатков:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\operatorname{median}_{i&amp;lt;j}\frac{y_j-y_i}{t_j-t_i},\qquad&lt;br /&gt;
b=\operatorname{median}_k\big(y_k-a\,t_k\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
База считается по &#039;&#039;&#039;телу&#039;&#039;&#039; дорожки (исключая голову/заставку: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;gt;\text{head}+5&amp;lt;/math&amp;gt; с). Затем:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{dev}_{\text{якорь}}=\text{shift}-(a\,t+b),\qquad&lt;br /&gt;
\text{dev}_{\text{кривая}}=\text{curve}-(a\,T+b),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а предел оси берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\max(12,\ p_{99}(|\text{dev}|)+4)&amp;lt;/math&amp;gt; кадров — авто-масштаб под реальный разброс. На резах в линию вставляется &amp;lt;code&amp;gt;NaN&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;_breaks&amp;lt;/code&amp;gt;), чтобы она &#039;&#039;&#039;не соединяла&#039;&#039;&#039; куски через разрыв — ступень видна как обрыв, а не как вертикаль.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 14.1 — «Сырой сдвиг vs дрейф-форма».&#039;&#039;&#039; Два графика друг над другом для одной дорожки: сверху сырой &amp;lt;code&amp;gt;shift(t)&amp;lt;/code&amp;gt; (линия уходит на −600 кадров, мелкие детали не видны); снизу та же дорожка в дрейф-форме (база вычтена, всё колеблется в ±15 кадров вокруг 0, видны и плато, и ступенька реза). Подпись: «Дрейф-форма вытаскивает структуру, которую крупный масштаб прячет».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;15-слои-и-разбор-кейсов&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;15-слои-цветокодирование-разбор-реальных-кейсов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 15. Слои, цветокодирование, разбор реальных кейсов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;151-слои-единого-графика&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.1. Слои единого графика ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхняя панель (ЗРЕНИЕ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки якорей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t_{\text{реф}},\ \text{dev}_{\text{якорь}})&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;cos&#039;&#039;&#039; (viridis, 0…1) — надёжность каждого видео-якоря;&lt;br /&gt;
* оранжевая линия — &#039;&#039;&#039;карта зрения&#039;&#039;&#039; в дрейф-форме, с разрывами на резах;&lt;br /&gt;
* crimson-пунктиры — &#039;&#039;&#039;резы зрения&#039;&#039;&#039; с подписью величины (мс);&lt;br /&gt;
* заголовок несёт абсолютную медиану сдвига.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Нижняя панель (ЗВУК):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(T,\ o)&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;w&#039;&#039;&#039; (cividis, 0…1.2) — уверенность band;&lt;br /&gt;
* серые точки — &#039;&#039;&#039;gcc-свидетель&#039;&#039;&#039; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с), справочно;&lt;br /&gt;
* зелёная линия — &#039;&#039;&#039;кривая band&#039;&#039;&#039; (дрейф), с разрывами на резах;&lt;br /&gt;
* purple-пунктиры — &#039;&#039;&#039;резы детекта&#039;&#039;&#039; аудио.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;gcc — НЕ судья.&#039;&#039;&#039; Серая gcc-линия на контенте проекта (закадр+музыка) почти слепа и скачет ±40к = шум; её нельзя брать за подтверждение укладки. Судьи сходимости — сам band (48 полос) / muq + уши/глаза на наложенных дорожках (память &amp;lt;code&amp;gt;feedback_gcc_ncc_blind_judge&amp;lt;/code&amp;gt;). gcc на графике — фон, не вердикт.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;152-как-цвет-читается-мгновенно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.2. Как цвет читается мгновенно ===&lt;br /&gt;
&lt;br /&gt;
Цвет точки = уверенность. Тёмные (фиолетовые/синие) — шумные, ярких (жёлтые/зелёные) — надёжные. &#039;&#039;&#039;Правильная линия идёт по ярким точкам.&#039;&#039;&#039; Если линия отклонилась, а рядом плотное облако ярких — это сигнал проблемы: либо увод укладки, либо линия последовала за тусклыми выбросами на краю. Это и есть «диагноз с одного взгляда».&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.1 — «Эталон единого графика (здоровая дорожка)».&#039;&#039;&#039; Реальный скрин двухпанельного графика: вверху зрение — плотное жёлтое облако якорей точно на 0, оранжевая линия плоская, резов нет; внизу звук — узкое облако вокруг 0, зелёная линия плоская. Подпись: «Так выглядит ОК: линии по ярким точкам, ноль отклонения, нет резов». &#039;&#039;(Скрин взять из &amp;lt;code&amp;gt;&amp;amp;lt;серия&amp;amp;gt;/_plots/&amp;amp;lt;stem&amp;amp;gt;__track.png&amp;lt;/code&amp;gt; любой здоровой пары.)&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.2 — «Перекос укладки (до фикса опоры-после-реза)».&#039;&#039;&#039; Скрин зоны 180–220 с AniDUB: после реза линия band провалилась вниз за парой тусклых (тёмных) якорей у края, хотя выше плотное плато ярких якорей на другом уровне. Рядом — тот же участок после фикса: линия держится за плато (опора = увереннейший якорь начала плато), слепой старт подтянут к плато. Подпись: «Линия не должна вестись за тусклыми единицами на краю».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.3 — «Ложный рез из-за слепоты зрения (ep05 Amazing, 950–979 с)».&#039;&#039;&#039; Скрин зоны с двумя близкими резами: до фикса между ними узкий клин и лишний рез (окно проверки ±80 с перехлестнуло через соседний рез и поймало чужой уровень); после фикса (окно ограничено соседними резами) лишний рез убран. Подпись: «Окно проверки уровня ограничивается соседними резами — иначе чужой уровень ложно подтверждает рез».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;153-носители-png-превью-и-интерактивный-html&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.3. Носители: PNG-превью и интерактивный HTML ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PNG&#039;&#039;&#039; (&amp;lt;code&amp;gt;matplotlib&amp;lt;/code&amp;gt;, Agg) — статичная миниатюра (1 или 2 панели) для быстрого взгляда в панели/отчёте.&lt;br /&gt;
* &#039;&#039;&#039;HTML&#039;&#039;&#039; (&amp;lt;code&amp;gt;plotly&amp;lt;/code&amp;gt;, self-contained, открывается офлайн) — для детального разбора. Точки и линия в WebGL (&amp;lt;code&amp;gt;scattergl&amp;lt;/code&amp;gt;), чтобы линия рисовалась поверх точек независимо от порядка; ховер &amp;lt;code&amp;gt;x unified&amp;lt;/code&amp;gt;, &#039;&#039;&#039;зум любой зоны вручную&#039;&#039;&#039;. Отдельные PNG каждого реза не создаём — HTML позволяет приблизить любой стык (решение 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
Read-only по контракту: падение рендера не роняет conform (ловит вызывающий).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Приложения =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;приложение-а-константы&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-а-константы-и-их-обоснования&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение А. Константы и их обоснования ==&lt;br /&gt;
&lt;br /&gt;
Все ключевые числа калиброваны не «на глаз», а по &#039;&#039;&#039;плато нечувствительности&#039;&#039;&#039;: на свипе по диапазону результат (резы по датасету) не меняется — значит подстройки под контент нет.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;зрение&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Зрение ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;&lt;br /&gt;
| 128×72&lt;br /&gt;
| features&lt;br /&gt;
| размер серого кадра под SRM&lt;br /&gt;
|-&lt;br /&gt;
| clip &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| ±3&lt;br /&gt;
| features&lt;br /&gt;
| обрезка выбросов отклика фильтров&lt;br /&gt;
|-&lt;br /&gt;
| dim&lt;br /&gt;
| 18432&lt;br /&gt;
| features&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;2\cdot128\cdot72&amp;lt;/math&amp;gt;, два канала&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;K&amp;lt;/code&amp;gt; (coarse)&lt;br /&gt;
| 8&lt;br /&gt;
| coarse&lt;br /&gt;
| прорежение грубого прохода&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| coarse&lt;br /&gt;
| полуокно зануления при поиске 2-го пика&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VHI&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.60&lt;br /&gt;
| coarse&lt;br /&gt;
| порог «пик высокий» (надёжный якорь)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CMIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.12&lt;br /&gt;
| coarse&lt;br /&gt;
| порог различимости (пик одинок → не двойка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20&lt;br /&gt;
| dropdtw&lt;br /&gt;
| штраф выброса (базовый Drop-DTW)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;OPEN/EXT&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20 / 0.02&lt;br /&gt;
| dropdtw&lt;br /&gt;
| gap-open / gap-extend (острый шов)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DSYN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| плоская цена выброса кадра дубля (вставка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MATCH_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| guard: кадр с матчем лучше этого нельзя выбросить&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MARG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 120&lt;br /&gt;
| band_align&lt;br /&gt;
| полуширина следящей полосы (≈5 с)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CHUNK/OVERLAP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 4000 / 700&lt;br /&gt;
| band_align&lt;br /&gt;
| кусок Drop-DTW и перекрытие&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно агрегации якорей (плато 0.2–0.5)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 800&lt;br /&gt;
| vision_detect&lt;br /&gt;
| штраф DP-сегментации (плато 200–3000)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| vision_detect&lt;br /&gt;
| степень веса = (cos·agree)³ (плато 2–3)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.45&lt;br /&gt;
| vision_detect&lt;br /&gt;
| потолок наклона прямых, к/с (плато 0.3–0.6)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_MSIZE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30&lt;br /&gt;
| vision_detect&lt;br /&gt;
| мин. длина сегмента, с (плато 8–60)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно проверки устойчивости уровня (плато 45–120)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_GATE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.55&lt;br /&gt;
| vision_detect&lt;br /&gt;
| вес-гейт надёжных узлов (плато 0.3–0.8)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 200 мс&lt;br /&gt;
| vision_detect&lt;br /&gt;
| порог величины реза (плато 60–400 мс)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;звук&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Звук ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;sr&amp;lt;/code&amp;gt;&lt;br /&gt;
| 16000&lt;br /&gt;
| band&lt;br /&gt;
| частота анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NB&amp;lt;/code&amp;gt;&lt;br /&gt;
| 48&lt;br /&gt;
| band&lt;br /&gt;
| число лог-полос (50–14000 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;nfft/hop&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2048 / 256&lt;br /&gt;
| band&lt;br /&gt;
| STFT (темп огибающей 62.5 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;win&amp;lt;/code&amp;gt;&lt;br /&gt;
| 5 с&lt;br /&gt;
| band&lt;br /&gt;
| окно анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAXLAG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.7 с&lt;br /&gt;
| band&lt;br /&gt;
| рабочее окно лага (точность)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;COARSE_LAG_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2.5 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно грубой off0 (видит сдвиг опенинга)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tol&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2 кадра&lt;br /&gt;
| band&lt;br /&gt;
| допуск согласия полос (agree)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| 41.708 мс&lt;br /&gt;
| params&lt;br /&gt;
| мс/кадр (23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.5 с&lt;br /&gt;
| params&lt;br /&gt;
| шаг сетки T&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3 (120 мс)&lt;br /&gt;
| params&lt;br /&gt;
| порог величины реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 700&lt;br /&gt;
| params&lt;br /&gt;
| штраф DP-сегментации (детект)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| params&lt;br /&gt;
| степень веса якоря&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| params&lt;br /&gt;
| макс. наклон сегмента (≤~2%)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MSIZE_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 20 с&lt;br /&gt;
| params&lt;br /&gt;
| мин. длина сегмента&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 1.25 %/с&lt;br /&gt;
| apply&lt;br /&gt;
| потолок скорости кривой дрейфа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;anchor_lookback_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно поиска опоры-после-реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIL_FILL_DB&amp;lt;/code&amp;gt;&lt;br /&gt;
| −90 дБ&lt;br /&gt;
| align&lt;br /&gt;
| порог тишины озвучки (центр плато дыр)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;XFADE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 мс&lt;br /&gt;
| align&lt;br /&gt;
| кроссфейд заполнения рефом&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⏱ &#039;&#039;&#039;Правило прогонов&#039;&#039;&#039; (память &amp;lt;code&amp;gt;feedback_warn_long_runs&amp;lt;/code&amp;gt;): любой тест ≤10 мин — параллелить (клипы &amp;lt;code&amp;gt;--workers 8&amp;lt;/code&amp;gt;, конформы ×6 пар, синтетика &amp;lt;code&amp;gt;--workers 24&amp;lt;/code&amp;gt;) или резать объём; дольше 10 мин — только с явного согласия пользователя, оценку времени — заранее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;приложение-б-карта-файлов&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-б-карта-файлов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение Б. Карта файлов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;src/track_muxer/conform/&lt;br /&gt;
├── features.py              SRM-отпечатки кадров (KB/D1, L2, dim 18432)&lt;br /&gt;
├── kernel/&lt;br /&gt;
│   ├── coarse.py            грубый проход: G=cs·crᵀ, якоря, LIS, оконный&lt;br /&gt;
│   ├── dropdtw.py           Drop-DTW (base/affine/guard/free_start)&lt;br /&gt;
│   └── band_align.py        следящая полоса + склейка кусков&lt;br /&gt;
├── vision_detect.py         УКЛАДКА зрения: (o,w) → детект → ломаная (ЕДИНСТВЕННАЯ карта)&lt;br /&gt;
├── align.py                 оркестровка пары: pred → разбор правок → ресэмпл → звук → график&lt;br /&gt;
└── anchor/                  АУДИО-доводка band/muq поверх зрения&lt;br /&gt;
    ├── apply.py             coarse_off0, следящий band, smooth_drift_curve, варп, fill&lt;br /&gt;
    ├── detect.py            ОБЩИЙ аппарат: wfit, warp_curve, tail_filter, edge_refine&lt;br /&gt;
    ├── assemble.py          freeze + сборка варпа&lt;br /&gt;
    ├── params.py            FRAME, T, STEP, PEN, QPOW, SMAX, MSIZE_S …&lt;br /&gt;
    ├── maps/band.py         48-полосная DSP-метрика (без модели) — дефолт&lt;br /&gt;
    ├── maps/muq.py          GPU-эмбеддинги (альтернатива)&lt;br /&gt;
    ├── plots_unified.py     ЕДИНЫЙ график зрение+звук (PNG + HTML)&lt;br /&gt;
    └── plots_html.py        отдельный HTML band (легаси-путь)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Тема&lt;br /&gt;
! Отчёт (суть)&lt;br /&gt;
|-&lt;br /&gt;
| Прорыв видео-синхрона&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_align/BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Методика зрячих свидетелей аудио&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Грубый→band-follow&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Аудио-аанкер (band/muq)&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_audio_auto.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Архитектура conform&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/conform_architecture_audit.md&amp;lt;/code&amp;gt; §14&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Версия документа:&#039;&#039;&#039; 2026-06-18, под архитектуру «анализатор зрения — единственная видео-карта; band/muq — доводка поверх». Числа и формулы сверены с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt;. При расхождении кода и документа — приоритет у кода, документ актуализировать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1009</id>
		<title>Track Muxer</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1009"/>
		<updated>2026-06-18T19:50:57Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Импорт технической статьи об алгоритмах conform (зрение/звук)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Назначение документа.&#039;&#039;&#039; Полное инженерное описание двух алгоритмов выравнивания, на которых стоит модуль &amp;lt;code&amp;gt;conform&amp;lt;/code&amp;gt;: &#039;&#039;&#039;анализатора зрения&#039;&#039;&#039; (строит карту таймлайна по видеоряду) и &#039;&#039;&#039;аудио-доводки band/muq&#039;&#039;&#039; (правит остаточный сдвиг звука поверх зрения). Документ самодостаточен: математика, геометрия, рекурренты, точные константы и их обоснования, схемы пайплайнов и разбор реальных диагностических графиков. Читатель — инженер, который при желании сможет воспроизвести метод.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Конвенция знаков (сквозная).&#039;&#039;&#039; Сдвиг измеряем в &#039;&#039;кадрах рефа&#039;&#039;; &#039;&#039;&#039;правее (дубль отстаёт) = +&#039;&#039;&#039;, левее (спешит) = −. Один кадр = &#039;&#039;&#039;41.708 мс&#039;&#039;&#039; (23.976 fps, &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt; в &amp;lt;code&amp;gt;anchor/params.py&amp;lt;/code&amp;gt;). Сетка анализа звука &amp;lt;code&amp;gt;T = arange(2.5, 1400, 0.5)&amp;lt;/code&amp;gt; с, шаг &amp;lt;code&amp;gt;STEP = 0.5&amp;lt;/code&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Источники.&#039;&#039;&#039; Всё, что ниже, сверено с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt; и отчётами &amp;lt;code&amp;gt;doc/reports/**&amp;lt;/code&amp;gt;. Ключевые файлы перечислены в Приложении Б.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;0-постановка-задачи-и-общая-архитектура&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 0. Постановка задачи и общая архитектура ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;01-что-мы-выравниваем&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.1. Что мы выравниваем ===&lt;br /&gt;
&lt;br /&gt;
Дан &#039;&#039;&#039;эпизод сериала&#039;&#039;&#039; (или отдельный фильм) в виде набора файлов разных озвучек (студий). Все они содержат &#039;&#039;&#039;один и тот же видеоряд&#039;&#039;&#039; (с точностью до монтажных отличий: разные заставки, вырезы, вставки, иногда другой fps), но &#039;&#039;&#039;разные аудиодорожки&#039;&#039;&#039;. Нужно для каждой пары &amp;lt;code&amp;gt;(референс, дубль)&amp;lt;/code&amp;gt; так преобразовать аудио дубля во времени, чтобы оно &#039;&#039;&#039;синхронно&#039;&#039;&#039; легло на таймлайн референса — и потом смуксировать все дорожки в один MKV.&lt;br /&gt;
&lt;br /&gt;
Главная трудность — это &#039;&#039;&#039;не «найти один сдвиг»&#039;&#039;&#039;. Сдвиг переменный по всей длине:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Источник рассинхрона&lt;br /&gt;
! Как выглядит на карте &amp;lt;code&amp;gt;(t_реф → t_дубль)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Постоянный сдвиг (priming, encoder delay)&lt;br /&gt;
| вертикальный параллельный перенос&lt;br /&gt;
|-&lt;br /&gt;
| Дрейф скорости (разный fps, PAL/NTSC)&lt;br /&gt;
| наклон кривой ≠ 1&lt;br /&gt;
|-&lt;br /&gt;
| Монтажный &#039;&#039;&#039;вырез&#039;&#039;&#039; в дубле (короче рефа)&lt;br /&gt;
| ступень вверх (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Монтажная &#039;&#039;&#039;вставка&#039;&#039;&#039; в дубле (длиннее рефа)&lt;br /&gt;
| ступень вниз (разрыв)&lt;br /&gt;
|-&lt;br /&gt;
| Заставка/реклама в начале&lt;br /&gt;
| свободный левый конец&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И всё это — на контенте, где прямое применение обоих слоёв теряет чувствительность: видеоряд может идти «на двойках» (соседние кадры идентичны — анимация на 2 кадра, статичные планы), музыка повторяется (корреляция фиксируется на такт), у дубля чужой голос поверх общей музыки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 0.1 — «Проблема в одной картинке».&#039;&#039;&#039; Слева: две киноленты (реф и дубль) друг под другом, на дубле — вырезанный кусок (короче) и вставленная заставка в начале (длиннее), стрелки соответствия кадров между лентами расходятся (не параллельны). Справа: те же соответствия как точки в осях &amp;lt;code&amp;gt;t_реф&amp;lt;/code&amp;gt;(X) / &amp;lt;code&amp;gt;t_дубль&amp;lt;/code&amp;gt;(Y) — складываются в ломаную линию с наклоном и двумя вертикальными ступенями. Подпись: «Выравнивание = восстановить эту ломаную».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;02-двухслойная-архитектура-зрение-строит-карту-звук-доводит&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.2. Двухслойная архитектура: зрение строит карту, звук доводит ===&lt;br /&gt;
&lt;br /&gt;
Фундаментальное решение проекта (отчёт &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;): &#039;&#039;&#039;видеоряд — общий инвариант&#039;&#039;&#039; всех озвучек, поэтому грубую и структурную карту строим по видео, а аудио используем лишь как &#039;&#039;&#039;тонкую доводку остатка&#039;&#039;&#039;, который видео физически не видит (аудио источника бывает смещено относительно своего же видео).&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    subgraph IN[Вход]&lt;br /&gt;
        REF[Референс mp4/mkv]&lt;br /&gt;
        DUB[Дубль mp4/mkv]&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph VIS[&amp;quot;СЛОЙ 1 · ЗРЕНИЕ (видео-карта)&amp;quot;]&lt;br /&gt;
        SRM[SRM-отпечатки кадров&amp;lt;br/&amp;gt;features.py]&lt;br /&gt;
        CO[Грубый проход · LIS&amp;lt;br/&amp;gt;kernel/coarse.py]&lt;br /&gt;
        DD[Drop-DTW в полосе&amp;lt;br/&amp;gt;kernel/dropdtw + band_align]&lt;br /&gt;
        LV[Разбор правок&amp;lt;br/&amp;gt;level_decide / creep_drop]&lt;br /&gt;
        PW[Укладка ломаной + детект резов&amp;lt;br/&amp;gt;vision_detect.py]&lt;br /&gt;
        SRM --&amp;gt; CO --&amp;gt; DD --&amp;gt; LV --&amp;gt; PW&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph RES[Ресэмпл аудио дубля]&lt;br /&gt;
        MAP[Карта t_реф → t_дубль]&lt;br /&gt;
        SIL1[Тишина в монтажных резах]&lt;br /&gt;
        MAP --&amp;gt; SIL1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    subgraph AUD[&amp;quot;СЛОЙ 2 · ЗВУК (доводка)&amp;quot;]&lt;br /&gt;
        OFF0[Грубая off0 · band ±2.5с]&lt;br /&gt;
        FOL[band ±0.7с следит за off0]&lt;br /&gt;
        DET[Детект ступеней · detect.py]&lt;br /&gt;
        DRIFT[Кривая дрейфа + потолок скорости]&lt;br /&gt;
        WARP[Варп по кускам + тишина]&lt;br /&gt;
        OFF0 --&amp;gt; FOL --&amp;gt; DET --&amp;gt; DRIFT --&amp;gt; WARP&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    FILL[Заполнение тишины синхронным рефом&amp;lt;br/&amp;gt;fill_silence]&lt;br /&gt;
    PLOTS[Единый график зрение+звук&amp;lt;br/&amp;gt;plots_unified.py]&lt;br /&gt;
    OUT[Выровненный WAV → mux]&lt;br /&gt;
&lt;br /&gt;
    REF --&amp;gt; SRM&lt;br /&gt;
    DUB --&amp;gt; SRM&lt;br /&gt;
    PW --&amp;gt; MAP&lt;br /&gt;
    SIL1 --&amp;gt; OFF0&lt;br /&gt;
    WARP --&amp;gt; FILL --&amp;gt; PLOTS --&amp;gt; OUT&lt;br /&gt;
}}&lt;br /&gt;
Почему именно так, а не «всё по аудио» или «всё по видео»:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Только аудио&#039;&#039;&#039; упирается в потолок: 5 разных аудио-методов (PHAT, MFCC, 2D-спектр) дают &#039;&#039;одни и те же промахи&#039;&#039; на повторяющемся контенте — это не алгоритмическая, а &#039;&#039;информационная&#039;&#039; нехватка в одном аудиоканале (&amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §2&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Только видео&#039;&#039;&#039; не видит сдвиг аудио относительно видео внутри самого источника (AV-sync delta, студийный сдвиг M&amp;amp;amp;E) — его видит лишь аудио.&lt;br /&gt;
* Поэтому: &#039;&#039;&#039;видео даёт структуру&#039;&#039;&#039; (где вырез, где вставка, какой дрейф), &#039;&#039;&#039;аудио доводит&#039;&#039;&#039; субкадровый остаток там, где видео слепо по физике (двойки) или где аудио смещено относительно картинки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;03-сквозные-величины-и-обозначения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 0.3. Сквозные величины и обозначения ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Обозначение&lt;br /&gt;
! Смысл&lt;br /&gt;
! Значение / где&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| мс на кадр&lt;br /&gt;
| 41.708 (≈23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| сетка анализа звука&lt;br /&gt;
| &amp;lt;code&amp;gt;arange(2.5, 1400, 0.5)&amp;lt;/code&amp;gt; с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| шаг сетки&lt;br /&gt;
| 0.5 с&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;o[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| измеренный сдвиг в точке t&lt;br /&gt;
| кадры рефа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;w[t]&amp;lt;/code&amp;gt;&lt;br /&gt;
| уверенность измерения&lt;br /&gt;
| норм. по медиане&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pred[j]&amp;lt;/code&amp;gt;&lt;br /&gt;
| для кадра дубля j — кадр рефа (или −1)&lt;br /&gt;
| целое&lt;br /&gt;
|-&lt;br /&gt;
| cos&lt;br /&gt;
| мера схожести двух кадров&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\in[-1,1]&amp;lt;/math&amp;gt;, на практике &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0,1]&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Дальше — два алгоритма по слоям.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-i-зрение--построитель-видео-карты&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть I. Зрение — построитель видео-карты =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kernel/band_align.py&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;, разбор правок в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;. Это &#039;&#039;&#039;единственный&#039;&#039;&#039; построитель видео-карты в conform (прежний «скат» &amp;lt;code&amp;gt;_build_map_linear&amp;lt;/code&amp;gt; удалён 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;1-геометрия-задачи&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;1-геометрия-задачи-карта-как-монотонная-кусочно-линейная-кривая&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 1. Геометрия задачи: карта как монотонная кусочно-линейная кривая ==&lt;br /&gt;
&lt;br /&gt;
Всё зрение сводится к одной геометрической цели — построить функцию&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tau:\; t_{\text{реф}} \;\longmapsto\; t_{\text{дубль}},&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
которая каждому моменту таймлайна референса сопоставляет момент в дубле, откуда брать звук. У этой функции жёсткая физическая структура:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;кусочно-линейная&#039;&#039;&#039; — внутри монтажного блока скорость постоянна (дрейф = наклон);&lt;br /&gt;
* &#039;&#039;&#039;монотонно неубывающая&#039;&#039;&#039; — время не идёт вспять (контент не переставляется);&lt;br /&gt;
* &#039;&#039;&#039;с вертикальными разрывами на монтажных стыках&#039;&#039;&#039; — вырез/вставка = ступень.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; t_дубль&lt;br /&gt;
   ▲&lt;br /&gt;
   │                                        ╱ (наклон ≈ fps_реф/fps_дубль)&lt;br /&gt;
   │                                   ╱╱╱&lt;br /&gt;
   │            вставка ↓         ╱╱╱&lt;br /&gt;
   │                        ┌────╱           ← разрыв вниз: в дубле лишний кусок&lt;br /&gt;
   │                        ┊&lt;br /&gt;
   │                   ╱╱╱╱╱┘&lt;br /&gt;
   │              ╱╱╱╱&lt;br /&gt;
   │   вырез ↑   ╱&lt;br /&gt;
   │         ┌──┘                            ← разрыв вверх: в дубле кусок вырезан&lt;br /&gt;
   │      ╱╱╱┊&lt;br /&gt;
   │  ╱╱╱╱   ┊&lt;br /&gt;
   └──┴──────┴──────────────────────────────►  t_реф&amp;lt;/pre&amp;gt;&lt;br /&gt;
Задача зрения — &#039;&#039;&#039;восстановить эту ломаную из зашумлённых измерений соответствия кадров&#039;&#039;&#039;. Шум двух видов: (а) ложные соответствия (двойки, лого, тёмные сцены), (б) пропуски (вырезы/вставки рвут непрерывность). Поэтому метод строится в три эшелона надёжности: грубая монотонная цепочка (§3) → точное выравнивание с пропусками (§4) → робастная ломаная поверх (§6).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;2-srm-отпечаток-кадра&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;2-srm-отпечаток-кадра&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 2. SRM-отпечаток кадра ==&lt;br /&gt;
&lt;br /&gt;
Чтобы сравнивать кадры рефа и дубля, каждый кадр сворачивается в вектор-отпечаток так, чтобы &#039;&#039;&#039;схожесть контента&#039;&#039;&#039; была устойчива к различиям источника (BD vs Web, разный кодек, яркость, логотип студии). Наивный путь — сравнивать пиксели яркости — ломается на первом же логотипе и перепаде гаммы. Решение — &#039;&#039;&#039;SRM (Spatial Rich Model): отпечаток не яркости, а остаточного высокочастотного сигнала&#039;&#039;&#039; (текстуры/краёв).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: что такое «отпечаток кадра» и SRM.&#039;&#039;&#039; Отпечаток (дескриптор) — это короткий числовой слепок картинки: вектор чисел, по которому два кадра можно сравнить, не сличая их попиксельно. Хороший отпечаток одинаков у одного и того же кадра в разном качестве и не сбивается от логотипа или яркости. &#039;&#039;&#039;SRM (Spatial Rich Model)&#039;&#039;&#039; — приём из цифровой криминалистики изображений: вместо самой картинки берут её &#039;&#039;высокочастотный остаток&#039;&#039; — то, что остаётся после вычитания плавных перепадов яркости (контуры, мелкая текстура). Именно остаток устойчив к перекодированию и смене источника.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;21-конвейер-построения-featurespybuild_srm&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.1. Конвейер построения (&amp;lt;code&amp;gt;features.py::build_srm&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    A[Кадр ffmpeg&amp;lt;br/&amp;gt;-vsync 0] --&amp;gt; B[Серый 128×72]&lt;br /&gt;
    B --&amp;gt; C1[Свёртка KB&amp;lt;br/&amp;gt;лапласиан]&lt;br /&gt;
    B --&amp;gt; C2[Свёртка D1&amp;lt;br/&amp;gt;градиент x]&lt;br /&gt;
    C1 --&amp;gt; D1[clip ±3]&lt;br /&gt;
    C2 --&amp;gt; D2[clip ±3]&lt;br /&gt;
    D1 --&amp;gt; E1[L2-норма канала]&lt;br /&gt;
    D2 --&amp;gt; E2[L2-норма канала]&lt;br /&gt;
    E1 --&amp;gt; F[concat × 1/√2&amp;lt;br/&amp;gt;float16, dim=18432]&lt;br /&gt;
    E2 --&amp;gt; F&lt;br /&gt;
}}&lt;br /&gt;
Пошагово. Кадр приводится к серому &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;g&amp;lt;/math&amp;gt; размера &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;128\times72&amp;lt;/math&amp;gt; (&amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: свёртка ядром.&#039;&#039;&#039; Свёртка — это проход маленькой матрицей-окном по всем пикселям: в каждой точке берётся взвешенная сумма соседних пикселей. Подбором весов окно настраивают на нужный признак — перепад яркости, край, текстуру. Сумма весов, равная нулю, означает, что окно «не замечает» ровной заливки и реагирует только на изменения.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Здесь два фиксированных ядра свёртки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
K_B=\begin{pmatrix}-1&amp;amp;2&amp;amp;-1\\ 2&amp;amp;-4&amp;amp;2\\ -1&amp;amp;2&amp;amp;-1\end{pmatrix},&lt;br /&gt;
\qquad&lt;br /&gt;
D_1=\begin{pmatrix}0&amp;amp;0&amp;amp;0\\ 0&amp;amp;-1&amp;amp;1\\ 0&amp;amp;0&amp;amp;0\end{pmatrix}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; — дискретный оператор второго порядка (лапласиан-подобный, сумма коэффициентов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0&amp;lt;/math&amp;gt;). Он &#039;&#039;&#039;зануляет постоянную и линейную составляющую&#039;&#039;&#039; яркости и реагирует на &#039;&#039;кривизну&#039;&#039; — текстуру, контуры, мелкие детали. Именно эта высокочастотная «подпись» одинакова у одного и того же кадра в разных рипах.&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; — первая горизонтальная разность (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\partial/\partial x&amp;lt;/math&amp;gt;), ловит вертикальные края и направление градиента.&lt;br /&gt;
&lt;br /&gt;
Свёртка идёт с краевым режимом &amp;lt;code&amp;gt;reflect&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_K = (g * K_B)&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r_D = (g * D_1)&amp;lt;/math&amp;gt;. Далее — обрезка выбросов и нормировка каждого канала по отдельности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\tilde r_K=\frac{\operatorname{clip}(r_K,-3,3)}{\lVert\operatorname{clip}(r_K,-3,3)\rVert_2+\varepsilon},&lt;br /&gt;
\qquad&lt;br /&gt;
\tilde r_D=\frac{\operatorname{clip}(r_D,-3,3)}{\lVert\operatorname{clip}(r_D,-3,3)\rVert_2+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обрезка &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm3&amp;lt;/math&amp;gt; давит редкие яркие выбросы (блики, субтитры), L2-нормировка убирает зависимость от общего контраста кадра. Итоговый вектор кадра — конкатенация обоих каналов, масштабированная на &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v=\tfrac{1}{\sqrt2}\,[\,\tilde r_K \;\Vert\; \tilde r_D\,]\in\mathbb{R}^{18432},\qquad&lt;br /&gt;
18432 = 2\cdot128\cdot72 .&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;22-почему-cos--v_1cdot-v_2-напрямую&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 2.2. Почему &amp;lt;code&amp;gt;cos = v_1\cdot v_2&amp;lt;/code&amp;gt; напрямую ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: косинусная близость.&#039;&#039;&#039; Если каждый кадр представить стрелкой (вектором) в многомерном пространстве, то мерой их похожести служит угол между стрелками: сонаправленные (один контент) дают косинус ≈ 1, перпендикулярные (разный контент) ≈ 0. Косинус зависит только от направления, не от длины стрелок, — поэтому он устойчив к общей яркости и контрасту.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Множитель &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;1/\sqrt2&amp;lt;/math&amp;gt; выбран так, что &#039;&#039;&#039;готовый вектор уже нормирован&#039;&#039;&#039;: поскольку каждый канал имеет единичную норму,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\lVert v\rVert_2^2=\tfrac12\big(\lVert\tilde r_K\rVert_2^2+\lVert\tilde r_D\rVert_2^2\big)=\tfrac12(1+1)=1.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поэтому скалярное произведение двух отпечатков &#039;&#039;&#039;сразу равно косинусной близости&#039;&#039;&#039;, и она же — среднее канальных косинусов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
v_1\cdot v_2=\tfrac12\big(\cos_{K_B}+\cos_{D_1}\big)=\cos(v_1,v_2)\in[-1,1].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это ключ к скорости всего зрения: матрица всех попарных схожестей — одно матричное умножение &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G = C_s C_r^{\top}&amp;lt;/math&amp;gt; (косинусы без отдельной нормировки), а стоимость для Drop-DTW — просто &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C = 1 - \cos&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Инженерные детали, ломающие бит-в-бит совпадение, если их нарушить&#039;&#039;&#039; (&amp;lt;code&amp;gt;features.py&amp;lt;/code&amp;gt;): ffmpeg &amp;lt;code&amp;gt;-vsync 0&amp;lt;/code&amp;gt; (passthrough — индекс кадра = позиция во времени), &amp;lt;code&amp;gt;float16&amp;lt;/code&amp;gt;, порядок &amp;lt;code&amp;gt;clip → norm → concat&amp;lt;/code&amp;gt;. Декод потоковый, при &amp;lt;code&amp;gt;low_mem&amp;lt;/code&amp;gt; фичи пишутся в memmap, значения идентичны in-RAM пути.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 2.1 — «Что видит SRM».&#039;&#039;&#039; Три колонки: (1) исходный кадр видео с логотипом студии в углу; (2) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K_B&amp;lt;/math&amp;gt; (контуры/текстура, лого почти не выделяется — оно гладкое); (3) карта отклика &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_1&amp;lt;/math&amp;gt; (вертикальные края). Подпись: «Отпечаток строится по высокочастотному остатку → устойчив к яркости и логотипу».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;3-грубый-проход&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;3-грубый-проход-матрица-косинусов-надёжные-якоря-lis&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 3. Грубый проход: матрица косинусов, надёжные якоря, LIS ==&lt;br /&gt;
&lt;br /&gt;
Цель первого эшелона — получить &#039;&#039;&#039;грубую, но монотонную&#039;&#039;&#039; оценку сдвига &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;j&amp;lt;/math&amp;gt;, чтобы потом узкая полоса Drop-DTW искала точное соответствие не во всём рефе, а в коридоре вокруг этой линии. Файл &amp;lt;code&amp;gt;kernel/coarse.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;31-надёжные-якоря-_anchors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.1. Надёжные якоря (&amp;lt;code&amp;gt;_anchors&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кадры прореживаются с шагом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;K=8&amp;lt;/math&amp;gt; (грубому проходу субкадровая точность не нужна). Строится матрица косинусов всех прореженных synth × ref:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
G = C_s\,C_r^{\top},\qquad G_{m,r}=\cos\big(v^{\text{syn}}_m, v^{\text{ref}}_r\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;m&amp;lt;/math&amp;gt; берётся лучший партнёр и его близость &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{best}_m=\arg\max_r G_{m,r}&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{bestv}_m=\max_r G_{m,r}&amp;lt;/math&amp;gt;. Затем окрестность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm W&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;W=80&amp;lt;/math&amp;gt; прореженных кадров) этого пика &#039;&#039;&#039;зануляется&#039;&#039;&#039;, и берётся второй максимум &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{second}_m&amp;lt;/math&amp;gt;. Кадр становится &#039;&#039;&#039;надёжным якорем&#039;&#039;&#039; только если выполнены оба условия:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\underbrace{\text{bestv}_m &amp;gt; V_{HI}}_{\text{пик высокий}}\quad\wedge\quad&lt;br /&gt;
\underbrace{\text{bestv}_m-\text{second}_m &amp;gt; C_{MIN}}_{\text{пик одинок (различим)}},\qquad&lt;br /&gt;
V_{HI}=0.60,\; C_{MIN}=0.12.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первое условие отсекает мусорные сцены (тьма, переход), второе — &#039;&#039;&#039;двойки и повторы&#039;&#039;&#039;: если в рефе есть второй почти такой же кадр, отрыв мал и кадр НЕ берётся в якоря. Это прямой ответ на главную проблему повторяющегося контента.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;32-монотонная-цепочка-через-lis-lis_nd-coarse_robust&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.2. Монотонная цепочка через LIS (&amp;lt;code&amp;gt;lis_nd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;coarse_robust&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: зачем «длиннейшая неубывающая подпоследовательность» (LIS).&#039;&#039;&#039; Среди якорей есть верные и единичные ложные. Верные обязаны идти «по возрастанию»: чем позже кадр в дубле, тем позже его партнёр в рефе (время не отматывается назад). LIS — классический алгоритм, который из разбросанных точек вытаскивает самую длинную цепочку, идущую только вверх; всё, что в неё не уложилось, почти наверняка ложные пары. Так костяк соответствия очищается без порогов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Надёжные якоря ещё могут содержать одиночные ложные пары. Истинное соответствие &#039;&#039;&#039;монотонно&#039;&#039;&#039; по рефу, поэтому из набора якорей &amp;lt;code&amp;gt;(synth_кадр → ref_кадр)&amp;lt;/code&amp;gt; извлекается &#039;&#039;&#039;длиннейшая неубывающая по ref подпоследовательность&#039;&#039;&#039; (Longest Non-Decreasing Subsequence, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(n\log n)&amp;lt;/math&amp;gt; через хвостовые индексы + бинарный поиск). Всё, что не легло в монотонную цепочку, отбрасывается как шум. По выжившим якорям линейной интерполяцией строится &amp;lt;code&amp;gt;off(j)&amp;lt;/code&amp;gt; для всех кадров:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}(j)=\operatorname{interp}\big(j;\ \{a_k\},\ \{r_k-a_k\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; ref                     LIS оставляет монотонный костяк, шум выпадает&lt;br /&gt;
  ▲        ✗(ложный)&lt;br /&gt;
  │   ●───●        ✗&lt;br /&gt;
  │  ╱       ●───●───●&lt;br /&gt;
  │ ●                   ●───●     ← возрастающая цепочка = реальное соответствие&lt;br /&gt;
  │╱            ✗&lt;br /&gt;
  └───────────────────────────►  synth (кадр дубля)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;33-оконный-вариант-для-длинных-файлов-coarse_windowed&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 3.3. Оконный вариант для длинных файлов (&amp;lt;code&amp;gt;coarse_windowed&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Полная матрица &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;G&amp;lt;/math&amp;gt; имеет размер &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; — для серии это гигабайты. Оконный проход режет дубль на окна по &amp;lt;code&amp;gt;CWIN=8000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;COV=2000&amp;lt;/code&amp;gt;; &#039;&#039;&#039;seed-offset перетекает из окна в окно&#039;&#039;&#039;, и в каждом окне ищется ref только в полосе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;CSR=4000&amp;lt;/code&amp;gt; вокруг seed. Память и время ограничены окном (не растут с длиной файла), а результат &#039;&#039;&#039;бит-в-бит совпал&#039;&#039;&#039; с полным проходом на 26 кейсах.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;4-drop-dtw-выравнивание-с-пропусками&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;4-drop-dtw-выравнивание-с-пропусками&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 4. Drop-DTW: выравнивание с пропусками ==&lt;br /&gt;
&lt;br /&gt;
Второй эшелон — точное соответствие кадров. Обычный DTW (Dynamic Time Warping) &#039;&#039;&#039;обязан&#039;&#039;&#039; сопоставить каждый кадр — он не умеет сказать «этого куска в рефе нет, это вставка». А у нас именно вырезы и вставки. Решение — &#039;&#039;&#039;Drop-DTW&#039;&#039;&#039;: DTW, которому разрешено &#039;&#039;выбрасывать&#039;&#039; кадры с обеих сторон за фиксированный штраф. Это ровно как &amp;lt;code&amp;gt;diff&amp;lt;/code&amp;gt; для текста или выравнивание последовательностей ДНК: ищем общий костяк, непарные куски помечаем как «вставка»/«удаление». Файл &amp;lt;code&amp;gt;kernel/dropdtw.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;41-стоимость-и-базовая-рекуррента-drop_dtw&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.1. Стоимость и базовая рекуррента (&amp;lt;code&amp;gt;drop_dtw&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Стоимость сопоставить кадр рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt; с кадром дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
C_{i,k}=1-\cos\!\big(v^{\text{ref}}_i,v^{\text{syn}}_k\big)\in[0,2].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами.&#039;&#039;&#039; Задача — перебрать все способы «растянуть / сжать / пропустить» кадры так, чтобы суммарная непохожесть сопоставленных кадров была минимальной, и сделать это не наивным перебором (астрономически долго), а накоплением лучшего ответа клетка за клеткой — это и есть динамическое программирование. Ниже — правило, по которому заполняется таблица лучших частичных ответов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Динамика заполняет таблицу &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_{i,k}&amp;lt;/math&amp;gt; — минимальную накопленную стоимость пути, пришедшего в клетку &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(i,k)&amp;lt;/math&amp;gt;. На каждом шаге доступны &#039;&#039;&#039;пять&#039;&#039;&#039; ходов (диагональ, два варпа, два выброса); старт — отдельная инициализация (свободный левый конец):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_{i,k}=\min\begin{cases}&lt;br /&gt;
D_{i-1,k-1}+C_{i,k} &amp;amp; \text{диагональ (1:1 совпадение)}\\&lt;br /&gt;
D_{i-1,k}+C_{i,k} &amp;amp; \text{вертикальный варп (ref тянется)}\\&lt;br /&gt;
D_{i,k-1}+C_{i,k} &amp;amp; \text{горизонтальный варп (дубль тянется)}\\&lt;br /&gt;
D_{i,k-1}+\text{DROP} &amp;amp; \text{выброс кадра дубля (вставка)}\\&lt;br /&gt;
D_{i-1,k}+\text{DROP} &amp;amp; \text{выброс кадра рефа (вырез)}\\&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с штрафом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{DROP}=0.20&amp;lt;/math&amp;gt;. Backpointer &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;B_{i,k}&amp;lt;/math&amp;gt; хранит, какой ход выбран, для восстановления пути. &#039;&#039;&#039;Свободные концы&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;свободный старт&#039;&#039;&#039; — &amp;lt;code&amp;gt;D[i,0] = C[i,0]&amp;lt;/code&amp;gt; для всех &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;i&amp;lt;/math&amp;gt;: путь может начаться с любой строки рефа (дубль не обязан стартовать с самого начала рефа);&lt;br /&gt;
* &#039;&#039;&#039;свободный конец&#039;&#039;&#039; — обратный ход начинается с &amp;lt;code&amp;gt;argmin&amp;lt;/code&amp;gt; последнего столбца: путь может закончиться на любой строке рефа.&lt;br /&gt;
&lt;br /&gt;
Восстановление пути (&amp;lt;code&amp;gt;backtrack&amp;lt;/code&amp;gt;) даёт три выхода: &amp;lt;code&amp;gt;pred[k]&amp;lt;/code&amp;gt; (для кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — его кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;drop_syn&amp;lt;/code&amp;gt; (маска выброшенных кадров дубля = вставки), &amp;lt;code&amp;gt;drop_ref&amp;lt;/code&amp;gt; (список выброшенных кадров рефа = вырезы).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 4.1 — «Путь по матрице Drop-DTW».&#039;&#039;&#039; Тепловая карта стоимости &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C_{i,k}&amp;lt;/math&amp;gt; (тёмное = похоже), ось X = кадры дубля, ось Y = кадры рефа. Поверх — оптимальный путь: диагональные участки (совпадение), один горизонтальный «провал» (выброс кадров дубля = вставка) и один вертикальный «прыжок» (выброс кадров рефа = вырез). Сбоку — легенда шести ходов. Подпись: «Зелёная линия — карта; разрывы = монтажные стыки».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;42-аффинный-штраф-острый-шов-вместо-размазни-drop_dtw_affine&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.2. Аффинный штраф: острый шов вместо размазни (&amp;lt;code&amp;gt;drop_dtw_affine&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Если вырез длинный, выбрасывать его «по одному кадру» дорого и нестабильно — путь норовит размазать выброс или убежать на логотипе. Аффинная (two-state) динамика берёт &#039;&#039;&#039;открытие&#039;&#039;&#039; прогона выброса дороже, чем его &#039;&#039;&#039;продолжение&#039;&#039;&#039; (как gap-open / gap-extend в биоинформатике):&lt;br /&gt;
&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; — «матч/варп» (платит &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;C&amp;lt;/math&amp;gt;);&lt;br /&gt;
* состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; — «внутри выброса рефа»: открыть из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;OPEN=0.20&amp;lt;/code&amp;gt;, продлить из &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; за &amp;lt;code&amp;gt;EXT=0.02&amp;lt;/code&amp;gt;;&lt;br /&gt;
* выброс кадра дубля (вставка) — плоская цена &amp;lt;code&amp;gt;DSYN=0.30&amp;lt;/code&amp;gt; внутри &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;M&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
D_r[i,k]=\min\big(M[i-1,k]+\text{OPEN},\; D_r[i-1,k]+\text{EXT}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дёшево продлевать (0.02) → длинный вырез схлопывается в один чистый шов, а не в рваную лесенку. На ровных логотипных участках состояние &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;D_r&amp;lt;/math&amp;gt; не открывается → путь не уходит в сторону.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;43-guard-нельзя-выбросить-кадр-с-хорошим-матчем-drop_dtw_affine_guard&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.3. Guard: нельзя выбросить кадр с хорошим матчем (&amp;lt;code&amp;gt;drop_dtw_affine_guard&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Без защиты прогон выброса склонен захватывать хвосты совпадающих кадров (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos=1&amp;lt;/math&amp;gt;), раздувая вставку. Guard заранее считает для каждого кадра дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; лучший достижимый матч по столбцу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{colmin}_k=\min_i C_{i,k}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выброс этого кадра (ход drop-syn) разрешён &#039;&#039;&#039;только если&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{colmin}_k&amp;gt;\text{MATCH\_THR}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=0.30&amp;lt;/math&amp;gt;); иначе цена выброса &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;=\infty&amp;lt;/math&amp;gt; — кадр с хорошим матчем защищён от выбрасывания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;44-free-start-заставка-начала-выпадает-сама&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.4. Free-start: заставка начала выпадает сама ===&lt;br /&gt;
&lt;br /&gt;
Реклама/заставка в начале дубля не имеет пары в рефе. Free-start даёт &#039;&#039;&#039;свободный левый конец по дублю&#039;&#039;&#039;: путь может стартовать с любой клетки, выбросив ведущие кадры &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[0..k-1]&amp;lt;/math&amp;gt; по цене вставки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k\cdot\text{DSYN}&amp;lt;/math&amp;gt;. Штраф растёт с &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;k&amp;lt;/math&amp;gt; — поэтому короткая заставка выпадает, но путь не «улетает» далеко.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{старт в } (i,k):\quad \text{cost}=C_{i,k}+k\cdot\text{DSYN}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;45-следящая-полоса-band_align&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 4.5. Следящая полоса (&amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Drop-DTW на полной матрице &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s\times N_r&amp;lt;/math&amp;gt; невозможен по памяти. Поэтому он считается в &#039;&#039;&#039;узкой полосе&#039;&#039;&#039; вокруг грубой линии &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; (§3): дубль режется на куски по &amp;lt;code&amp;gt;CHUNK=4000&amp;lt;/code&amp;gt; кадров с перекрытием &amp;lt;code&amp;gt;OVERLAP=700&amp;lt;/code&amp;gt;; для каждого куска ref-окно берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[\min(\text{refk})-\text{MARG},\ \max(\text{refk})+\text{MARG}]&amp;lt;/math&amp;gt;, полуширина &amp;lt;code&amp;gt;MARG=120&amp;lt;/code&amp;gt; кадров (≈5 с — покрывает обычную правку). На стыках кусков для каждого кадра берётся ответ из того куска, где кадр &#039;&#039;&#039;дальше от края&#039;&#039;&#039; (надёжнее):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; кусок A:  ├──────────────────┤&lt;br /&gt;
 кусок B:              ├──────────────────┤&lt;br /&gt;
                       └ overlap ┘&lt;br /&gt;
 кадр в overlap → берём из того куска, где он ГЛУБЖЕ (центр надёжнее краёв)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    OFF[Грубая off из coarse] --&amp;gt; WIN[Окно ref ±MARG=120&amp;lt;br/&amp;gt;вокруг линии]&lt;br /&gt;
    CHUNK[Кусок дубля 4000&amp;lt;br/&amp;gt;overlap 700] --&amp;gt; WIN&lt;br /&gt;
    WIN --&amp;gt; C[&amp;quot;C = 1 − cos&amp;quot;]&lt;br /&gt;
    C --&amp;gt; DD[drop_dtw_affine_guard&amp;lt;br/&amp;gt;OPEN .20 / EXT .02 / DSYN .30&amp;lt;br/&amp;gt;MATCH_THR .30 / free_start]&lt;br /&gt;
    DD --&amp;gt; BT[backtrack → pred куска]&lt;br /&gt;
    BT --&amp;gt; MERGE[Склейка по «глубине» кадра]&lt;br /&gt;
}}&lt;br /&gt;
Итог слоя §4 — массив &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; длины &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;N_s&amp;lt;/math&amp;gt;: для каждого кадра дубля либо кадр рефа, либо &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; (выброшен).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;5-разбор-правок&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;5-разбор-правок-вырезы-вставки-налипания&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 5. Разбор правок: вырезы, вставки, налипания ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; сырой: в нём вперемешку настоящие монтажные правки и артефакты (слепые зоны зрения, налипания). Их разбирает &#039;&#039;&#039;единое физическое правило&#039;&#039;&#039; в &amp;lt;code&amp;gt;align.py&amp;lt;/code&amp;gt;, а не набор порогов под контент.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;51-правка-реальна-только-при-стойком-сдвиге-уровня-offset-_level_decide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.1. Правка реальна только при стойком сдвиге УРОВНЯ offset (&amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Ключевая идея: считаем поканальный offset на сопоставленных кадрах &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{off}_{\text{asg}}=\text{pred}[\text{asg}]-\text{asg}&amp;lt;/math&amp;gt;. Для каждого выброшенного блока (drop-syn) сравниваем &#039;&#039;&#039;медиану уровня offset до и после&#039;&#039;&#039; блока (окно &amp;lt;code&amp;gt;LEVEL_WIN_S=4&amp;lt;/code&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\Delta=\big|\operatorname{median}(\text{off}_{\text{после}})-\operatorname{median}(\text{off}_{\text{до}})\big|.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta\le&amp;lt;/math&amp;gt; &amp;lt;code&amp;gt;LEVEL_EDIT_S&amp;lt;/code&amp;gt; (1 с) &#039;&#039;&#039;или&#039;&#039;&#039; блок короче &amp;lt;code&amp;gt;LEVEL_MIN_INS_S&amp;lt;/code&amp;gt; (2 с) → &#039;&#039;&#039;слепая зона / дрожь&#039;&#039;&#039; → восстановить интерполяцией (заодно перекрывает парный пробел рефа). Ложное восстановление безвредно по построению.&lt;br /&gt;
* иначе → &#039;&#039;&#039;реальная вставка&#039;&#039;&#039; → оставить выброшенной.&lt;br /&gt;
&lt;br /&gt;
Любой оставшийся пробел рефа при непрерывном дубле &amp;amp;gt; &amp;lt;code&amp;gt;CUT_MIN_S&amp;lt;/code&amp;gt; (0.3 с) — это &#039;&#039;&#039;реальный вырез&#039;&#039;&#039;. Доказано: пробел рефа при непрерывном дубле всегда сдвигает уровень ⇒ слепых вырезов не остаётся.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;52-фикс-пилы-вырез-только-при-устойчивом-сдвиге-а-не-при-выбросе-на-статике&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.2. Фикс «пилы»: вырез только при устойчивом сдвиге, а не при выбросе на статике ===&lt;br /&gt;
&lt;br /&gt;
На статике (длинный неподвижный план) зрение может дать &#039;&#039;временный&#039;&#039; выброс offset, который &#039;&#039;&#039;возвращается&#039;&#039;&#039; — это не вырез, а артефакт. Соседние пробелы склеиваются в кластер (&amp;lt;code&amp;gt;LEVEL_CUT_COALESCE_S=3&amp;lt;/code&amp;gt; с); если в пределах &amp;lt;code&amp;gt;LEVEL_CUT_RECOVER_S&amp;lt;/code&amp;gt; (8 с) уровень &#039;&#039;&#039;возвращается&#039;&#039;&#039; к доврезному — кластер восстанавливается (пила убрана), если держится — схлопывается в &#039;&#039;&#039;один&#039;&#039;&#039; интервал выреза (а не лесенку коротких тишин).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;53-детектор-налипания-_creep_drop-кейс-case04&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.3. Детектор «налипания» (&amp;lt;code&amp;gt;_creep_drop&amp;lt;/code&amp;gt;, кейс case/04) ===&lt;br /&gt;
&lt;br /&gt;
Особый дефект: вставка-повтор своего же контента с наложенным текстом присваивается Drop-DTW «ползучим ходом» вместо выброса. Сигнал кричащий: присвоенные кадры по содержимому &#039;&#039;&#039;чужие&#039;&#039;&#039; своим реф-партнёрам — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\approx0.00\text{–}0.02&amp;lt;/math&amp;gt; на много секунд подряд, тогда как честные матчи даже в тёмных сценах держат &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos\ge0.22&amp;lt;/math&amp;gt;. Механика: скользящая медиана &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt; присвоенных кадров (окно ~1 с); связные зоны ниже &amp;lt;code&amp;gt;CREEP_COS=0.15&amp;lt;/code&amp;gt; длиннее &amp;lt;code&amp;gt;CREEP_MIN_S=3&amp;lt;/code&amp;gt; с → &amp;lt;code&amp;gt;pred=-1&amp;lt;/code&amp;gt;. Дальнейшую судьбу решает &amp;lt;code&amp;gt;_level_decide&amp;lt;/code&amp;gt; (§5.1) — ложное срабатывание безвредно.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;54-возврат-краёв-_recover_edges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 5.4. Возврат краёв (&amp;lt;code&amp;gt;_recover_edges&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Free-start мог выкинуть &#039;&#039;&#039;совпадающее&#039;&#039;&#039; начало/конец (общий опенинг при разном вступлении BD↔WEB). Берём выброшенный кусок дубля против непокрытого куска рефа, гоняем &amp;lt;code&amp;gt;band_align&amp;lt;/code&amp;gt; на подзадаче и вписываем вернувшиеся матчи (где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;gt;&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;EDGE_COS_MIN=0.5&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;6-укладка-ломаной&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;6-укладка-ломаной-ow--детект-ступеней--кусочно-линейная-карта&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 6. Укладка ломаной: (o,w) → детект ступеней → кусочно-линейная карта ==&lt;br /&gt;
&lt;br /&gt;
Финал зрения (&amp;lt;code&amp;gt;vision_detect.py&amp;lt;/code&amp;gt;) — превратить дискретный &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в &#039;&#039;&#039;гладкую кусочно-линейную карту&#039;&#039;&#039; с явными разрывами на резах. Это перенос сильного аудио-аппарата (&amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt;) на выход зрения: зрение видит структуру сдвига точнее аудио, а прежний «скат» (&amp;lt;code&amp;gt;maximum.accumulate&amp;lt;/code&amp;gt;) её подавлял.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;61-из-pred-в-узлы-сетки-o-w-vision_ow&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.1. Из &amp;lt;code&amp;gt;pred&amp;lt;/code&amp;gt; в узлы сетки &amp;lt;code&amp;gt;(o, w)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;vision_ow&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для сопоставленных кадров считаем время в рефе и сдвиг в кадрах:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
t^{\text{ref}}_k=\frac{\text{pred}[k]}{\text{fps}_{\text{ref}}},\qquad&lt;br /&gt;
\text{sh}_k=\Big(\frac{k}{\text{fps}_{\text{dub}}}-t^{\text{ref}}_k\Big)\cdot\text{fps}_{\text{ref}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На каждом узле сетки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t\in T&amp;lt;/math&amp;gt; в окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt; (0.3 с) берём &#039;&#039;&#039;взвешенную медиану&#039;&#039;&#039; сдвига (вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;), и оцениваем уверенность как медиану &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\cos&amp;lt;/math&amp;gt;, умноженную на &#039;&#039;&#039;долю согласных&#039;&#039;&#039; (anchor-«agree») — кадров, чей сдвиг в пределах &amp;lt;code&amp;gt;tol_fr=2&amp;lt;/code&amp;gt; кадра от медианы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o[t]=\operatorname{wmedian}\big(\text{sh},\ \cos\big),\qquad&lt;br /&gt;
w[t]=\operatorname{median}(\cos)\cdot\underbrace{\tfrac{1}{n}\#\{|\text{sh}-o|\le2\}}_{\text{agree}}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Взвешенная медиана — робастная статистика без порогов: сортируем по значению, идём по кумулятивному весу до половины. Пустые узлы интерполируются (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w=0&amp;lt;/math&amp;gt;), веса нормируются на медиану положительных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;62-детект-ступеней-dp-сегментация-ломаными-detectwarp_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.2. Детект ступеней: DP-сегментация ломаными (&amp;lt;code&amp;gt;detect.warp_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Кривую &amp;lt;code&amp;gt;o(t)&amp;lt;/code&amp;gt; режем на сегменты, в каждом — &#039;&#039;&#039;одна взвешенная прямая&#039;&#039;&#039;; стыки сегментов = кандидаты в резы. Это DP по префиксным суммам.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: робастная прямая (IRLS).&#039;&#039;&#039; Обычная прямая по методу наименьших квадратов легко уводится одной выбросной точкой. IRLS (итеративно перевзвешенный МНК) лечит это: проводим прямую, смотрим, кто далеко отклонился, уменьшаем таким точкам вес и проводим заново. Через пару итераций прямая опирается на основную массу точек и игнорирует выбросы.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Взвешенная прямая (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, IRLS).&#039;&#039;&#039; Для точек &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t,y)&amp;lt;/math&amp;gt; с весами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; оценка наклона &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;a&amp;lt;/math&amp;gt; и сдвига &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;b&amp;lt;/math&amp;gt; — обычная взвешенная регрессия, но в &#039;&#039;&#039;2 итерации с даунвейтом выбросов&#039;&#039;&#039; (Iteratively Reweighted Least Squares):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\frac{\sum w\,(t-\bar t)(y-\bar y)}{\sum w\,(t-\bar t)^2},\quad b=\bar y-a\bar t,\quad&lt;br /&gt;
w\leftarrow w\cdot\exp\!\Big[-\big(\tfrac{r}{2.5\,s}\big)^2\Big],&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;r=y-(at+b)&amp;lt;/math&amp;gt; — остаток, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;s&amp;lt;/math&amp;gt; — взвешенный СКО. Второй проход гасит выбросы → прямая не ведётся за единичными ложными узлами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Сегментация (&amp;lt;code&amp;gt;_wsegment&amp;lt;/code&amp;gt;).&#039;&#039;&#039; Стоимость сегмента &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;[i,j)&amp;lt;/math&amp;gt; — взвешенная SSE наилучшей прямой (берётся за &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;O(1)&amp;lt;/math&amp;gt; из шести префиксных сумм &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sum w,\sum wt,\sum wy,\sum wt^2,&lt;br /&gt;
\sum wty,\sum wy^2&amp;lt;/math&amp;gt;). DP минимизирует суммарную ошибку плюс штраф &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt; за каждый сегмент (чем больше PEN, тем меньше резов), при минимальной длине сегмента &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{opt}[j]=\min_{i\le j-\text{msize}}\big(\text{opt}[i]+\text{SSE}(i,j)+\text{PEN}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Наклон каждого сегмента ограничен потолком &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt; (физика дрейфа ≤~2%). Рез ставится на стыке, если разрыв прямых &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\ge&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt; (3 кадра ≈ 120 мс).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;63-фильтр-резов-устойчивость-уровня-на-большом-окне-video_cut_filter&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.3. Фильтр резов: устойчивость уровня на большом окне (&amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
DP-сегментация склонна давать избыточные стыки; настоящий рез отличается тем, что уровень offset &#039;&#039;&#039;устойчиво&#039;&#039;&#039; разный слева и справа на &#039;&#039;&#039;большом&#039;&#039;&#039; окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt; (80 с), и считать его надо по &#039;&#039;&#039;надёжным&#039;&#039;&#039; узлам. Берём взвешенную медиану уровня слева/справа (робастна к «холмику» в окне — холмик у слепой зоны зрения возвращается, и медиана его игнорирует):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{рез реален}\iff \big|\operatorname{wmedian}(o_R,w^3)-\operatorname{wmedian}(o_L,w^3)\big|\ge\text{VC\_THR}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два важных нюанса:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;вес-гейт&#039;&#039;&#039; &amp;lt;code&amp;gt;VC_GATE=0.55&amp;lt;/code&amp;gt;: в окне берутся только узлы с весом выше квантиля 0.55 (&amp;lt;code&amp;gt;_gmask&amp;lt;/code&amp;gt;) — всплески у слепых зон отбрасываются;&lt;br /&gt;
* &#039;&#039;&#039;окно ограничено соседними резами&#039;&#039;&#039; (как &amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt; в аудио): на узком сегменте между двумя близкими резами окно &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm80&amp;lt;/math&amp;gt; с не должно перехлёстывать через соседний рез, иначе поймает чужой уровень и &#039;&#039;&#039;ложно подтвердит&#039;&#039;&#039; рез. Регрессия 0/36 дорожек.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;64-кусочно-линейная-укладка-piecewise_lines&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.4. Кусочно-линейная укладка (&amp;lt;code&amp;gt;piecewise_lines&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Между резами кладём &#039;&#039;&#039;одну робастную прямую&#039;&#039;&#039; (&amp;lt;code&amp;gt;wfit&amp;lt;/code&amp;gt;, наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;=0.45 к/с), изломы только на резах. Это усредняет весь кусок и потому нечувствительно к мелкой ряби &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;. Итог — кривая сдвига на сетке T.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;65-сборка-карты-build_map&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 6.5. Сборка карты (&amp;lt;code&amp;gt;build_map&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{shift}(t)=\operatorname{interp}(t;\ T,\ \text{curve}),\qquad&lt;br /&gt;
\tau(t)=t+\text{shift}(t)\cdot\frac{\text{FRAME}}{1000}\ \text{[с]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На резах в &amp;lt;code&amp;gt;shift&amp;lt;/code&amp;gt; ступень (разрыв) — её перекрывает &#039;&#039;&#039;тишина&#039;&#039;&#039; (вызывающий разносит контент на монтажном стыке). Между резами карта монотонна (наклон &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;). Выход: &amp;lt;code&amp;gt;(grid, tg_s, cuts, o, w, curve)&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;tg_s&amp;lt;/code&amp;gt; идёт в ресэмпл аудио, &amp;lt;code&amp;gt;o/w/curve&amp;lt;/code&amp;gt; — в графики, &amp;lt;code&amp;gt;cuts&amp;lt;/code&amp;gt; — в тишину.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    PRED[pred / asg / cos] --&amp;gt; OW[&amp;quot;vision_ow → (o, w) на сетке T&amp;quot;]&lt;br /&gt;
    OW --&amp;gt; WC[warp_curve: DP-сегментация ломаными]&lt;br /&gt;
    WC --&amp;gt; VCF[video_cut_filter:&amp;lt;br/&amp;gt;устойчивость уровня ±80с, вес-гейт,&amp;lt;br/&amp;gt;окно по соседним резам]&lt;br /&gt;
    OW --&amp;gt; PL[piecewise_lines:&amp;lt;br/&amp;gt;робастная прямая между резами]&lt;br /&gt;
    VCF --&amp;gt; PL&lt;br /&gt;
    PL --&amp;gt; MAP[&amp;quot;build_map → tg_s + cuts&amp;quot;]&lt;br /&gt;
    MAP --&amp;gt; SIL[Тишина в резах]&lt;br /&gt;
    MAP --&amp;gt; RES[Ресэмпл аудио дубля на сетку рефа]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части I.&#039;&#039;&#039; Зрение превращает два видеофайла в карту &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\tau(t)&amp;lt;/math&amp;gt; «время рефа → время дубля» — монотонную кусочно-линейную, с явными монтажными резами, устойчивую к двойкам, лого и слепым зонам. По этой карте аудио дубля ресэмплится на таймлайн рефа; в резах ставится тишина. Дальше — слой звука.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-ii-звук--доводка-bandmuq-поверх-зрения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть II. Звук — доводка band/muq поверх зрения =&lt;br /&gt;
&lt;br /&gt;
Файлы: &amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt; (оркестровка), &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt; (метрика), &amp;lt;code&amp;gt;anchor/detect.py&amp;lt;/code&amp;gt; (общий аппарат детекта, тот же, что у зрения), &amp;lt;code&amp;gt;anchor/assemble.py&amp;lt;/code&amp;gt;. Метод по умолчанию — &#039;&#039;&#039;band&#039;&#039;&#039; (DSP, 48 полос, без модели/лицензии); альтернатива &#039;&#039;&#039;muq&#039;&#039;&#039; (GPU-эмбеддинги, та же сборка).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;7-зачем-аудио-после-видео&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;7-зачем-аудио-после-видео&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 7. Зачем аудио после видео ==&lt;br /&gt;
&lt;br /&gt;
Зрение положило дубль по картинке. Но между &#039;&#039;&#039;видео и аудио внутри самого файла&#039;&#039;&#039; бывает сдвиг, которого на картинке не видно:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;AV-sync delta&#039;&#039;&#039; — разный AAC priming / encoder delay (в JoJo измерено стабильные +89 мс между файлами, &amp;lt;code&amp;gt;BREAKTHROUGH_VIDEO_SYNC.md §4.4&amp;lt;/code&amp;gt;);&lt;br /&gt;
* &#039;&#039;&#039;студийный сдвиг M&amp;amp;amp;E&#039;&#039;&#039; — студия подвинула фон/музыку в миксе относительно своего видео;&lt;br /&gt;
* &#039;&#039;&#039;двойки&#039;&#039;&#039; — видео физически не различает соседние одинаковые кадры (предел ±83 мс), аудио снимает это ограничение.&lt;br /&gt;
&lt;br /&gt;
Поэтому второй слой меряет &#039;&#039;&#039;остаточный&#039;&#039;&#039; сдвиг аудио дубля (уже лежащего по видео-карте) относительно аудио рефа и доводит его. Важно: аудио-слой работает &#039;&#039;&#039;после&#039;&#039;&#039; простановки тишины в монтажных резах — он должен видеть реф/тишину в вырезе (как боевой эталон), а не сырой несинхронный дубль, иначе на стыке зрение↔аудио появляется ложный краевой рез.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;8-принцип-зрячих-свидетелей&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;8-принцип-зрячих-свидетелей&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 8. Принцип зрячих свидетелей ==&lt;br /&gt;
&lt;br /&gt;
Отчёт &amp;lt;code&amp;gt;METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt; фиксирует намертво: &#039;&#039;&#039;наивная корреляция звука слепа&#039;&#039;&#039;, и это легко принять за «аудио измерить нельзя». Озвучка = чужой голос поверх той же музыки и эффектов (M&amp;amp;amp;E). Прямой NCC сырого сигнала ловит несовпадение голоса и возвращает шум. Лечится &#039;&#039;&#039;не другим инструментом, а изоляцией инварианта ПЕРЕД корреляцией&#039;&#039;&#039;. У каждого искажения дубляжа есть то, что оно не трогает:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Искажение дубляжа&lt;br /&gt;
! Что губит&lt;br /&gt;
! Инвариант (что не трогает)&lt;br /&gt;
! Как изолируем&lt;br /&gt;
|-&lt;br /&gt;
| Чужой голос диктора&lt;br /&gt;
| часть частотных полос&lt;br /&gt;
| чистые полосы M&amp;amp;amp;E&lt;br /&gt;
| &#039;&#039;&#039;48 лог-полос + взвеш. медиана&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Другой мастеринг/тембр&lt;br /&gt;
| тонкую структуру волны&lt;br /&gt;
| динамику громкости&lt;br /&gt;
| &#039;&#039;&#039;лог-огибающая RMS&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Повтор музыкального такта&lt;br /&gt;
| однозначность пика&lt;br /&gt;
| окна с одиночным пиком&lt;br /&gt;
| &#039;&#039;&#039;comb-veto&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Свидетели разной физики теряют чувствительность в &#039;&#039;&#039;разных&#039;&#039;&#039; местах → их сумма покрывает всю длину. Ниже подробно про основной свидетель боевого band — &#039;&#039;&#039;многополосную метрику&#039;&#039;&#039; (она же несёт оба первых инварианта: полосы против голоса, лог-энергия против мастеринга).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 8.1 — «Почему полосы видят сквозь голос».&#039;&#039;&#039; Спектрограмма окна озвучки: горизонтальными лентами выделены 48 лог-полос; полосы, занятые голосом диктора (≈300–3000 Гц), подсвечены красным («низкий контраст, малый вес»), полосы чистой музыки/эффектов — зелёным («высокий контраст, голосуют»). Снизу — та же сцена в рефе. Подпись: «Взвешенная медиана по полосам игнорирует испорченные голосом полосы — без знания, где голос».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;9-band-метрика-48-полос&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;9-band-метрика-48-полос&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 9. band-метрика: 48 полос ==&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/maps/band.py&amp;lt;/code&amp;gt;. Рецепт &amp;lt;code&amp;gt;NB48&amp;lt;/code&amp;gt;: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;sr=16000&amp;lt;/math&amp;gt;, &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;NB=48&amp;lt;/math&amp;gt; лог-полос в диапазоне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;50\text{–}14000&amp;lt;/math&amp;gt; Гц, окно &amp;lt;code&amp;gt;win=5&amp;lt;/code&amp;gt; с, STFT &amp;lt;code&amp;gt;nfft=2048&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hop=256&amp;lt;/code&amp;gt; (⇒ кадровый темп огибающей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{fps}=sr/hop=62.5&amp;lt;/math&amp;gt; Гц), агрегация &amp;lt;code&amp;gt;wmedian&amp;lt;/code&amp;gt;, качество &amp;lt;code&amp;gt;agree&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;91-полосовые-огибающие-_benv&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.1. Полосовые огибающие (&amp;lt;code&amp;gt;_benv&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: STFT и полосы.&#039;&#039;&#039; STFT (кратковременное преобразование Фурье) режет звук на короткие кусочки и для каждого показывает, сколько в нём энергии на каждой частоте, — получается спектрограмма «время × частота». Мы группируем частоты в 48 логарифмических полос (как деления на эквалайзере) и следим за громкостью каждой полосы во времени — это и есть «огибающая полосы». Лог-шкала частот ближе к слуху: низкие частоты дробятся мельче, высокие — крупнее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Для окна сигнала &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;x&amp;lt;/math&amp;gt; берём STFT и мощность &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;P=|Z|^2&amp;lt;/math&amp;gt;. Лог-частотная &#039;&#039;&#039;полосовая маска&#039;&#039;&#039; &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;BM\in\{0,1\}^{NB\times(nfft/2+1)}&amp;lt;/math&amp;gt; (границы — &amp;lt;code&amp;gt;logspace(50,14000,49)&amp;lt;/code&amp;gt;) суммирует мощность по полосам:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
E_{b,t}=\sum_f BM_{b,f}\,P_{f,t},\qquad&lt;br /&gt;
\tilde E_{b,t}=\log(1+E_{b,t}).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Каждая полоса z-нормируется по времени (убираем общий уровень и масштаб — это инвариант к мастерингу):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\hat E_{b,t}=\frac{\tilde E_{b,t}-\operatorname{mean}_t \tilde E_b}{\operatorname{std}_t \tilde E_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;log1p&amp;lt;/code&amp;gt; + z-score — это и есть «лог-огибающая громкости полосы»: динамика, общая у дубля и рефа, остаётся; тембр выкидывается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;92-по-полосный-лаг-через-кросс-корреляцию-_om_core&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.2. По-полосный лаг через кросс-корреляцию (&amp;lt;code&amp;gt;_om_core&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Для каждой полосы независимо считаем кросс-корреляцию огибающих рефа &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{r}_b&amp;lt;/math&amp;gt; и дубля &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat E^{d}_b&amp;lt;/math&amp;gt; по времени (через FFT, знак &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;E_d\cdot\overline{E_r}&amp;lt;/math&amp;gt; даёт «правее=+»):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{cc}_{b}[\ell]=\big(\hat E^{d}_b \star \hat E^{r}_b\big)[\ell],\qquad&lt;br /&gt;
\ell\in[-M_f,\,M_f],\ \ M_f=\text{maxlag}\cdot\text{fps}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выраженность пика полосы (насколько он торчит над фоном):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{prom}_b=\max_\ell \text{cc}_b[\ell]-\operatorname{median}_\ell \text{cc}_b[\ell].&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;93-агрегация-взвешенная-медиана-по-полосам-aggwmedian&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.3. Агрегация: взвешенная медиана по полосам (&amp;lt;code&amp;gt;agg=&amp;amp;quot;wmedian&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Вместо суммы корреляций (её подавляет голос) — &#039;&#039;&#039;по-полосный аргмакс-лаг&#039;&#039;&#039; и &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; этих лагов по 48 полосам, вес = &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{prom}_b^{\text{promp}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{promp}=1&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{bsh}_b=\arg\max_\ell \text{cc}_b[\ell],\qquad&lt;br /&gt;
\hat\ell=\operatorname{wmedian}\big(\{\text{bsh}_b\},\ \{\text{prom}_b\}\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Медиана робастна: даже если голос испортил треть полос и они показывают «не туда», медиана держится за большинство чистых. Финальный сдвиг уточняется &#039;&#039;&#039;параболической интерполяцией&#039;&#039;&#039; вокруг &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\hat\ell&amp;lt;/math&amp;gt; по агрегированной поверхности и переводится в кадры:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{off}=\frac{\hat\ell-\tfrac12\frac{y_{+}-y_{-}}{y_{+}-2y_0+y_{-}}}{\text{fps}}\cdot\frac{1000}{\text{FRAME}}\ \text{[кадры]}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;94-качество--согласие-полос-qualityagree&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 9.4. Качество = согласие полос (&amp;lt;code&amp;gt;quality=&amp;amp;quot;agree&amp;amp;quot;&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Уверенность узла — &#039;&#039;&#039;доля полос, чей собственный аргмакс-лаг согласен&#039;&#039;&#039; с итоговым сдвигом (в пределах &amp;lt;code&amp;gt;tol=2&amp;lt;/code&amp;gt; кадра), взвешенная по выраженности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
w=\frac{\sum_b \text{prom}_b\cdot\mathbf{1}\big[|\text{bsh}_b-\text{off}|\le 2\big]}{\sum_b \text{prom}_b+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это самопроверка без внешней истины: если полосы единодушны — узлу можно верить; если разбрелись (повтор/тишина/голос везде) — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt; мал, и детект/укладка такой узел не слушают.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart LR&lt;br /&gt;
    X[Окно 5с ref и dub] --&amp;gt; ST[STFT 2048/256]&lt;br /&gt;
    ST --&amp;gt; BM[48 лог-полос&amp;lt;br/&amp;gt;log1p + z-score по времени]&lt;br /&gt;
    BM --&amp;gt; CC[По-полосная кросс-корр&amp;lt;br/&amp;gt;cc_b lag]&lt;br /&gt;
    CC --&amp;gt; PR[prom_b = max − median]&lt;br /&gt;
    CC --&amp;gt; BSH[bsh_b = argmax lag]&lt;br /&gt;
    BSH --&amp;gt; WM[&amp;quot;wmedian по полосам (вес prom)&amp;quot;]&lt;br /&gt;
    PR --&amp;gt; WM&lt;br /&gt;
    WM --&amp;gt; OFF[off: сдвиг кадры + парабола]&lt;br /&gt;
    BSH --&amp;gt; AG[&amp;quot;agree: доля согласных полос → w&amp;quot;]&lt;br /&gt;
    OFF --&amp;gt; AG&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;muq как альтернатива.&#039;&#039;&#039; Метод &amp;lt;code&amp;gt;muq&amp;lt;/code&amp;gt; заменяет полосовую огибающую на GPU-эмбеддинги музыкальной модели, но &#039;&#039;&#039;дальше всё то же&#039;&#039;&#039; — та же поверхность &amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt;, тот же детект &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt;, та же сборка &amp;lt;code&amp;gt;assemble.py&amp;lt;/code&amp;gt;. band не требует новых зависимостей (только torch/numpy) и потому выбран дефолтом.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;10-грубая-off0--следящий-band&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;10-грубая-off0--следящий-band&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 10. Грубая off0 + следящий band ==&lt;br /&gt;
&lt;br /&gt;
Рабочее окно band узкое — &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;MAXLAG=0.7&amp;lt;/code&amp;gt; с (точность ценой диапазона). Но сдвиг опенинга бывает крупнее. Поэтому, как и в зрении, две стадии: грубая «тропа» + точное слежение (&amp;lt;code&amp;gt;anchor/apply.py&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;101-грубая-off0-_coarse_off0&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.1. Грубая off0 (&amp;lt;code&amp;gt;_coarse_off0&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Та же band-метрика, но с &#039;&#039;&#039;широким&#039;&#039;&#039; окном лага &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;COARSE_LAG_S=2.5&amp;lt;/code&amp;gt; с — она видит большой сдвиг. Робастность только статистикой, без порогов под контент:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;страж края&#039;&#039;&#039; — если аргмакс на границе &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, это «нет пика», вес обнуляется;&lt;br /&gt;
# скользящая &#039;&#039;&#039;взвешенная медиана&#039;&#039;&#039; (окно &amp;lt;code&amp;gt;med_win_s=5&amp;lt;/code&amp;gt; с) сдвига;&lt;br /&gt;
# &#039;&#039;&#039;гейт согласия соседей&#039;&#039;&#039; — узел стабилен, если &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|o-\text{med}|\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;stab_tol=4&amp;lt;/code&amp;gt; кадра;&lt;br /&gt;
# повторная медиана только по стабильным + интерполяция → непрерывная &amp;lt;code&amp;gt;off0(t)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;102-слежение-bandbuild_arr-07с&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 10.2. Слежение (&amp;lt;code&amp;gt;band.build_arr ±0.7с&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль предварительно варпится по &amp;lt;code&amp;gt;off0&amp;lt;/code&amp;gt; (грубая коррекция), и band с &#039;&#039;&#039;узким&#039;&#039;&#039; окном &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.7&amp;lt;/math&amp;gt; с меряет &#039;&#039;&#039;остаток&#039;&#039;&#039; на пред-варпленном дубле. Полный сдвиг — сумма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
o(t)=\text{off0}(t)+o_{\text{ост}}(t).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так узкое точное окно никогда не «теряет» крупный сдвиг — он уже снят грубой тропой, а band доводит лишь малый остаток. (Урок &amp;lt;code&amp;gt;EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;: GCC в грубом проходе подавляется голосом, band-грубый с медианой по полосам — нет.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;11-детект-ступеней-и-кривая-дрейфа&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;11-детект-ступеней-и-кривая-дрейфа&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 11. Детект ступеней и кривая дрейфа ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;111-детект--тот-же-аппарат-что-у-зрения-detectdetect&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.1. Детект — тот же аппарат, что у зрения (&amp;lt;code&amp;gt;detect.detect&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;(o,w)&amp;lt;/code&amp;gt; идёт в &#039;&#039;&#039;тот же&#039;&#039;&#039; &amp;lt;code&amp;gt;warp_curve&amp;lt;/code&amp;gt; (DP-сегментация взвешенными ломаными, §6.2), затем:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;tail_filter&amp;lt;/code&amp;gt;&#039;&#039;&#039; — рез реален только при устойчивом сдвиге &#039;&#039;&#039;хвоста&#039;&#039;&#039;: медиана уровня до/после на окне &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;win=25&amp;lt;/code&amp;gt; с, ограниченном соседними резами, с вес-гейтом; величина реза = это смещение. Аналог &amp;lt;code&amp;gt;video_cut_filter&amp;lt;/code&amp;gt; из зрения, но для аудио.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;_edge_refine&amp;lt;/code&amp;gt;&#039;&#039;&#039; — отдельно выделяет рез в первом/последнем сегменте (краевая зона, короче &amp;lt;code&amp;gt;msize&amp;lt;/code&amp;gt;), где обычная сегментация его не выделит.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;112-кривая-дрейфа-надараяуотсон--потолок-скорости-_smooth_drift_curve&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.2. Кривая дрейфа: Надарая–Уотсон + потолок скорости (&amp;lt;code&amp;gt;_smooth_drift_curve&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Простыми словами: сглаживание Надарая–Уотсона.&#039;&#039;&#039; Это «скользящее среднее с приоритетом надёжным точкам»: значение кривой в каждый момент — среднее соседних измерений, но уверенные точки (большой вес &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w&amp;lt;/math&amp;gt;) тянут сильнее, а шумные почти не влияют. Ширина «гауссова окна» &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma&amp;lt;/math&amp;gt; задаёт, насколько далёких соседей ещё учитывать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Внутри куска между резами строим гладкую кривую, которая &#039;&#039;&#039;следит за горками&#039;&#039;&#039; (плавный дрейф) и &#039;&#039;&#039;стоит на шуме&#039;&#039;&#039;. Это ядерное сглаживание Надарая–Уотсона с весом &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;w^{\text{qpow}}&amp;lt;/math&amp;gt; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\text{qpow}=3&amp;lt;/math&amp;gt;, гауссово ядро &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\sigma=3&amp;lt;/math&amp;gt; с):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{sm}(t)=\frac{\big(o\cdot w^{3}\big)\!*\!G_\sigma}{w^{3}\!*\!G_\sigma+\varepsilon}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поверх — &#039;&#039;&#039;потолок скорости изменения сдвига&#039;&#039;&#039;: между соседними узлами &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta\text{sm}|&amp;lt;/math&amp;gt; не больше &amp;lt;code&amp;gt;lim&amp;lt;/code&amp;gt;, что соответствует ускорению &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\le&amp;lt;/math&amp;gt;&amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt; (1.25 %/с = &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{lim}=\frac{\text{max\_pct\_s}}{100}\cdot\text{STEP}\cdot\frac{1000}{\text{FRAME}}\approx0.15\ \text{кадра/узел}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Потолок не даёт кривой прыгнуть за единичными яркими выбросами в слепой зоне.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;113-опора-после-реза-фикс-2026-06-18&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 11.3. Опора после реза (фикс 2026-06-18) ===&lt;br /&gt;
&lt;br /&gt;
Тонкость: в куске, &#039;&#039;&#039;который начинается с реза&#039;&#039;&#039;, band теряет чувствительность на самом монтажном стыке (тусклые выбросы сразу за резом). Если вести потолок скорости от левого края куска, он занижает старт, и кривая ~35 с медленно вытягивается к плато. Идея пользователя: расходиться в &#039;&#039;&#039;обе стороны от самого уверенного якоря&#039;&#039;&#039; в первых &amp;lt;code&amp;gt;anchor_lookback_s=30&amp;lt;/code&amp;gt; с куска (= начало плато):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;   сдвиг&lt;br /&gt;
     ▲                  ● ● ● ● ● ● ●  ← плато (уверенные якоря)&lt;br /&gt;
     │      опора ↑ argmax w в первых 30с&lt;br /&gt;
     │     ╱   ╲        clamp влево к резу + вправо вдоль плато&lt;br /&gt;
     │  ✗ ╱     ╲&lt;br /&gt;
     │ слепой    &lt;br /&gt;
     │ старт      &lt;br /&gt;
     ┼──┊─────────────────────────────►  t&lt;br /&gt;
        рез&amp;lt;/pre&amp;gt;&lt;br /&gt;
Слепой старт подтягивается &#039;&#039;&#039;к&#039;&#039;&#039; плато, а не плато к старту. Первый кусок (без реза слева) — по-старому: левый край = опора. Приёмка: 47/55 дорожек не тронуты, изменения = только исправления (память &amp;lt;code&amp;gt;project_band_postcut_anchor_fix&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;12-варп-тишина-в-резах-заполнение-рефом&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;12-варп-тишина-в-резах-заполнение-рефом&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 12. Варп, тишина в резах, заполнение рефом ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;121-кусочный-варп-_warp_piecewise&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.1. Кусочный варп (&amp;lt;code&amp;gt;_warp_piecewise&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Дубль ресэмплится по кускам между резами: внутри куска — гладкая &amp;lt;code&amp;gt;wcurve&amp;lt;/code&amp;gt; (горки), на резе — резкий стык. Для выходного отсчёта &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;lt;/math&amp;gt; источник в дубле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{src}(t)=\Big(t+\text{wcurve}(t)\cdot\tfrac{\text{FRAME}}{1000}\Big)\cdot sr,\qquad&lt;br /&gt;
\text{out}[t]=\operatorname{interp}\big(\text{src}(t)\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;122-тишина-в-резах&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.2. Тишина в резах ===&lt;br /&gt;
&lt;br /&gt;
Непрерывный варп на монтажном стыке «переиграл» бы звук дважды, поэтому в резах ставится тишина (контент разносится):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;lt;0&amp;lt;/math&amp;gt; (нехватка дубля) → тишина &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;|\Delta|&amp;lt;/math&amp;gt; по центру реза;&lt;br /&gt;
* &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\Delta&amp;gt;0&amp;lt;/math&amp;gt; (лишнее вырезано) → узкий шов &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm0.15&amp;lt;/math&amp;gt; с.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;123-заполнение-тишины-синхронным-рефом-_fill_silence_from_ref&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.3. Заполнение тишины синхронным рефом (&amp;lt;code&amp;gt;_fill_silence_from_ref&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Финальный проход — &#039;&#039;&#039;только после&#039;&#039;&#039; полного band/muq, когда дорожка уже синхронна рефу. Там, где озвучка молчит, а реф звучит, подставляем реф с кроссфейдом. Порог тишины измерен:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{RMS}_{\text{dBFS}}=20\log_{10}\!\Big(\tfrac{\text{RMS}}{32768}+\varepsilon\Big),\qquad&lt;br /&gt;
\text{fill}\iff \text{RMS}_{\text{дубль}}&amp;lt;-90\ \text{дБ}\ \wedge\ \text{RMS}_{\text{реф}}\ge-90\ \text{дБ}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Порог &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-90&amp;lt;/math&amp;gt; дБ — в центре измеренного плато настоящих дыр (ниже &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-80&amp;lt;/math&amp;gt;), далеко от обрыва тихого контента (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;-40\ldots-60&amp;lt;/math&amp;gt;), окно RMS 20 мс, мин. длина зоны 0.15 с, кроссфейд 30 мс. Где у обоих тишина — остаётся тишина; озвучка не теряется (трогаем только пустоты).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;Запрет (намертво).&#039;&#039;&#039; &amp;lt;code&amp;gt;fill_cuts&amp;lt;/code&amp;gt; — заливка вырезов рефом &#039;&#039;&#039;по видео, до&#039;&#039;&#039; анализа — запрещён навсегда: вставка в ещё несинхронную дорожку плодит мнимый рассинхрон. Разрешено только &amp;lt;code&amp;gt;fill_silence&amp;lt;/code&amp;gt; — заполнение &#039;&#039;&#039;после&#039;&#039;&#039; выравнивания, по синхронной дорожке (память &amp;lt;code&amp;gt;feedback_no_audio_replacement_fill_cuts&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;124-кросс-модальный-детектор-студийного-av-десинка-detect_av_desync&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 12.4. Кросс-модальный детектор студийного A/V-десинка (&amp;lt;code&amp;gt;detect_av_desync&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Read-only диагностика (на wav не влияет). После укладки &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; уже на сетке рефа; широким PHAT-окном (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с, полоса 50–13500 Гц) меряем остаточный лаг M&amp;amp;amp;E к реф-аудио по окнам. Если он &#039;&#039;&#039;большой и устойчивый&#039;&#039;&#039; (медиана велика, MAD мал) — аудио источника смещено относительно его же видео (студийный дефект, надёжно не чинится) → дорожка помечается &#039;&#039;&#039;красным&#039;&#039;&#039;. Паттерн валидирован: AniLibria ep01 kamennyj-okean медиана −40к / MAD 2.2к против ~0/~0 у 10 здоровых.&lt;br /&gt;
&lt;br /&gt;
{{#mermaid:&lt;br /&gt;
flowchart TB&lt;br /&gt;
    SIL[Дубль на сетке рефа + тишина в резах] --&amp;gt; OFF0[_coarse_off0&amp;lt;br/&amp;gt;band ±2.5с, медиана по полосам]&lt;br /&gt;
    OFF0 --&amp;gt; PRE[Пред-варп по off0]&lt;br /&gt;
    PRE --&amp;gt; BAND[&amp;quot;band ±0.7с → o_ост, w&amp;quot;]&lt;br /&gt;
    BAND --&amp;gt; SUM[&amp;quot;o = off0 + o_ост&amp;quot;]&lt;br /&gt;
    SUM --&amp;gt; DET[detect.detect&amp;lt;br/&amp;gt;warp_curve + tail_filter + edge_refine]&lt;br /&gt;
    DET --&amp;gt; DRIFT[_smooth_drift_curve&amp;lt;br/&amp;gt;Надарая–Уотсон + потолок 1.25%/с + опора-после-реза]&lt;br /&gt;
    DRIFT --&amp;gt; WP[_warp_piecewise + тишина в резах]&lt;br /&gt;
    WP --&amp;gt; FILL[_fill_silence_from_ref −90дБ]&lt;br /&gt;
    FILL --&amp;gt; AV[detect_av_desync&amp;lt;br/&amp;gt;красный флаг студийного десинка]&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Итог Части II.&#039;&#039;&#039; Поверх видео-карты звук band/muq снимает остаточный сдвиг аудио, который видео не видит: многополосная метрика делает корреляцию зрячей сквозь голос и мастеринг, грубая off0 ловит крупный сдвиг, следящий band доводит остаток, общий аппарат &amp;lt;code&amp;gt;detect.py&amp;lt;/code&amp;gt; ставит резы, кривая дрейфа с потолком скорости и опорой-после-реза кладёт гладко, тишина заполняется синхронным рефом. Дальше — как всё это читается на графиках.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;часть-iii-графики--диагноз-с-одного-взгляда&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Часть III. Графики — диагноз с одного взгляда =&lt;br /&gt;
&lt;br /&gt;
Файл &amp;lt;code&amp;gt;anchor/plots_unified.py&amp;lt;/code&amp;gt;. Проектная установка: алгоритмы доведены до состояния, где &#039;&#039;&#039;качество читается по графику, без ручной сверки в Premiere&#039;&#039;&#039;. Беглый взгляд на любой график даёт чёткий ответ «всё в порядке или нет». Поэтому график — не украшение, а главный инструмент приёмки и отладки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;13-зачем-графики&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;13-зачем-графики-и-что-на-них-видно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 13. Зачем графики и что на них видно ==&lt;br /&gt;
&lt;br /&gt;
Единый график строится &#039;&#039;&#039;на каждую пару&#039;&#039;&#039; и показывает оба слоя сразу: &#039;&#039;&#039;верхняя панель — зрение&#039;&#039;&#039; (видео-укладка, всегда), &#039;&#039;&#039;нижняя — звук&#039;&#039;&#039; (доводка band/muq, если включена), на общей оси времени с синхронным зумом и ховером. Один взгляд отвечает на четыре вопроса:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Вопрос&lt;br /&gt;
! Где смотреть&lt;br /&gt;
|-&lt;br /&gt;
| Держится ли укладка за &#039;&#039;&#039;надёжные&#039;&#039;&#039; данные?&lt;br /&gt;
| цвет точек (уверенность): линия должна идти по ярким, не по тусклым&lt;br /&gt;
|-&lt;br /&gt;
| Есть ли необъяснённый &#039;&#039;&#039;перекос/увод&#039;&#039;&#039;?&lt;br /&gt;
| отклонение линии от облака точек&lt;br /&gt;
|-&lt;br /&gt;
| Верно ли найдены &#039;&#039;&#039;монтажные резы&#039;&#039;&#039;?&lt;br /&gt;
| вертикальные пунктиры и их подписи (мс)&lt;br /&gt;
|-&lt;br /&gt;
| Согласны ли &#039;&#039;&#039;зрение и звук&#039;&#039;&#039;?&lt;br /&gt;
| сравнение двух панелей в одной зоне&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;14-дрейф-форма&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;14-дрейф-форма-и-робастная-база-тейласена&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 14. Дрейф-форма и робастная база Тейла–Сена ==&lt;br /&gt;
&lt;br /&gt;
Сырой сдвиг по всей серии может быть сотни кадров (крупный масштаб оси), и на нём не видно ни плато, ни ступеней в десяток кадров. Поэтому обе панели рисуются в &#039;&#039;&#039;дрейф-форме&#039;&#039;&#039; — вычитается робастная линейная база, и всё разворачивается вокруг нуля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #3a86ff; background:#eef3ff; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Отступление: база Тейла–Сена.&#039;&#039;&#039; Чтобы провести «среднюю линию» через облако точек и не дать единичным выбросам её перекосить, берут наклон между каждой парой точек и выбирают медианный — половина пар «за», половина «против». Это устойчивее обычной прямой и не требует никаких порогов отбраковки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
База оценивается &#039;&#039;&#039;методом Тейла–Сена&#039;&#039;&#039; (&amp;lt;code&amp;gt;_baseline&amp;lt;/code&amp;gt;) — он устойчив к выбросам, в отличие от МНК: на выборке точек (до 400) берутся все попарные наклоны, и базовый наклон — их медиана; сдвиг — медиана остатков:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
a=\operatorname{median}_{i&amp;lt;j}\frac{y_j-y_i}{t_j-t_i},\qquad&lt;br /&gt;
b=\operatorname{median}_k\big(y_k-a\,t_k\big).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
База считается по &#039;&#039;&#039;телу&#039;&#039;&#039; дорожки (исключая голову/заставку: &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;t&amp;gt;\text{head}+5&amp;lt;/math&amp;gt; с). Затем:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math display=&amp;quot;block&amp;quot;&amp;gt;&lt;br /&gt;
\text{dev}_{\text{якорь}}=\text{shift}-(a\,t+b),\qquad&lt;br /&gt;
\text{dev}_{\text{кривая}}=\text{curve}-(a\,T+b),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а предел оси берётся как &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\max(12,\ p_{99}(|\text{dev}|)+4)&amp;lt;/math&amp;gt; кадров — авто-масштаб под реальный разброс. На резах в линию вставляется &amp;lt;code&amp;gt;NaN&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;_breaks&amp;lt;/code&amp;gt;), чтобы она &#039;&#039;&#039;не соединяла&#039;&#039;&#039; куски через разрыв — ступень видна как обрыв, а не как вертикаль.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 14.1 — «Сырой сдвиг vs дрейф-форма».&#039;&#039;&#039; Два графика друг над другом для одной дорожки: сверху сырой &amp;lt;code&amp;gt;shift(t)&amp;lt;/code&amp;gt; (линия уходит на −600 кадров, мелкие детали не видны); снизу та же дорожка в дрейф-форме (база вычтена, всё колеблется в ±15 кадров вокруг 0, видны и плато, и ступенька реза). Подпись: «Дрейф-форма вытаскивает структуру, которую крупный масштаб прячет».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;15-слои-и-разбор-кейсов&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;15-слои-цветокодирование-разбор-реальных-кейсов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== 15. Слои, цветокодирование, разбор реальных кейсов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;151-слои-единого-графика&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.1. Слои единого графика ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхняя панель (ЗРЕНИЕ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки якорей &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(t_{\text{реф}},\ \text{dev}_{\text{якорь}})&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;cos&#039;&#039;&#039; (viridis, 0…1) — надёжность каждого видео-якоря;&lt;br /&gt;
* оранжевая линия — &#039;&#039;&#039;карта зрения&#039;&#039;&#039; в дрейф-форме, с разрывами на резах;&lt;br /&gt;
* crimson-пунктиры — &#039;&#039;&#039;резы зрения&#039;&#039;&#039; с подписью величины (мс);&lt;br /&gt;
* заголовок несёт абсолютную медиану сдвига.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Нижняя панель (ЗВУК):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* точки &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;(T,\ o)&amp;lt;/math&amp;gt;, цвет = &#039;&#039;&#039;w&#039;&#039;&#039; (cividis, 0…1.2) — уверенность band;&lt;br /&gt;
* серые точки — &#039;&#039;&#039;gcc-свидетель&#039;&#039;&#039; (&amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;\pm2.5&amp;lt;/math&amp;gt; с), справочно;&lt;br /&gt;
* зелёная линия — &#039;&#039;&#039;кривая band&#039;&#039;&#039; (дрейф), с разрывами на резах;&lt;br /&gt;
* purple-пунктиры — &#039;&#039;&#039;резы детекта&#039;&#039;&#039; аудио.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⚠ &#039;&#039;&#039;gcc — НЕ судья.&#039;&#039;&#039; Серая gcc-линия на контенте проекта (закадр+музыка) почти слепа и скачет ±40к = шум; её нельзя брать за подтверждение укладки. Судьи сходимости — сам band (48 полос) / muq + уши/глаза на наложенных дорожках (память &amp;lt;code&amp;gt;feedback_gcc_ncc_blind_judge&amp;lt;/code&amp;gt;). gcc на графике — фон, не вердикт.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;152-как-цвет-читается-мгновенно&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.2. Как цвет читается мгновенно ===&lt;br /&gt;
&lt;br /&gt;
Цвет точки = уверенность. Тёмные (фиолетовые/синие) — шумные, ярких (жёлтые/зелёные) — надёжные. &#039;&#039;&#039;Правильная линия идёт по ярким точкам.&#039;&#039;&#039; Если линия отклонилась, а рядом плотное облако ярких — это сигнал проблемы: либо увод укладки, либо линия последовала за тусклыми выбросами на краю. Это и есть «диагноз с одного взгляда».&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.1 — «Эталон единого графика (здоровая дорожка)».&#039;&#039;&#039; Реальный скрин двухпанельного графика: вверху зрение — плотное жёлтое облако якорей точно на 0, оранжевая линия плоская, резов нет; внизу звук — узкое облако вокруг 0, зелёная линия плоская. Подпись: «Так выглядит ОК: линии по ярким точкам, ноль отклонения, нет резов». &#039;&#039;(Скрин взять из &amp;lt;code&amp;gt;&amp;amp;lt;серия&amp;amp;gt;/_plots/&amp;amp;lt;stem&amp;amp;gt;__track.png&amp;lt;/code&amp;gt; любой здоровой пары.)&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.2 — «Перекос укладки (до фикса опоры-после-реза)».&#039;&#039;&#039; Скрин зоны 180–220 с AniDUB: после реза линия band провалилась вниз за парой тусклых (тёмных) якорей у края, хотя выше плотное плато ярких якорей на другом уровне. Рядом — тот же участок после фикса: линия держится за плато (опора = увереннейший якорь начала плато), слепой старт подтянут к плато. Подпись: «Линия не должна вестись за тусклыми единицами на краю».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:2px dashed #b8860b; background:#fffdf3; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
🖼️ &#039;&#039;&#039;ИЗОБРАЖЕНИЕ 15.3 — «Ложный рез из-за слепоты зрения (ep05 Amazing, 950–979 с)».&#039;&#039;&#039; Скрин зоны с двумя близкими резами: до фикса между ними узкий клин и лишний рез (окно проверки ±80 с перехлестнуло через соседний рез и поймало чужой уровень); после фикса (окно ограничено соседними резами) лишний рез убран. Подпись: «Окно проверки уровня ограничивается соседними резами — иначе чужой уровень ложно подтверждает рез».&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;153-носители-png-превью-и-интерактивный-html&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== 15.3. Носители: PNG-превью и интерактивный HTML ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PNG&#039;&#039;&#039; (&amp;lt;code&amp;gt;matplotlib&amp;lt;/code&amp;gt;, Agg) — статичная миниатюра (1 или 2 панели) для быстрого взгляда в панели/отчёте.&lt;br /&gt;
* &#039;&#039;&#039;HTML&#039;&#039;&#039; (&amp;lt;code&amp;gt;plotly&amp;lt;/code&amp;gt;, self-contained, открывается офлайн) — для детального разбора. Точки и линия в WebGL (&amp;lt;code&amp;gt;scattergl&amp;lt;/code&amp;gt;), чтобы линия рисовалась поверх точек независимо от порядка; ховер &amp;lt;code&amp;gt;x unified&amp;lt;/code&amp;gt;, &#039;&#039;&#039;зум любой зоны вручную&#039;&#039;&#039;. Отдельные PNG каждого реза не создаём — HTML позволяет приблизить любой стык (решение 2026-06-18).&lt;br /&gt;
&lt;br /&gt;
Read-only по контракту: падение рендера не роняет conform (ловит вызывающий).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложения&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Приложения =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;приложение-а-константы&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-а-константы-и-их-обоснования&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение А. Константы и их обоснования ==&lt;br /&gt;
&lt;br /&gt;
Все ключевые числа калиброваны не «на глаз», а по &#039;&#039;&#039;плато нечувствительности&#039;&#039;&#039;: на свипе по диапазону результат (резы по датасету) не меняется — значит подстройки под контент нет.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;зрение&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Зрение ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GW×GH&amp;lt;/code&amp;gt;&lt;br /&gt;
| 128×72&lt;br /&gt;
| features&lt;br /&gt;
| размер серого кадра под SRM&lt;br /&gt;
|-&lt;br /&gt;
| clip &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;&lt;br /&gt;
| ±3&lt;br /&gt;
| features&lt;br /&gt;
| обрезка выбросов отклика фильтров&lt;br /&gt;
|-&lt;br /&gt;
| dim&lt;br /&gt;
| 18432&lt;br /&gt;
| features&lt;br /&gt;
| &amp;lt;math display=&amp;quot;inline&amp;quot;&amp;gt;2\cdot128\cdot72&amp;lt;/math&amp;gt;, два канала&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;K&amp;lt;/code&amp;gt; (coarse)&lt;br /&gt;
| 8&lt;br /&gt;
| coarse&lt;br /&gt;
| прорежение грубого прохода&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| coarse&lt;br /&gt;
| полуокно зануления при поиске 2-го пика&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VHI&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.60&lt;br /&gt;
| coarse&lt;br /&gt;
| порог «пик высокий» (надёжный якорь)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CMIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.12&lt;br /&gt;
| coarse&lt;br /&gt;
| порог различимости (пик одинок → не двойка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20&lt;br /&gt;
| dropdtw&lt;br /&gt;
| штраф выброса (базовый Drop-DTW)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;OPEN/EXT&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.20 / 0.02&lt;br /&gt;
| dropdtw&lt;br /&gt;
| gap-open / gap-extend (острый шов)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DSYN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| плоская цена выброса кадра дубля (вставка)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MATCH_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.30&lt;br /&gt;
| dropdtw/align&lt;br /&gt;
| guard: кадр с матчем лучше этого нельзя выбросить&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MARG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 120&lt;br /&gt;
| band_align&lt;br /&gt;
| полуширина следящей полосы (≈5 с)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CHUNK/OVERLAP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 4000 / 700&lt;br /&gt;
| band_align&lt;br /&gt;
| кусок Drop-DTW и перекрытие&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_WIN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно агрегации якорей (плато 0.2–0.5)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 800&lt;br /&gt;
| vision_detect&lt;br /&gt;
| штраф DP-сегментации (плато 200–3000)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| vision_detect&lt;br /&gt;
| степень веса = (cos·agree)³ (плато 2–3)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.45&lt;br /&gt;
| vision_detect&lt;br /&gt;
| потолок наклона прямых, к/с (плато 0.3–0.6)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VIS_MSIZE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30&lt;br /&gt;
| vision_detect&lt;br /&gt;
| мин. длина сегмента, с (плато 8–60)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_WIN_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 80&lt;br /&gt;
| vision_detect&lt;br /&gt;
| окно проверки устойчивости уровня (плато 45–120)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_GATE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.55&lt;br /&gt;
| vision_detect&lt;br /&gt;
| вес-гейт надёжных узлов (плато 0.3–0.8)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;VC_THR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 200 мс&lt;br /&gt;
| vision_detect&lt;br /&gt;
| порог величины реза (плато 60–400 мс)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;звук&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Звук ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Константа&lt;br /&gt;
! Значение&lt;br /&gt;
! Файл&lt;br /&gt;
! Смысл / обоснование&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;sr&amp;lt;/code&amp;gt;&lt;br /&gt;
| 16000&lt;br /&gt;
| band&lt;br /&gt;
| частота анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NB&amp;lt;/code&amp;gt;&lt;br /&gt;
| 48&lt;br /&gt;
| band&lt;br /&gt;
| число лог-полос (50–14000 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;nfft/hop&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2048 / 256&lt;br /&gt;
| band&lt;br /&gt;
| STFT (темп огибающей 62.5 Гц)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;win&amp;lt;/code&amp;gt;&lt;br /&gt;
| 5 с&lt;br /&gt;
| band&lt;br /&gt;
| окно анализа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAXLAG&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.7 с&lt;br /&gt;
| band&lt;br /&gt;
| рабочее окно лага (точность)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;COARSE_LAG_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2.5 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно грубой off0 (видит сдвиг опенинга)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tol&amp;lt;/code&amp;gt;&lt;br /&gt;
| 2 кадра&lt;br /&gt;
| band&lt;br /&gt;
| допуск согласия полос (agree)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FRAME&amp;lt;/code&amp;gt;&lt;br /&gt;
| 41.708 мс&lt;br /&gt;
| params&lt;br /&gt;
| мс/кадр (23.976 fps)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;STEP&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.5 с&lt;br /&gt;
| params&lt;br /&gt;
| шаг сетки T&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_FR&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3 (120 мс)&lt;br /&gt;
| params&lt;br /&gt;
| порог величины реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PEN&amp;lt;/code&amp;gt;&lt;br /&gt;
| 700&lt;br /&gt;
| params&lt;br /&gt;
| штраф DP-сегментации (детект)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;QPOW&amp;lt;/code&amp;gt;&lt;br /&gt;
| 3.0&lt;br /&gt;
| params&lt;br /&gt;
| степень веса якоря&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SMAX&amp;lt;/code&amp;gt;&lt;br /&gt;
| 0.3&lt;br /&gt;
| params&lt;br /&gt;
| макс. наклон сегмента (≤~2%)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MSIZE_S&amp;lt;/code&amp;gt;&lt;br /&gt;
| 20 с&lt;br /&gt;
| params&lt;br /&gt;
| мин. длина сегмента&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;max_pct_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 1.25 %/с&lt;br /&gt;
| apply&lt;br /&gt;
| потолок скорости кривой дрейфа&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;anchor_lookback_s&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 с&lt;br /&gt;
| apply&lt;br /&gt;
| окно поиска опоры-после-реза&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIL_FILL_DB&amp;lt;/code&amp;gt;&lt;br /&gt;
| −90 дБ&lt;br /&gt;
| align&lt;br /&gt;
| порог тишины озвучки (центр плато дыр)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;XFADE&amp;lt;/code&amp;gt;&lt;br /&gt;
| 30 мс&lt;br /&gt;
| align&lt;br /&gt;
| кроссфейд заполнения рефом&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #e8a317; background:#fff9e6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
⏱ &#039;&#039;&#039;Правило прогонов&#039;&#039;&#039; (память &amp;lt;code&amp;gt;feedback_warn_long_runs&amp;lt;/code&amp;gt;): любой тест ≤10 мин — параллелить (клипы &amp;lt;code&amp;gt;--workers 8&amp;lt;/code&amp;gt;, конформы ×6 пар, синтетика &amp;lt;code&amp;gt;--workers 24&amp;lt;/code&amp;gt;) или резать объём; дольше 10 мин — только с явного согласия пользователя, оценку времени — заранее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;a name=&amp;quot;приложение-б-карта-файлов&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;приложение-б-карта-файлов&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Приложение Б. Карта файлов ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;src/track_muxer/conform/&lt;br /&gt;
├── features.py              SRM-отпечатки кадров (KB/D1, L2, dim 18432)&lt;br /&gt;
├── kernel/&lt;br /&gt;
│   ├── coarse.py            грубый проход: G=cs·crᵀ, якоря, LIS, оконный&lt;br /&gt;
│   ├── dropdtw.py           Drop-DTW (base/affine/guard/free_start)&lt;br /&gt;
│   └── band_align.py        следящая полоса + склейка кусков&lt;br /&gt;
├── vision_detect.py         УКЛАДКА зрения: (o,w) → детект → ломаная (ЕДИНСТВЕННАЯ карта)&lt;br /&gt;
├── align.py                 оркестровка пары: pred → разбор правок → ресэмпл → звук → график&lt;br /&gt;
└── anchor/                  АУДИО-доводка band/muq поверх зрения&lt;br /&gt;
    ├── apply.py             coarse_off0, следящий band, smooth_drift_curve, варп, fill&lt;br /&gt;
    ├── detect.py            ОБЩИЙ аппарат: wfit, warp_curve, tail_filter, edge_refine&lt;br /&gt;
    ├── assemble.py          freeze + сборка варпа&lt;br /&gt;
    ├── params.py            FRAME, T, STEP, PEN, QPOW, SMAX, MSIZE_S …&lt;br /&gt;
    ├── maps/band.py         48-полосная DSP-метрика (без модели) — дефолт&lt;br /&gt;
    ├── maps/muq.py          GPU-эмбеддинги (альтернатива)&lt;br /&gt;
    ├── plots_unified.py     ЕДИНЫЙ график зрение+звук (PNG + HTML)&lt;br /&gt;
    └── plots_html.py        отдельный HTML band (легаси-путь)&amp;lt;/pre&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Тема&lt;br /&gt;
! Отчёт (суть)&lt;br /&gt;
|-&lt;br /&gt;
| Прорыв видео-синхрона&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_align/BREAKTHROUGH_VIDEO_SYNC.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Методика зрячих свидетелей аудио&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/METHODOLOGY_audio_matching.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Грубый→band-follow&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_coarse_band_follow.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Аудио-аанкер (band/muq)&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/reports/audio_bench/EXPERIMENTS_audio_auto.md&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Архитектура conform&lt;br /&gt;
| &amp;lt;code&amp;gt;doc/conform_architecture_audit.md&amp;lt;/code&amp;gt; §14&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border-left:5px solid #aaa; background:#f6f6f6; padding:6px 12px; margin:10px 0;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Версия документа:&#039;&#039;&#039; 2026-06-18, под архитектуру «анализатор зрения — единственная видео-карта; band/muq — доводка поверх». Числа и формулы сверены с боевым кодом &amp;lt;code&amp;gt;src/track_muxer/conform/**&amp;lt;/code&amp;gt;. При расхождении кода и документа — приоритет у кода, документ актуализировать.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1008</id>
		<title>Track Muxer</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=Track_Muxer&amp;diff=1008"/>
		<updated>2026-06-18T19:43:36Z</updated>

		<summary type="html">&lt;p&gt;Владимир: Новая страница: «пока что пусто»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;пока что пусто&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=1007</id>
		<title>Заглавная страница</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=1007"/>
		<updated>2026-06-18T19:42:11Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* Мои проекты */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Да! Это моя вики, и ничего тут не поделать!&lt;br /&gt;
&lt;br /&gt;
Пока все в разнобой.&lt;br /&gt;
&lt;br /&gt;
==[[Моя сетевая инфраструктура|Дела админские]]==&lt;br /&gt;
&lt;br /&gt;
*[[Linux]]&lt;br /&gt;
*[[Сеть]]&lt;br /&gt;
*[[Сервер Proxmox]]&lt;br /&gt;
*[[Умное управление вентиляторами для Huananzhi H12D-8D (EPYC SP3 + NVIDIA GPU)]]&lt;br /&gt;
*[[Облачный сервер Nextcloud]]&lt;br /&gt;
*[[Nginx прокси сервер]]&lt;br /&gt;
*[[Сервис MediaWiki]]&lt;br /&gt;
*[[Файловый сервер SMB 2.0]] (Устарело: [[Файловый сервер SMB 1.0|1.0]])&lt;br /&gt;
*[[Сервер загрузки]]&lt;br /&gt;
*[[Plex медиа сервер]]&lt;br /&gt;
*[[WG]]&lt;br /&gt;
*Zabbix сервер&lt;br /&gt;
*[[Умный дом]]&lt;br /&gt;
*[[Суверенный интернет]]&lt;br /&gt;
&lt;br /&gt;
* [[Gitlab]]&lt;br /&gt;
* &#039;&#039;&#039;[[ZFS]]&#039;&#039;&#039;&lt;br /&gt;
* [[NFS]]&lt;br /&gt;
* [[Автоматизация печати по расписанию|Как я автоматизировал печать по расписанию.]]&lt;br /&gt;
* [[О mi R3P]]&lt;br /&gt;
*[[Mercusys MR70X|О Mercusys MR70X]]&lt;br /&gt;
* [[alt]]&lt;br /&gt;
* [[OpenWrt]]&lt;br /&gt;
&lt;br /&gt;
==3D Принтер Wolfram 2.0==&lt;br /&gt;
&lt;br /&gt;
* [[Marlin|Прошивка marlin]]&lt;br /&gt;
*[[Прошивка marlin 2 0]]&lt;br /&gt;
&lt;br /&gt;
* Прошивка Китайская&lt;br /&gt;
&lt;br /&gt;
* [[Драйверы для 3D принтеров]]&lt;br /&gt;
&lt;br /&gt;
==Различные проекты использующие ИИ==&lt;br /&gt;
&lt;br /&gt;
* [[MMEditing]]&lt;br /&gt;
* [[ESRGAN]]&lt;br /&gt;
* [[BasicSR]]&lt;br /&gt;
* [[WSL2 и CUDA]]&lt;br /&gt;
* [[Обучение ESRGAN]]&lt;br /&gt;
* [[Bandit Audio Separation: Форки и Standalone Проект]]&lt;br /&gt;
&lt;br /&gt;
== The Elder Scrolls V Skyrim ==&lt;br /&gt;
&lt;br /&gt;
* [[The Elder Scrolls V Skyrim сборка 2021|сборка 2021]]&lt;br /&gt;
* [[jBS2BG - BodySlide to BodyGen Converter and Generator]]&lt;br /&gt;
&lt;br /&gt;
== Minecraft ==&lt;br /&gt;
&lt;br /&gt;
* [[Minecraft 1.7.10 сборка для сервера GregTech|сервер 01]]&lt;br /&gt;
&lt;br /&gt;
== Работа с Видео ==&lt;br /&gt;
&lt;br /&gt;
* [[Dolby Vision]]&lt;br /&gt;
* [[FFmpeg]]&lt;br /&gt;
* [[Мои скрипты]]&lt;br /&gt;
&lt;br /&gt;
== Умный дом ==&lt;br /&gt;
&lt;br /&gt;
* [[Выключатели на базе ESP]]&lt;br /&gt;
*[[Управление кондиционерами]]&lt;br /&gt;
&lt;br /&gt;
== Мои проекты ==&lt;br /&gt;
&lt;br /&gt;
* [[Эквализация комнаты с Sony HT-A9]]&lt;br /&gt;
* [[Track Muxer]]&lt;br /&gt;
&lt;br /&gt;
==Некоторые полезные ресурсы==&lt;br /&gt;
&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список возможных настроек];&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Manual:FAQ/ru Часто задаваемые вопросы и ответы по MediaWiki];&lt;br /&gt;
*[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Рассылка уведомлений о выходе новых версий MediaWiki].&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Перевод MediaWiki на свой язык]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Узнайте, как бороться со спамом в вашей вики]&lt;br /&gt;
*[[Служебная:Версия|Служебная информация]]&lt;br /&gt;
&lt;br /&gt;
__БЕЗ_ОГЛАВЛЕНИЯ__&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=1006</id>
		<title>Заглавная страница</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=1006"/>
		<updated>2026-06-18T19:41:34Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Да! Это моя вики, и ничего тут не поделать!&lt;br /&gt;
&lt;br /&gt;
Пока все в разнобой.&lt;br /&gt;
&lt;br /&gt;
==[[Моя сетевая инфраструктура|Дела админские]]==&lt;br /&gt;
&lt;br /&gt;
*[[Linux]]&lt;br /&gt;
*[[Сеть]]&lt;br /&gt;
*[[Сервер Proxmox]]&lt;br /&gt;
*[[Умное управление вентиляторами для Huananzhi H12D-8D (EPYC SP3 + NVIDIA GPU)]]&lt;br /&gt;
*[[Облачный сервер Nextcloud]]&lt;br /&gt;
*[[Nginx прокси сервер]]&lt;br /&gt;
*[[Сервис MediaWiki]]&lt;br /&gt;
*[[Файловый сервер SMB 2.0]] (Устарело: [[Файловый сервер SMB 1.0|1.0]])&lt;br /&gt;
*[[Сервер загрузки]]&lt;br /&gt;
*[[Plex медиа сервер]]&lt;br /&gt;
*[[WG]]&lt;br /&gt;
*Zabbix сервер&lt;br /&gt;
*[[Умный дом]]&lt;br /&gt;
*[[Суверенный интернет]]&lt;br /&gt;
&lt;br /&gt;
* [[Gitlab]]&lt;br /&gt;
* &#039;&#039;&#039;[[ZFS]]&#039;&#039;&#039;&lt;br /&gt;
* [[NFS]]&lt;br /&gt;
* [[Автоматизация печати по расписанию|Как я автоматизировал печать по расписанию.]]&lt;br /&gt;
* [[О mi R3P]]&lt;br /&gt;
*[[Mercusys MR70X|О Mercusys MR70X]]&lt;br /&gt;
* [[alt]]&lt;br /&gt;
* [[OpenWrt]]&lt;br /&gt;
&lt;br /&gt;
==3D Принтер Wolfram 2.0==&lt;br /&gt;
&lt;br /&gt;
* [[Marlin|Прошивка marlin]]&lt;br /&gt;
*[[Прошивка marlin 2 0]]&lt;br /&gt;
&lt;br /&gt;
* Прошивка Китайская&lt;br /&gt;
&lt;br /&gt;
* [[Драйверы для 3D принтеров]]&lt;br /&gt;
&lt;br /&gt;
==Различные проекты использующие ИИ==&lt;br /&gt;
&lt;br /&gt;
* [[MMEditing]]&lt;br /&gt;
* [[ESRGAN]]&lt;br /&gt;
* [[BasicSR]]&lt;br /&gt;
* [[WSL2 и CUDA]]&lt;br /&gt;
* [[Обучение ESRGAN]]&lt;br /&gt;
* [[Bandit Audio Separation: Форки и Standalone Проект]]&lt;br /&gt;
&lt;br /&gt;
== The Elder Scrolls V Skyrim ==&lt;br /&gt;
&lt;br /&gt;
* [[The Elder Scrolls V Skyrim сборка 2021|сборка 2021]]&lt;br /&gt;
* [[jBS2BG - BodySlide to BodyGen Converter and Generator]]&lt;br /&gt;
&lt;br /&gt;
== Minecraft ==&lt;br /&gt;
&lt;br /&gt;
* [[Minecraft 1.7.10 сборка для сервера GregTech|сервер 01]]&lt;br /&gt;
&lt;br /&gt;
== Работа с Видео ==&lt;br /&gt;
&lt;br /&gt;
* [[Dolby Vision]]&lt;br /&gt;
* [[FFmpeg]]&lt;br /&gt;
* [[Мои скрипты]]&lt;br /&gt;
&lt;br /&gt;
== Умный дом ==&lt;br /&gt;
&lt;br /&gt;
* [[Выключатели на базе ESP]]&lt;br /&gt;
*[[Управление кондиционерами]]&lt;br /&gt;
&lt;br /&gt;
== Мои проекты ==&lt;br /&gt;
&lt;br /&gt;
* [[Эквализация комнаты с Sony HT-A9]]&lt;br /&gt;
* Track Muxer&lt;br /&gt;
&lt;br /&gt;
==Некоторые полезные ресурсы==&lt;br /&gt;
&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список возможных настроек];&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Manual:FAQ/ru Часто задаваемые вопросы и ответы по MediaWiki];&lt;br /&gt;
*[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Рассылка уведомлений о выходе новых версий MediaWiki].&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Перевод MediaWiki на свой язык]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Узнайте, как бороться со спамом в вашей вики]&lt;br /&gt;
*[[Служебная:Версия|Служебная информация]]&lt;br /&gt;
&lt;br /&gt;
__БЕЗ_ОГЛАВЛЕНИЯ__&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%9E%D0%B1%D1%85%D0%BE%D0%B4_%D0%9D%D0%9A%D0%A0&amp;diff=1005</id>
		<title>Обход НКР</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%9E%D0%B1%D1%85%D0%BE%D0%B4_%D0%9D%D0%9A%D0%A0&amp;diff=1005"/>
		<updated>2026-06-05T12:04:22Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* Шаг 1: Скопируйте список доменов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Установка и настройка связки Zapret + Podkop на OpenWRT =&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
Данная инструкция описывает установку и настройку связки двух сервисов обхода блокировок на роутере OpenWRT:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Zapret&#039;&#039;&#039; — утилита DPI-обхода (anti-DPI), работает без VPN&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Podkop&#039;&#039;&#039; — прокси-менеджер на базе sing-box с поддержкой VPN&lt;br /&gt;
&lt;br /&gt;
=== Суть связки ===&lt;br /&gt;
Комбинация Zapret + Podkop позволяет реализовать оптимальную стратегию обхода блокировок:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Zapret&#039;&#039;&#039; обрабатывает трафик &#039;&#039;&#039;первым&#039;&#039;&#039; (приоритет firewall -150/-100) и применяет DPI-обход для популярных сервисов (YouTube, Discord, Steam и др.) &#039;&#039;&#039;без использования VPN&#039;&#039;&#039;. Это обеспечивает:&lt;br /&gt;
&lt;br /&gt;
#* Высокую скорость (нет шифрования VPN)&lt;br /&gt;
&lt;br /&gt;
#* Сохранение геолокации РФ (важно для YouTube, Twitch и других региональных сервисов)&lt;br /&gt;
&lt;br /&gt;
#* Минимальную нагрузку на роутер&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Podkop&#039;&#039;&#039; обрабатывает трафик &#039;&#039;&#039;вторым&#039;&#039;&#039; (приоритет firewall 0) и направляет через VPN/прокси &#039;&#039;&#039;только те домены&#039;&#039;&#039;, которые Zapret не может обойти или требуют смены региона&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-message-box mw-message-box-notice&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Важно.&#039;&#039;&#039; Эта архитектура позволяет &#039;&#039;&#039;экономить VPN-трафик&#039;&#039;&#039; и &#039;&#039;&#039;сохранять региональный контент&#039;&#039;&#039; (например, русскоязычные каналы YouTube), при этом имея полноценный VPN для заблокированных ресурсов.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
&lt;br /&gt;
* OpenWRT версии 24 или выше&lt;br /&gt;
&lt;br /&gt;
* Минимум 25 МБ свободного места в памяти роутера&lt;br /&gt;
&lt;br /&gt;
* Доступ к интернету для загрузки пакетов&lt;br /&gt;
&lt;br /&gt;
* Базовые навыки работы с SSH/терминалом&lt;br /&gt;
&lt;br /&gt;
== Установка Zapret ==&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1: Установка вспомогательных пакетов ===&lt;br /&gt;
Через веб-интерфейс OpenWRT перейдите в &#039;&#039;&#039;System → Software&#039;&#039;&#039; и установите:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; — для скачивания установщика&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;code&amp;gt;luci-app-ttyd&amp;lt;/code&amp;gt; — для доступа к терминалу через веб-интерфейс&lt;br /&gt;
&lt;br /&gt;
После установки &amp;lt;code&amp;gt;luci-app-ttyd&amp;lt;/code&amp;gt; в меню появится раздел &#039;&#039;&#039;Services → Terminal&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2: Запуск установщика Zapret ===&lt;br /&gt;
Откройте терминал (&#039;&#039;&#039;Services → Terminal&#039;&#039;&#039;) и выполните команду:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
curl -fsSL https://raw.githubusercontent.com/remittor/zapret-openwrt/zap1/zapret/update-pkg.sh -o /tmp/zap.sh &amp;amp;&amp;amp; sh /tmp/zap.sh -u 1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Дождитесь завершения установки.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3: Перезагрузка интерфейса ===&lt;br /&gt;
Выйдите из веб-интерфейса (&#039;&#039;&#039;Log Out&#039;&#039;&#039;) и войдите снова. В меню &#039;&#039;&#039;Services&#039;&#039;&#039; появится раздел &#039;&#039;&#039;Zapret&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Установка Podkop ==&lt;br /&gt;
Выполните команду в терминале:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sh &amp;lt;(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;После установки &#039;&#039;&#039;обязательно очистите кэш браузера&#039;&#039;&#039; и перезайдите в LuCI. В меню &#039;&#039;&#039;Services&#039;&#039;&#039; появится раздел &#039;&#039;&#039;Podkop&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Настройка Zapret ==&lt;br /&gt;
&lt;br /&gt;
После установки пакет &#039;&#039;&#039;уже содержит&#039;&#039;&#039; нужные домены и параметры — &#039;&#039;&#039;ручную настройку проходить не нужно&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Откройте &#039;&#039;&#039;Services → Zapret → Service&#039;&#039;&#039; и проверьте только следующее:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Service daemons control&#039;&#039;&#039; — включён (сервис Zapret активен).&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Service autorun control&#039;&#039;&#039; — включён (Zapret поднимается после перезагрузки роутера).&lt;br /&gt;
&lt;br /&gt;
При необходимости можно добавить свои домены в поле &#039;&#039;&#039;User hostname entries&#039;&#039;&#039; в веб-интерфейсе (как правило — &#039;&#039;&#039;по одному домену на строку&#039;&#039;&#039;); для типовой установки это &#039;&#039;&#039;не обязательно&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Настройка Podkop ==&lt;br /&gt;
&lt;br /&gt;
=== Важное предупреждение о списках сообщества ===&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-message-box mw-message-box-warning&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ПРЕДУПРЕЖДЕНИЕ:&#039;&#039;&#039; Не используйте готовые списки сообщества &#039;&#039;&#039;Russian inside&#039;&#039;&#039; в базовой конфигурации!&lt;br /&gt;
&amp;lt;/div&amp;gt;&#039;&#039;&#039;Почему нельзя использовать готовые списки:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Готовые списки сообщества (например, Russian inside) включают домены YouTube, Google, Discord, которые &#039;&#039;&#039;уже обрабатываются Zapret&#039;&#039;&#039;. Если добавить их в Podkop, весь трафик этих сервисов будет принудительно направлен через VPN, что приведёт к:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Увеличению расхода VPN-трафика в десятки раз&#039;&#039;&#039; (YouTube потребляет гигабайты данных)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Смене региона&#039;&#039;&#039; на регион VPN-сервера (потеряете русскоязычный контент YouTube, региональные рекомендации)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Конфликту с Zapret&#039;&#039;&#039; (один и тот же домен будет обрабатываться дважды)&lt;br /&gt;
&lt;br /&gt;
=== Использование готового списка доменов ===&lt;br /&gt;
Для удобства мы подготовили &#039;&#039;&#039;готовый список доменов&#039;&#039;&#039; для Podkop, который:&lt;br /&gt;
&lt;br /&gt;
* Основан на актуальном списке &#039;&#039;&#039;Russian inside&#039;&#039;&#039; (&amp;lt;code&amp;gt;inside-raw.lst&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Исключает&#039;&#039;&#039; все домены из &amp;lt;code&amp;gt;zapret-hosts-google.txt&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;zapret-hosts-user.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Дополнен&#039;&#039;&#039; специализированными доменами (торрент-трекеры, облачные сервисы, AI-инструменты и др.)&lt;br /&gt;
&lt;br /&gt;
==== Шаг 1: Скопируйте список доменов ====&lt;br /&gt;
&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%; overflow:auto;&amp;quot;&amp;gt;&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Список доменов для Podkop (нажмите, чтобы развернуть)&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
.ua&lt;br /&gt;
10minutemail.com&lt;br /&gt;
1337x.to&lt;br /&gt;
1fichier.com&lt;br /&gt;
24.kg&lt;br /&gt;
4freerussia.org&lt;br /&gt;
4pda.to&lt;br /&gt;
4pda.ws&lt;br /&gt;
4pna.com&lt;br /&gt;
529055.xyz&lt;br /&gt;
5sim.net&lt;br /&gt;
7dniv.rv.ua&lt;br /&gt;
7tv.app&lt;br /&gt;
7tv.io&lt;br /&gt;
9tv.co.il&lt;br /&gt;
a-13.1fichier.com&lt;br /&gt;
a-vrv.akamaized.net&lt;br /&gt;
abercrombie.com&lt;br /&gt;
abook-club.ru&lt;br /&gt;
academy.terrasoft.ua&lt;br /&gt;
activatica.org&lt;br /&gt;
adguard.com&lt;br /&gt;
adidas.com&lt;br /&gt;
adminforge.de&lt;br /&gt;
adobe.com&lt;br /&gt;
ads-twitter.com&lt;br /&gt;
adultmult.tv&lt;br /&gt;
agents.media&lt;br /&gt;
ahrefs.com&lt;br /&gt;
ai-chat.bsg.brave.com&lt;br /&gt;
ai.com&lt;br /&gt;
aircanada.com&lt;br /&gt;
akc.org&lt;br /&gt;
allegro.pl&lt;br /&gt;
alphacoders.com&lt;br /&gt;
alza.hu&lt;br /&gt;
amazfitwatchfaces.com&lt;br /&gt;
amdm.ru&lt;br /&gt;
amedia.site&lt;br /&gt;
amnezia.org&lt;br /&gt;
amplitude.com&lt;br /&gt;
amx.com&lt;br /&gt;
analog.com&lt;br /&gt;
andrevi.ch&lt;br /&gt;
anidub.com&lt;br /&gt;
anilib.me&lt;br /&gt;
anilibria.org&lt;br /&gt;
anilibria.top&lt;br /&gt;
anilibria.tv&lt;br /&gt;
anilibria.uno&lt;br /&gt;
anilibria.wtf&lt;br /&gt;
animaunt.org&lt;br /&gt;
anime-portal.su&lt;br /&gt;
anime1.best&lt;br /&gt;
animebest.org&lt;br /&gt;
animedia.tv&lt;br /&gt;
animego.org&lt;br /&gt;
animespirit.ru&lt;br /&gt;
anistar.org&lt;br /&gt;
anistars.ru&lt;br /&gt;
annas-archive.org&lt;br /&gt;
ansys.com&lt;br /&gt;
anthropic.com&lt;br /&gt;
any.do&lt;br /&gt;
aol.com&lt;br /&gt;
api.jetbrains.ai&lt;br /&gt;
api.service-kp.com&lt;br /&gt;
api.theins.info&lt;br /&gt;
api.themoviedb.org&lt;br /&gt;
apkmirror.com&lt;br /&gt;
app.zerossl.com&lt;br /&gt;
arbat.media&lt;br /&gt;
arc.net&lt;br /&gt;
archive.midnightchannel.net&lt;br /&gt;
archive.ph&lt;br /&gt;
archiveofourown.org&lt;br /&gt;
arduino.cc&lt;br /&gt;
as6723.net&lt;br /&gt;
assets.heroku.com&lt;br /&gt;
atlassian.com&lt;br /&gt;
atn.ua&lt;br /&gt;
att.com&lt;br /&gt;
attachments.f95zone.to&lt;br /&gt;
atv4.dnskp.cc&lt;br /&gt;
audiobookbay.lu&lt;br /&gt;
augmentcode.com&lt;br /&gt;
autodesk.com&lt;br /&gt;
avira.com&lt;br /&gt;
azathabar.com&lt;br /&gt;
azattyq.org&lt;br /&gt;
babook.org&lt;br /&gt;
baginya.org&lt;br /&gt;
baikal-journal.ru&lt;br /&gt;
bato.to&lt;br /&gt;
bbc.co.uk&lt;br /&gt;
bbc.com&lt;br /&gt;
bbci.co.uk&lt;br /&gt;
bcbits.com&lt;br /&gt;
bell-sw.com&lt;br /&gt;
bellingcat.com&lt;br /&gt;
bestbuy.com&lt;br /&gt;
bestchange.ru&lt;br /&gt;
bihus.info&lt;br /&gt;
bitdefender.com&lt;br /&gt;
bitnami.com&lt;br /&gt;
blackseanews.net&lt;br /&gt;
blinkshot.io&lt;br /&gt;
bluehost.com&lt;br /&gt;
booktracker.org&lt;br /&gt;
boominfo.org&lt;br /&gt;
booth.pm&lt;br /&gt;
booth.pximg.net&lt;br /&gt;
bosch.com&lt;br /&gt;
boschaftermarket.com&lt;br /&gt;
boschautoparts.com&lt;br /&gt;
botnadzor.org&lt;br /&gt;
brawlstarsgame.com&lt;br /&gt;
bricklink.com&lt;br /&gt;
broadcom.com&lt;br /&gt;
broncosportforum.com&lt;br /&gt;
bt4gprx.com&lt;br /&gt;
btdig.com&lt;br /&gt;
btod.com&lt;br /&gt;
buanzo.org&lt;br /&gt;
buf.build&lt;br /&gt;
bufferbloat.net&lt;br /&gt;
builds.parsec.app&lt;br /&gt;
buymeacoffee.com&lt;br /&gt;
byteoversea.com&lt;br /&gt;
canva.com&lt;br /&gt;
canva.dev&lt;br /&gt;
capacitorjs.com&lt;br /&gt;
capcut.com&lt;br /&gt;
carnegieendowment.org&lt;br /&gt;
carrefouruae.com&lt;br /&gt;
cats.com&lt;br /&gt;
cbilling.eu&lt;br /&gt;
cbilling.vip&lt;br /&gt;
cdn.web-platform.io&lt;br /&gt;
cdnbunny.org&lt;br /&gt;
cdninstagram.com&lt;br /&gt;
cdromance.org&lt;br /&gt;
cdw.com&lt;br /&gt;
censor.net&lt;br /&gt;
censortracker.org&lt;br /&gt;
chaos.com&lt;br /&gt;
chat.com&lt;br /&gt;
chat.openai.com.cdn.cloudflare.net&lt;br /&gt;
chatgpt.com&lt;br /&gt;
chaturbate.com&lt;br /&gt;
chenbro.com&lt;br /&gt;
cherta.media&lt;br /&gt;
chess.com&lt;br /&gt;
chesscomfiles.com&lt;br /&gt;
chub.ai&lt;br /&gt;
circlecrewpinkcrowd.com&lt;br /&gt;
cisco.com&lt;br /&gt;
cisecurity.org&lt;br /&gt;
citrix.com&lt;br /&gt;
clamav.net&lt;br /&gt;
clashofclans.com&lt;br /&gt;
clashroyaleapp.com&lt;br /&gt;
claude.ai&lt;br /&gt;
clevelandclinic.org&lt;br /&gt;
clickup.com&lt;br /&gt;
clip.opus.pro&lt;br /&gt;
cloudtorrents.com&lt;br /&gt;
cms-twdigitalassets.com&lt;br /&gt;
cnd2exp.online&lt;br /&gt;
cock.li&lt;br /&gt;
code.gist.build&lt;br /&gt;
codecguide.com&lt;br /&gt;
codeium.com&lt;br /&gt;
codelinaro.org&lt;br /&gt;
coingate.com&lt;br /&gt;
coinpayments.net&lt;br /&gt;
coinsbee.com&lt;br /&gt;
coldfilm.city&lt;br /&gt;
coldfilm.ink&lt;br /&gt;
coldfilm.xyz&lt;br /&gt;
colta.ru&lt;br /&gt;
community.sophos.com&lt;br /&gt;
connect.ngrok-agent.com&lt;br /&gt;
contabo.com&lt;br /&gt;
coomer.su&lt;br /&gt;
copilot.microsoft.com&lt;br /&gt;
corsair.com&lt;br /&gt;
coursera.org&lt;br /&gt;
cpu-monkey.com&lt;br /&gt;
credly.com&lt;br /&gt;
croxyproxy.com&lt;br /&gt;
crunchyroll.com&lt;br /&gt;
csskor.ill.in.ua&lt;br /&gt;
cub.red&lt;br /&gt;
currenttime.tv&lt;br /&gt;
cursorinfo.co.il&lt;br /&gt;
cvedetails.com&lt;br /&gt;
cyberghostvpn.com&lt;br /&gt;
cyxymu.info&lt;br /&gt;
daemon-tools.cc&lt;br /&gt;
dailylviv.com&lt;br /&gt;
dailymotion.com&lt;br /&gt;
danbooru.donmai.us&lt;br /&gt;
darthsternie.net&lt;br /&gt;
data-cdn.mbamupdates.com&lt;br /&gt;
data.cline.bot&lt;br /&gt;
deckbrew.xyz&lt;br /&gt;
decrypt.day&lt;br /&gt;
deepl.com&lt;br /&gt;
deepstatemap.live&lt;br /&gt;
deezer.com&lt;br /&gt;
delfi.lt&lt;br /&gt;
delfi.lv&lt;br /&gt;
delivery.2d.net.co&lt;br /&gt;
dell.com&lt;br /&gt;
dellcdn.com&lt;br /&gt;
depositphotos.com&lt;br /&gt;
designer.microsoft.com&lt;br /&gt;
designify.com&lt;br /&gt;
developer.nvidia.com&lt;br /&gt;
devexpress.com&lt;br /&gt;
deviantart.com&lt;br /&gt;
diabrowser.com&lt;br /&gt;
digash.live&lt;br /&gt;
digikey.com&lt;br /&gt;
digitalcontent.sky&lt;br /&gt;
digitalocean.com&lt;br /&gt;
discord.tools&lt;br /&gt;
discours.io&lt;br /&gt;
disctech.com&lt;br /&gt;
disneyplus.com&lt;br /&gt;
dlpsgame.com&lt;br /&gt;
docs.liquibase.com&lt;br /&gt;
document360.com&lt;br /&gt;
document360.io&lt;br /&gt;
dorama.live&lt;br /&gt;
doramalive.ru&lt;br /&gt;
doramy.club&lt;br /&gt;
dovod.online&lt;br /&gt;
download3.omnissa.com&lt;br /&gt;
doxa.team&lt;br /&gt;
dpidetector.org&lt;br /&gt;
dreamhost.com&lt;br /&gt;
dreamina.capcut.com&lt;br /&gt;
dub.sh&lt;br /&gt;
ducati.com&lt;br /&gt;
dumka.media&lt;br /&gt;
dw.com&lt;br /&gt;
dyson.com&lt;br /&gt;
e-hentai.org&lt;br /&gt;
e621.net&lt;br /&gt;
easydmarc.com&lt;br /&gt;
echofm.online&lt;br /&gt;
edemtv.me&lt;br /&gt;
editorx.com&lt;br /&gt;
edu-cisco.org&lt;br /&gt;
ef.com&lt;br /&gt;
ef.edu&lt;br /&gt;
eggertspiele.de&lt;br /&gt;
ej.ru&lt;br /&gt;
ekhokavkaza.com&lt;br /&gt;
element14.com&lt;br /&gt;
elements.envato.com&lt;br /&gt;
elevenlabs.io&lt;br /&gt;
elgato.com&lt;br /&gt;
eneba.com&lt;br /&gt;
epidemz.net.co&lt;br /&gt;
eporner.com&lt;br /&gt;
espreso.tv&lt;br /&gt;
etahub.com&lt;br /&gt;
etsy.com&lt;br /&gt;
euronews.com&lt;br /&gt;
euroradio.fm&lt;br /&gt;
eutrp.eu&lt;br /&gt;
everand.com&lt;br /&gt;
exchanger.bits.media&lt;br /&gt;
exler.ru&lt;br /&gt;
expandrive.com&lt;br /&gt;
expres.online&lt;br /&gt;
extremetech.com&lt;br /&gt;
f1.com&lt;br /&gt;
f95-zone.to&lt;br /&gt;
facebook.com&lt;br /&gt;
facebook.net&lt;br /&gt;
fansly.com&lt;br /&gt;
fast-torrent.club&lt;br /&gt;
fast.com&lt;br /&gt;
fastpic.org&lt;br /&gt;
fb.com&lt;br /&gt;
fbcdn.net&lt;br /&gt;
fbsbx.com&lt;br /&gt;
ficbook.net&lt;br /&gt;
filebin.net&lt;br /&gt;
filmitorrent.net&lt;br /&gt;
filmix.ac&lt;br /&gt;
filmix.biz&lt;br /&gt;
filmix.day&lt;br /&gt;
filmix.fan&lt;br /&gt;
filmix.fm&lt;br /&gt;
filmix.la&lt;br /&gt;
flibusta.is&lt;br /&gt;
flibusta.net&lt;br /&gt;
flipboard.com&lt;br /&gt;
flir.com&lt;br /&gt;
flir.eu&lt;br /&gt;
flisland.net&lt;br /&gt;
flourish.studio&lt;br /&gt;
fls.guru&lt;br /&gt;
fluke.com&lt;br /&gt;
flukenetworks.com&lt;br /&gt;
flyertalk.com&lt;br /&gt;
fn-volga.ru&lt;br /&gt;
fonge.org&lt;br /&gt;
footballapi.pulselive.com&lt;br /&gt;
force-user-content.com&lt;br /&gt;
force.com&lt;br /&gt;
forklog.com&lt;br /&gt;
formula1.com&lt;br /&gt;
fortanga.org&lt;br /&gt;
forum.netgate.com&lt;br /&gt;
forum.ru-board.com&lt;br /&gt;
foxnews.com&lt;br /&gt;
framer.com&lt;br /&gt;
freedomletters.org&lt;br /&gt;
freeimages.com&lt;br /&gt;
freemedia.io&lt;br /&gt;
freeones.com&lt;br /&gt;
fxnetworks.com&lt;br /&gt;
g2a.com&lt;br /&gt;
gagadget.com&lt;br /&gt;
gamedistribution.com&lt;br /&gt;
gamesrepack.com&lt;br /&gt;
gamestop.com&lt;br /&gt;
gaming.amazon.com&lt;br /&gt;
geforcenow.com&lt;br /&gt;
gelbooru.com&lt;br /&gt;
genspark.ai&lt;br /&gt;
geolocation.onetrust.com&lt;br /&gt;
germania.one&lt;br /&gt;
getoutline.com&lt;br /&gt;
getoutline.org&lt;br /&gt;
gfn.am&lt;br /&gt;
ghostrc.game.idtech.services&lt;br /&gt;
gifyu.com&lt;br /&gt;
git.new&lt;br /&gt;
glavred.info&lt;br /&gt;
glavred.net&lt;br /&gt;
global.fncstatic.com&lt;br /&gt;
global.platform.seconddinnertech.com&lt;br /&gt;
glpals.com&lt;br /&gt;
godaddy.com&lt;br /&gt;
gofile.io&lt;br /&gt;
gofundme.com&lt;br /&gt;
golosameriki.com&lt;br /&gt;
gonitro.com&lt;br /&gt;
goodreads.com&lt;br /&gt;
&lt;br /&gt;
gpsonextra.net&lt;br /&gt;
gpu-monkey.com&lt;br /&gt;
gql.twitch.tv&lt;br /&gt;
gr-assets.com&lt;br /&gt;
grafana.com&lt;br /&gt;
grani.ru&lt;br /&gt;
graty.me&lt;br /&gt;
graylog.org&lt;br /&gt;
grizzlysms.com&lt;br /&gt;
grok.com&lt;br /&gt;
groq.com&lt;br /&gt;
groupon.com&lt;br /&gt;
guilded.gg&lt;br /&gt;
gulagu.net&lt;br /&gt;
habr.com&lt;br /&gt;
hackernoon.com&lt;br /&gt;
hackmd.io&lt;br /&gt;
halooglasi.com&lt;br /&gt;
hashicorp.com&lt;br /&gt;
haydaygame.com&lt;br /&gt;
hbomax.com&lt;br /&gt;
hc-ping.com&lt;br /&gt;
hchk.io&lt;br /&gt;
hd-rezka.tv&lt;br /&gt;
hdkinoteatr.com&lt;br /&gt;
hdrezka.ac&lt;br /&gt;
hdrezka.ag&lt;br /&gt;
hdrezka.app&lt;br /&gt;
hdrezka.me&lt;br /&gt;
hdrezka.tech&lt;br /&gt;
hdrezka.tv&lt;br /&gt;
hdrzk.org&lt;br /&gt;
hdstudio.org&lt;br /&gt;
healthline.com&lt;br /&gt;
hentai-foundry.com&lt;br /&gt;
herokucdn.com&lt;br /&gt;
hetzner.com&lt;br /&gt;
hitomi.la&lt;br /&gt;
hollisterco.com&lt;br /&gt;
holod.media&lt;br /&gt;
home-connect.com&lt;br /&gt;
home.by.me&lt;br /&gt;
hostgator.com&lt;br /&gt;
hostinger.com&lt;br /&gt;
hotels.com&lt;br /&gt;
housebrand.com&lt;br /&gt;
hqporner.com&lt;br /&gt;
hromadske.ua&lt;br /&gt;
hs.fi&lt;br /&gt;
htmhell.dev&lt;br /&gt;
hume.ai&lt;br /&gt;
hybrid-analysis.com&lt;br /&gt;
i.sakh.com&lt;br /&gt;
ibm.com&lt;br /&gt;
ibytedtos.com&lt;br /&gt;
idelreal.org&lt;br /&gt;
iditelesombase.org&lt;br /&gt;
iedb.org&lt;br /&gt;
ig.me&lt;br /&gt;
ign.com&lt;br /&gt;
iherb.com&lt;br /&gt;
iichan.hk&lt;br /&gt;
ikea.com&lt;br /&gt;
ilook.tv&lt;br /&gt;
image.tmdb.org&lt;br /&gt;
imgburn.com&lt;br /&gt;
imgur.com&lt;br /&gt;
important-stories.com&lt;br /&gt;
indeed.com&lt;br /&gt;
indiehackers.com&lt;br /&gt;
infineon.com&lt;br /&gt;
insearch.site&lt;br /&gt;
instagram.com&lt;br /&gt;
install.launcher.omniverse.nvidia.com&lt;br /&gt;
intel.com&lt;br /&gt;
intel.de&lt;br /&gt;
intel.nl&lt;br /&gt;
intelix.sophos.com&lt;br /&gt;
interactivebrokers.co.uk&lt;br /&gt;
interfax.com.ua&lt;br /&gt;
internalfb.com&lt;br /&gt;
intuit.com&lt;br /&gt;
intuitibits.com&lt;br /&gt;
ionos.com&lt;br /&gt;
iptv.online&lt;br /&gt;
is.fi&lt;br /&gt;
istories.media&lt;br /&gt;
itch.io&lt;br /&gt;
itninja.com&lt;br /&gt;
itsmycity.ru&lt;br /&gt;
jamf.com&lt;br /&gt;
jellyfin.org&lt;br /&gt;
jetbrains.com&lt;br /&gt;
jetbrains.space&lt;br /&gt;
jetkvm.com&lt;br /&gt;
jskor.ill.in.ua&lt;br /&gt;
jut-su.net&lt;br /&gt;
jut.su&lt;br /&gt;
jw-russia.org&lt;br /&gt;
jw.org&lt;br /&gt;
kaktus.media&lt;br /&gt;
kaleido.ai&lt;br /&gt;
kamatera.com&lt;br /&gt;
kara.su&lt;br /&gt;
kasparov.ru&lt;br /&gt;
kavkaz-uzel.eu&lt;br /&gt;
kavkazr.com&lt;br /&gt;
kemono.su&lt;br /&gt;
keysight.com&lt;br /&gt;
kilo.ai&lt;br /&gt;
kilocode.ai&lt;br /&gt;
kino.pub&lt;br /&gt;
kinogo.ec&lt;br /&gt;
kinogo.la&lt;br /&gt;
kinogo.uk&lt;br /&gt;
kinopub.me&lt;br /&gt;
kinovod.net&lt;br /&gt;
kinozal.guru&lt;br /&gt;
kinozal.me&lt;br /&gt;
kinozal.tv&lt;br /&gt;
kinozaltv.life&lt;br /&gt;
klarna.com&lt;br /&gt;
kmail-lists.com&lt;br /&gt;
knaben.xyz&lt;br /&gt;
knews.kg&lt;br /&gt;
knowyourmeme.com&lt;br /&gt;
kolsar.org&lt;br /&gt;
kor.ill.in.ua&lt;br /&gt;
korrespondent.net&lt;br /&gt;
kovcheg.live&lt;br /&gt;
krymr.com&lt;br /&gt;
kupujemprodajem.com&lt;br /&gt;
kym-cdn.com&lt;br /&gt;
lambdalabs.com&lt;br /&gt;
lamcdn.net&lt;br /&gt;
langdock.com&lt;br /&gt;
lantern.io&lt;br /&gt;
latestmodapks.com&lt;br /&gt;
ldoceonline.com&lt;br /&gt;
le-production.tv&lt;br /&gt;
leafletjs.com&lt;br /&gt;
legalshield.com&lt;br /&gt;
lgeapi.com&lt;br /&gt;
lgthinq.com&lt;br /&gt;
lib.rus.ec&lt;br /&gt;
libgen.li&lt;br /&gt;
licdn.com&lt;br /&gt;
lidarr.audio&lt;br /&gt;
lifehacker.com&lt;br /&gt;
liga.net&lt;br /&gt;
lightning.ai&lt;br /&gt;
linear.app&lt;br /&gt;
lingq.com&lt;br /&gt;
linkedin.com&lt;br /&gt;
linktr.ee&lt;br /&gt;
livetv.sx&lt;br /&gt;
liveuamap.com&lt;br /&gt;
locals.md&lt;br /&gt;
lolz.guru&lt;br /&gt;
lostfilm.download&lt;br /&gt;
lostfilm.tv&lt;br /&gt;
lostfilm.tw&lt;br /&gt;
lostfilm.uno&lt;br /&gt;
lostfilmtv2.site&lt;br /&gt;
lu4.org&lt;br /&gt;
lucid.app&lt;br /&gt;
magaz.global&lt;br /&gt;
mail-api.proton.me&lt;br /&gt;
mailerlite.com&lt;br /&gt;
mailfence.com&lt;br /&gt;
mailinator.com&lt;br /&gt;
mailo.com&lt;br /&gt;
make.com&lt;br /&gt;
malwarebytes.com&lt;br /&gt;
mangadex.org&lt;br /&gt;
mangahub.ru&lt;br /&gt;
mangapark.net&lt;br /&gt;
manus.im&lt;br /&gt;
manybooks.net&lt;br /&gt;
manyvids.com&lt;br /&gt;
marvelsnap.com&lt;br /&gt;
mashable.com&lt;br /&gt;
mattermost.com&lt;br /&gt;
max.com&lt;br /&gt;
mbk-news.appspot.com&lt;br /&gt;
mediazona.ca&lt;br /&gt;
medicalnewstoday.com&lt;br /&gt;
medium.com&lt;br /&gt;
meduza.io&lt;br /&gt;
mega.nz&lt;br /&gt;
megapeer.ru&lt;br /&gt;
megapeer.vip&lt;br /&gt;
merezha.co&lt;br /&gt;
meta.com&lt;br /&gt;
metacritic.com&lt;br /&gt;
metal-archives.com&lt;br /&gt;
metla.press&lt;br /&gt;
metopera.org&lt;br /&gt;
middlewareinventory.com&lt;br /&gt;
mignews.com&lt;br /&gt;
mintmobile.com&lt;br /&gt;
miracleptr.wordpress.com&lt;br /&gt;
mirror.jellyfin.org&lt;br /&gt;
mixcloud.com&lt;br /&gt;
modrinth.com&lt;br /&gt;
mongodb.com&lt;br /&gt;
monolisa.dev&lt;br /&gt;
monoprice.com&lt;br /&gt;
more.fm&lt;br /&gt;
moscowtimes.ru&lt;br /&gt;
mouser.com&lt;br /&gt;
mouser.fi&lt;br /&gt;
mssg.me&lt;br /&gt;
mullvad.net&lt;br /&gt;
multporn.net&lt;br /&gt;
muscdn.com&lt;br /&gt;
musical.ly&lt;br /&gt;
musicbrainz.org&lt;br /&gt;
musixmatch.com&lt;br /&gt;
mw2.wiki&lt;br /&gt;
myanimelist.net&lt;br /&gt;
mydoramy.club&lt;br /&gt;
myheritage.com&lt;br /&gt;
myjetbrains.com&lt;br /&gt;
myparallels.com&lt;br /&gt;
myqrcode.com&lt;br /&gt;
navalny.com&lt;br /&gt;
nba.com&lt;br /&gt;
neformat.com.ua&lt;br /&gt;
neo4j.com&lt;br /&gt;
netacad.com&lt;br /&gt;
netapp.com&lt;br /&gt;
netflix.ca&lt;br /&gt;
netflix.com&lt;br /&gt;
netflix.net&lt;br /&gt;
netflixinvestor.com&lt;br /&gt;
netflixtechblog.com&lt;br /&gt;
netlify.com&lt;br /&gt;
networksolutions.com&lt;br /&gt;
new.abb.com&lt;br /&gt;
newark.com&lt;br /&gt;
newsroom.porsche.com&lt;br /&gt;
newsru.co.il&lt;br /&gt;
newsru.com&lt;br /&gt;
newtimes.ru&lt;br /&gt;
nexus-cdn.com&lt;br /&gt;
nfl.com&lt;br /&gt;
nflxext.com&lt;br /&gt;
nflximg.com&lt;br /&gt;
nflximg.net&lt;br /&gt;
nflxsearch.net&lt;br /&gt;
nflxso.net&lt;br /&gt;
nflxvideo.net&lt;br /&gt;
ngrok.com&lt;br /&gt;
nhentai.com&lt;br /&gt;
nhentai.net&lt;br /&gt;
nhl.com&lt;br /&gt;
nih.gov&lt;br /&gt;
nike.com&lt;br /&gt;
nippon.com&lt;br /&gt;
nitropdf.com&lt;br /&gt;
nnmclub.to&lt;br /&gt;
nnmstatic.win&lt;br /&gt;
nordaccount.com&lt;br /&gt;
nordcdn.com&lt;br /&gt;
nordvpn.com&lt;br /&gt;
notepad-plus-plus.org&lt;br /&gt;
notion-emojis.s3-us-west-2.amazonaws.com&lt;br /&gt;
notion-static.com&lt;br /&gt;
notion.com&lt;br /&gt;
notion.new&lt;br /&gt;
notion.site&lt;br /&gt;
notion.so&lt;br /&gt;
novaline.fm&lt;br /&gt;
novaya.no&lt;br /&gt;
novayagazeta.eu&lt;br /&gt;
novayagazeta.ru&lt;br /&gt;
novyny.live&lt;br /&gt;
ntc.party&lt;br /&gt;
ntp.msn.com&lt;br /&gt;
nude-moon.org&lt;br /&gt;
nxp.com&lt;br /&gt;
oaistatic.com&lt;br /&gt;
oaiusercontent.com&lt;br /&gt;
obozrevatel.com&lt;br /&gt;
ocstore.com&lt;br /&gt;
oculus.com&lt;br /&gt;
ohmyswift.ru&lt;br /&gt;
oi.legal&lt;br /&gt;
okx.com&lt;br /&gt;
olx.ua&lt;br /&gt;
omnissa.com&lt;br /&gt;
omv-extras.org&lt;br /&gt;
onfastspring.com&lt;br /&gt;
onlinesim.io&lt;br /&gt;
onshape.com&lt;br /&gt;
open-vsx.org&lt;br /&gt;
openai.com&lt;br /&gt;
openh264.org&lt;br /&gt;
openmedia.io&lt;br /&gt;
openrouter.ai&lt;br /&gt;
opensanctions.org&lt;br /&gt;
opensea.io&lt;br /&gt;
opentrackr.org&lt;br /&gt;
openvsx.eclipsecontent.org&lt;br /&gt;
opposition-news.com&lt;br /&gt;
oracle.com&lt;br /&gt;
orbispatches.com&lt;br /&gt;
orcaslicer.com&lt;br /&gt;
ottplayer.tv&lt;br /&gt;
ovd.info&lt;br /&gt;
ovd.legal&lt;br /&gt;
ovd.news&lt;br /&gt;
ovdinfo.org&lt;br /&gt;
ozodi.org&lt;br /&gt;
paddle.com&lt;br /&gt;
paddlestatus.com&lt;br /&gt;
pages.dev&lt;br /&gt;
pandasecurity.com&lt;br /&gt;
pap.pl&lt;br /&gt;
paperpaper.io&lt;br /&gt;
paperpaper.ru&lt;br /&gt;
parallels.cn&lt;br /&gt;
parallels.com&lt;br /&gt;
parallels.net&lt;br /&gt;
parallelsaccess.com&lt;br /&gt;
paritydeals.com&lt;br /&gt;
patreon.com&lt;br /&gt;
patreonusercontent.com&lt;br /&gt;
patriot.dp.ua&lt;br /&gt;
pb.wtf&lt;br /&gt;
pcgamesn.com&lt;br /&gt;
pcmag.com&lt;br /&gt;
penguin.com&lt;br /&gt;
penguinrandomhouse.com&lt;br /&gt;
periscope.tv&lt;br /&gt;
pexels.com&lt;br /&gt;
phncdn.com&lt;br /&gt;
phncdn.com.sds.rncdn7.com&lt;br /&gt;
pimpletv.ru&lt;br /&gt;
pingdom.com&lt;br /&gt;
piratbit.top&lt;br /&gt;
pixabay.com&lt;br /&gt;
pkg-zone.com&lt;br /&gt;
pkgs.tailscale.com&lt;br /&gt;
platform.activestate.com&lt;br /&gt;
playboy.com&lt;br /&gt;
plugshare.com&lt;br /&gt;
polit.ru&lt;br /&gt;
politico.eu&lt;br /&gt;
politiken.dk&lt;br /&gt;
polymarket.com&lt;br /&gt;
pornhub.com&lt;br /&gt;
pornhub.org&lt;br /&gt;
pornolab.net&lt;br /&gt;
portal.lviv.ua&lt;br /&gt;
posle.media&lt;br /&gt;
posthog.com&lt;br /&gt;
postimees.ee&lt;br /&gt;
pravda.com&lt;br /&gt;
pravda.com.ua&lt;br /&gt;
premierleague.com&lt;br /&gt;
primevideo.com&lt;br /&gt;
primevue.org&lt;br /&gt;
privatekeys.pw&lt;br /&gt;
prnt.sc&lt;br /&gt;
production-openaicom-storage.azureedge.net&lt;br /&gt;
proekt.media&lt;br /&gt;
profitwell.com&lt;br /&gt;
prosleduetmedia.com&lt;br /&gt;
prosperopatches.com&lt;br /&gt;
prostovpn.org&lt;br /&gt;
proton.me&lt;br /&gt;
protonvpn.com&lt;br /&gt;
provereno.media&lt;br /&gt;
prowlarr.com&lt;br /&gt;
pscp.tv&lt;br /&gt;
psiphon.ca&lt;br /&gt;
psxhax.com&lt;br /&gt;
public.parsec.app&lt;br /&gt;
pvpessence.com&lt;br /&gt;
qdrant.io&lt;br /&gt;
qdrant.tech&lt;br /&gt;
qt.io&lt;br /&gt;
quadratichq.com&lt;br /&gt;
qualcomm.com&lt;br /&gt;
quicknode.com&lt;br /&gt;
quora.com&lt;br /&gt;
quora.com.cdn.cloudflare.net&lt;br /&gt;
qwant.com&lt;br /&gt;
r4.err.ee&lt;br /&gt;
radiojar.com&lt;br /&gt;
radiosakharov.org&lt;br /&gt;
radiosvoboda.org&lt;br /&gt;
rbc.ua&lt;br /&gt;
reactflow.dev&lt;br /&gt;
realbooru.com&lt;br /&gt;
realist.online&lt;br /&gt;
recraft.ai&lt;br /&gt;
reddxxx.com&lt;br /&gt;
redgifs.com&lt;br /&gt;
redis.io&lt;br /&gt;
redislabs.com&lt;br /&gt;
redshieldvpn.com&lt;br /&gt;
refactoring.guru&lt;br /&gt;
remna.st&lt;br /&gt;
remove.bg&lt;br /&gt;
render-state.to&lt;br /&gt;
rentry.co&lt;br /&gt;
rentry.org&lt;br /&gt;
repo.jellyfin.org&lt;br /&gt;
republic.ru&lt;br /&gt;
research.net&lt;br /&gt;
restream.io&lt;br /&gt;
reve.art&lt;br /&gt;
rezka-ua.in&lt;br /&gt;
rezka.ag&lt;br /&gt;
rezka.cc&lt;br /&gt;
rezka.fi&lt;br /&gt;
rezka.land&lt;br /&gt;
rezka.my&lt;br /&gt;
rezka.tv&lt;br /&gt;
rezkify.com&lt;br /&gt;
rezonans.media&lt;br /&gt;
rf.dobrochan.net&lt;br /&gt;
rferl.org&lt;br /&gt;
rima.media&lt;br /&gt;
riperam.org&lt;br /&gt;
riseup.net&lt;br /&gt;
roar-review.com&lt;br /&gt;
root-nation.com&lt;br /&gt;
roskomsvoboda.org&lt;br /&gt;
rublacklist.net&lt;br /&gt;
rule34.art&lt;br /&gt;
rule34.us&lt;br /&gt;
rule34.xxx&lt;br /&gt;
rus.delfi.ee&lt;br /&gt;
rus.jauns.lv&lt;br /&gt;
ruscryde.net&lt;br /&gt;
rustorka.com&lt;br /&gt;
rutor.info&lt;br /&gt;
rutor.is&lt;br /&gt;
rutor.org&lt;br /&gt;
rutracker.cc&lt;br /&gt;
rutracker.net&lt;br /&gt;
rutracker.org&lt;br /&gt;
rutracker.wiki&lt;br /&gt;
sakhalin.info&lt;br /&gt;
sakharovfoundation.org&lt;br /&gt;
salesforce-experience.com&lt;br /&gt;
salesforce-hub.com&lt;br /&gt;
salesforce-scrt.com&lt;br /&gt;
salesforce-setup.com&lt;br /&gt;
salesforce-sites.com&lt;br /&gt;
salesforce.com&lt;br /&gt;
salesforceiq.com&lt;br /&gt;
salesforceliveagent.com&lt;br /&gt;
sap.com&lt;br /&gt;
saverudata.net&lt;br /&gt;
schemas.microsoft.com&lt;br /&gt;
schemas.openxmlformats.org&lt;br /&gt;
sci-hub.se&lt;br /&gt;
sci-hub.st&lt;br /&gt;
scrollrevealjs.org&lt;br /&gt;
scryde.io&lt;br /&gt;
scryde.net&lt;br /&gt;
scryde.ru&lt;br /&gt;
scryde.world&lt;br /&gt;
scryde1.net&lt;br /&gt;
scryde10.net&lt;br /&gt;
scryde11.net&lt;br /&gt;
scryde12.net&lt;br /&gt;
scryde2.net&lt;br /&gt;
scryde3.net&lt;br /&gt;
scryde4.net&lt;br /&gt;
scryde5.net&lt;br /&gt;
scryde6.net&lt;br /&gt;
scryde7.net&lt;br /&gt;
scryde8.net&lt;br /&gt;
scryde9.net&lt;br /&gt;
sdxcentral.com&lt;br /&gt;
searchfloor.org&lt;br /&gt;
seasonvar.ru&lt;br /&gt;
selezen.org&lt;br /&gt;
semnasem.org&lt;br /&gt;
semrush.com&lt;br /&gt;
sentry.dev&lt;br /&gt;
sentry.io&lt;br /&gt;
sephora.com&lt;br /&gt;
servarr.com&lt;br /&gt;
severreal.org&lt;br /&gt;
sfdcopens.com&lt;br /&gt;
shikimori.me&lt;br /&gt;
shinyhardware.co.uk&lt;br /&gt;
shiza-project.com&lt;br /&gt;
shop.gameloft.com&lt;br /&gt;
showip.net&lt;br /&gt;
sibreal.org&lt;br /&gt;
signal.org&lt;br /&gt;
simplex.chat&lt;br /&gt;
simplex.im&lt;br /&gt;
simplix.info&lt;br /&gt;
singlekey-id.com&lt;br /&gt;
site.com&lt;br /&gt;
siteground.com&lt;br /&gt;
skat.media&lt;br /&gt;
sketchup.com&lt;br /&gt;
skiff.com&lt;br /&gt;
skladchik.com&lt;br /&gt;
sklatchiki.ru&lt;br /&gt;
sky.com&lt;br /&gt;
skycdp.com&lt;br /&gt;
slashlib.me&lt;br /&gt;
slavicsac.com&lt;br /&gt;
slifki.biz&lt;br /&gt;
smartbear.co&lt;br /&gt;
smartbear.com&lt;br /&gt;
smartdeploy.com&lt;br /&gt;
sms-activate.io&lt;br /&gt;
snapgene.com&lt;br /&gt;
sndcdn.com&lt;br /&gt;
snort.org&lt;br /&gt;
snyk.io&lt;br /&gt;
sobesednik.com&lt;br /&gt;
solarwinds.com&lt;br /&gt;
sonara.ai&lt;br /&gt;
sophos.com&lt;br /&gt;
sora.com&lt;br /&gt;
sotaproject.com&lt;br /&gt;
soundcloud.cloud&lt;br /&gt;
soundcloud.com&lt;br /&gt;
sovetromantica.com&lt;br /&gt;
spacelift.io&lt;br /&gt;
speedtest.net&lt;br /&gt;
spektr.press&lt;br /&gt;
spiceworks.com&lt;br /&gt;
spiegel.de&lt;br /&gt;
spitfireaudio.com&lt;br /&gt;
spotify.com&lt;br /&gt;
spreadthesign.com&lt;br /&gt;
sputnikipogrom.com&lt;br /&gt;
squadbustersgame.com&lt;br /&gt;
squareup.com&lt;br /&gt;
squietpc.com&lt;br /&gt;
startmail.com&lt;br /&gt;
startpage.com&lt;br /&gt;
static.lostfilm.top&lt;br /&gt;
statology.org&lt;br /&gt;
steamstat.info&lt;br /&gt;
strana.news&lt;br /&gt;
strana.today&lt;br /&gt;
strava.com&lt;br /&gt;
&lt;br /&gt;
supercell.com&lt;br /&gt;
supersliv.biz&lt;br /&gt;
support.anydesk.com&lt;br /&gt;
support.xerox.com&lt;br /&gt;
surfshark.com&lt;br /&gt;
surveymonkey.com&lt;br /&gt;
suspilne.media&lt;br /&gt;
svidomi.in.ua&lt;br /&gt;
svoboda.org&lt;br /&gt;
svoi.kr.ua&lt;br /&gt;
svtv.org&lt;br /&gt;
swagger.io&lt;br /&gt;
swapd.co&lt;br /&gt;
swissinfo.ch&lt;br /&gt;
synoforum.com&lt;br /&gt;
t-invariant.org&lt;br /&gt;
t.co&lt;br /&gt;
tableau.com&lt;br /&gt;
talosintelligence.com&lt;br /&gt;
tampermonkey.net&lt;br /&gt;
tayga.info&lt;br /&gt;
te-st.org&lt;br /&gt;
teamviewer.com&lt;br /&gt;
techbargains.com&lt;br /&gt;
telegraf.by&lt;br /&gt;
telegraf.news&lt;br /&gt;
telegram.org&lt;br /&gt;
telegraph.co.uk&lt;br /&gt;
telemetr.io&lt;br /&gt;
tellapart.com&lt;br /&gt;
tempmail.plus&lt;br /&gt;
temu.com&lt;br /&gt;
terraform.io&lt;br /&gt;
the-village.ru&lt;br /&gt;
theaudiodb.com&lt;br /&gt;
thebarentsobserver.com&lt;br /&gt;
thebell.io&lt;br /&gt;
theins.press&lt;br /&gt;
theins.ru&lt;br /&gt;
themoviedb.org&lt;br /&gt;
thepiratebay.org&lt;br /&gt;
theporndude.com&lt;br /&gt;
therarbg.to&lt;br /&gt;
thetruestory.news&lt;br /&gt;
thetvdb.com&lt;br /&gt;
threads.net&lt;br /&gt;
threema.ch&lt;br /&gt;
ti.com&lt;br /&gt;
tidal.com&lt;br /&gt;
tik-tokapi.com&lt;br /&gt;
tiktok.com&lt;br /&gt;
tiktokcdn-eu.com&lt;br /&gt;
tiktokcdn-us.com&lt;br /&gt;
tiktokcdn.com&lt;br /&gt;
tiktokd.net&lt;br /&gt;
tiktokd.org&lt;br /&gt;
tiktokv.com&lt;br /&gt;
tiktokv.eu&lt;br /&gt;
tiktokv.us&lt;br /&gt;
tiktokw.us&lt;br /&gt;
timberland.de&lt;br /&gt;
tiptop-vpn.com&lt;br /&gt;
tmdb-image-prod.b-cdn.net&lt;br /&gt;
tmdb.com&lt;br /&gt;
tmdb.org&lt;br /&gt;
toolbox.app&lt;br /&gt;
torproject.org&lt;br /&gt;
torrenteditor.com&lt;br /&gt;
torrentgalaxy.to&lt;br /&gt;
trae.ai&lt;br /&gt;
trailblazer.me&lt;br /&gt;
trailhead.com&lt;br /&gt;
transferwise.com&lt;br /&gt;
trellix.com&lt;br /&gt;
tria.ge&lt;br /&gt;
trueblackmetalradio.com&lt;br /&gt;
truthsocial.com&lt;br /&gt;
tsmc.com&lt;br /&gt;
ttwstatic.com&lt;br /&gt;
turbobit.net&lt;br /&gt;
tuta.com&lt;br /&gt;
tuta.io&lt;br /&gt;
tutanota.com&lt;br /&gt;
tvdevinfo.com&lt;br /&gt;
tvfreedom.io&lt;br /&gt;
tvrain.ru&lt;br /&gt;
tvrain.tv&lt;br /&gt;
tweetdeck.com&lt;br /&gt;
twimg.com&lt;br /&gt;
twin.me&lt;br /&gt;
twinlife-systems.com&lt;br /&gt;
twinme.com&lt;br /&gt;
twirpx.com&lt;br /&gt;
twitpic.com&lt;br /&gt;
twitter.biz&lt;br /&gt;
twitter.com&lt;br /&gt;
twitter.jp&lt;br /&gt;
twittercommunity.com&lt;br /&gt;
twitterflightschool.com&lt;br /&gt;
twitterinc.com&lt;br /&gt;
twitteroauth.com&lt;br /&gt;
twitterstat.us&lt;br /&gt;
twtrdns.net&lt;br /&gt;
twttr.com&lt;br /&gt;
twttr.net&lt;br /&gt;
twvid.com&lt;br /&gt;
typing.com&lt;br /&gt;
uaudio.com&lt;br /&gt;
ui.ill.in.ua&lt;br /&gt;
uizard.io&lt;br /&gt;
ukdevilz.com&lt;br /&gt;
ukr.net&lt;br /&gt;
ukr.radio&lt;br /&gt;
ukrtelcdn.net&lt;br /&gt;
unian.ua&lt;br /&gt;
unscreen.com&lt;br /&gt;
unsplash.com&lt;br /&gt;
upwork.com&lt;br /&gt;
urlr.me&lt;br /&gt;
usa.one&lt;br /&gt;
usatoday.com&lt;br /&gt;
usher.ttvnw.net&lt;br /&gt;
uupdump.net&lt;br /&gt;
v.vrv.co&lt;br /&gt;
vagrantcloud.com&lt;br /&gt;
veeam.com&lt;br /&gt;
verstka.media&lt;br /&gt;
vesma.one&lt;br /&gt;
vesma.today&lt;br /&gt;
vesty.co.il&lt;br /&gt;
viber.com&lt;br /&gt;
vice.com&lt;br /&gt;
vine.co&lt;br /&gt;
vip110.vip&lt;br /&gt;
vipergirls.to&lt;br /&gt;
visualcapitalist.com&lt;br /&gt;
vmware.com&lt;br /&gt;
vndb.org&lt;br /&gt;
voanews.com&lt;br /&gt;
vod-fy.crunchyrollcdn.com&lt;br /&gt;
voidboost.cc&lt;br /&gt;
volkswagen-classic-parts.com&lt;br /&gt;
vot-tak.tv&lt;br /&gt;
vpngate.net&lt;br /&gt;
vpngen.org&lt;br /&gt;
vpnlove.me&lt;br /&gt;
vpnpay.io&lt;br /&gt;
vyos.io&lt;br /&gt;
w.atwiki.jp&lt;br /&gt;
wa.me&lt;br /&gt;
walmart.com&lt;br /&gt;
watchguard.com&lt;br /&gt;
watermarkremover.io&lt;br /&gt;
wbgames.com&lt;br /&gt;
weather.com&lt;br /&gt;
webmd.com&lt;br /&gt;
webnames.ca&lt;br /&gt;
webtoons.com&lt;br /&gt;
weebly.com&lt;br /&gt;
welt.de&lt;br /&gt;
whatsapp.biz&lt;br /&gt;
whatsapp.com&lt;br /&gt;
whatsapp.net&lt;br /&gt;
widgetapp.stream&lt;br /&gt;
wiki.fextralife.com&lt;br /&gt;
wikidot.com&lt;br /&gt;
wilsoncenter.org&lt;br /&gt;
windows10spotlight.com&lt;br /&gt;
windsurf.com&lt;br /&gt;
wise.com&lt;br /&gt;
wonderzine.com&lt;br /&gt;
wpengine.com&lt;br /&gt;
wunderground.com&lt;br /&gt;
www3.corsair.com&lt;br /&gt;
x.ai&lt;br /&gt;
x.com&lt;br /&gt;
xhamster.com&lt;br /&gt;
xhamsterlive.com&lt;br /&gt;
xhcdn.com&lt;br /&gt;
xiaomi.eu&lt;br /&gt;
xnxx-cdn.com&lt;br /&gt;
xnxx-ru.com&lt;br /&gt;
xnxx.com&lt;br /&gt;
xnxx.net&lt;br /&gt;
xnxx.tv&lt;br /&gt;
xnxx3.com&lt;br /&gt;
xsts.auth.xboxlive.com&lt;br /&gt;
xtracloud.net&lt;br /&gt;
xv-ru.com&lt;br /&gt;
xvideos-cdn.com&lt;br /&gt;
xvideos.com&lt;br /&gt;
yande.re&lt;br /&gt;
yeggi.com&lt;br /&gt;
yle.fi&lt;br /&gt;
youtrack.cloud&lt;br /&gt;
yummyani.me&lt;br /&gt;
z.ai&lt;br /&gt;
zahav.ru&lt;br /&gt;
zapier.com&lt;br /&gt;
zaxid.net&lt;br /&gt;
zbigz.com&lt;br /&gt;
zedge.net&lt;br /&gt;
zendesk.com&lt;br /&gt;
zerkalo.io&lt;br /&gt;
zona.media&lt;br /&gt;
2fa.fb.tools&lt;br /&gt;
pytorch.org&lt;br /&gt;
rutracker.cc&lt;br /&gt;
uvnc.com&lt;br /&gt;
api.pwnedpasswords.com&lt;br /&gt;
pwnedpasswords.com&lt;br /&gt;
haveibeenpwned.com&lt;br /&gt;
nekoray.org&lt;br /&gt;
cursor.directory&lt;br /&gt;
fedoraproject.org&lt;br /&gt;
jdownloader.org&lt;br /&gt;
oii.la&lt;br /&gt;
sysdig.com&lt;br /&gt;
lizardbyte.dev&lt;br /&gt;
lego.com&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
google.com&lt;br /&gt;
lookerstudio.google.com&lt;br /&gt;
news.google.com&lt;br /&gt;
googleapis.com&lt;br /&gt;
suggestqueries.google.com&lt;br /&gt;
gemini.google.com&lt;br /&gt;
&lt;br /&gt;
2ip.ru&lt;br /&gt;
cline.bot&lt;br /&gt;
fragment.com&lt;br /&gt;
shikimori.one&lt;br /&gt;
thingiverse.com&lt;br /&gt;
rippersanime.info&lt;br /&gt;
forum.rippersanime.info&lt;br /&gt;
weloma.art&lt;br /&gt;
brave.com&lt;br /&gt;
search.brave.com&lt;br /&gt;
claude.com&lt;br /&gt;
roocode.com&lt;br /&gt;
unsloth.ai&lt;br /&gt;
sourceforge.net&lt;br /&gt;
trakt.tv&lt;br /&gt;
&lt;br /&gt;
statics.life&lt;br /&gt;
openstat.net&lt;br /&gt;
google-analytics.com&lt;br /&gt;
googletagmanager.com&lt;br /&gt;
torrenttip220.top&lt;br /&gt;
529072.xyz&lt;br /&gt;
asnet.pw&lt;br /&gt;
apachetorrent.com&lt;br /&gt;
bangumi.moe&lt;br /&gt;
bigfangroup.org&lt;br /&gt;
bitru.org&lt;br /&gt;
bitsearch.to&lt;br /&gt;
demonoid.nl&lt;br /&gt;
share.dmhy.org&lt;br /&gt;
extratorrent.st&lt;br /&gt;
eztvx.to&lt;br /&gt;
limontorrents.vip&lt;br /&gt;
www.frozen-layer.com&lt;br /&gt;
kickass.torrentbay.st&lt;br /&gt;
kickass.ws&lt;br /&gt;
limetorrents.fun&lt;br /&gt;
magnetcatcat.com&lt;br /&gt;
newstudio.tv&lt;br /&gt;
ww3-nortorrent.com&lt;br /&gt;
nyaa.si&lt;br /&gt;
torrentcore.xyz&lt;br /&gt;
torrenttip220.top&lt;br /&gt;
dontorrent.cash&lt;br /&gt;
electro-torrent.pl&lt;br /&gt;
extratorrent.ninjaproxy1.com&lt;br /&gt;
extratorrent.proxyninja.org&lt;br /&gt;
extratorrent.proxyninja.net&lt;br /&gt;
hdrtorrent.com&lt;br /&gt;
ilcorsaronero.link&lt;br /&gt;
magnetcatcat.com&lt;br /&gt;
clmclm.com&lt;br /&gt;
8800546.xyz&lt;br /&gt;
torrentbay.st&lt;br /&gt;
torrentsbay.org&lt;br /&gt;
unblockninja.com&lt;br /&gt;
ninjaproxy1.com&lt;br /&gt;
proxyninja.org&lt;br /&gt;
proxyninja.net&lt;br /&gt;
newstudio.tv&lt;br /&gt;
nekobt.to&lt;br /&gt;
moviesdvdr.co&lt;br /&gt;
nyaa.iss.ink&lt;br /&gt;
nyaa.land&lt;br /&gt;
nyaa.mom&lt;br /&gt;
nyaa.unblockninja.com&lt;br /&gt;
zktorrent.com&lt;br /&gt;
zamunda.rip&lt;br /&gt;
yts.bz&lt;br /&gt;
yts.unblockninja.com&lt;br /&gt;
yts.ninjaproxy1.com&lt;br /&gt;
yts.proxyninja.org&lt;br /&gt;
yts.proxyninja.net&lt;br /&gt;
yts.torrentbay.st&lt;br /&gt;
yts.torrentsbay.org&lt;br /&gt;
mania-torrent.org&lt;br /&gt;
cine-torrent.org&lt;br /&gt;
magnet-torrent.org&lt;br /&gt;
mega-torrent.cc&lt;br /&gt;
zone-torrent.com&lt;br /&gt;
warez-torrent.org&lt;br /&gt;
wolfmax4k.com&lt;br /&gt;
uztracker.net&lt;br /&gt;
uindex.org&lt;br /&gt;
ygg.gratis&lt;br /&gt;
torrenttip219.top&lt;br /&gt;
torrentsome238.com&lt;br /&gt;
torrentqq412.com&lt;br /&gt;
torrentegg87.com&lt;br /&gt;
torrentgalaxy.one&lt;br /&gt;
torrentgalaxy.info&lt;br /&gt;
torrentgalaxy.space&lt;br /&gt;
torrentdownload.info&lt;br /&gt;
torrentcore.xyz&lt;br /&gt;
www5.torrent9.to&lt;br /&gt;
www.torrentdownloads.pro&lt;br /&gt;
torrentdownloads.unblockninja.com&lt;br /&gt;
torrentdownloads.ninjaproxy1.com&lt;br /&gt;
torrentdownloads.proxyninja.org&lt;br /&gt;
torrentdownloads.proxyninja.net&lt;br /&gt;
therarbg.to&lt;br /&gt;
therarbg.com&lt;br /&gt;
therar.site&lt;br /&gt;
rarbg.unblockninja.com&lt;br /&gt;
rarbg.ninjaproxy1.com&lt;br /&gt;
rarbg.proxyninja.org&lt;br /&gt;
rarbg.proxyninja.net&lt;br /&gt;
rarbg.torrentbay.st&lt;br /&gt;
thepiratebay.org&lt;br /&gt;
solidtorrents.eu&lt;br /&gt;
kickasstorrents.bz&lt;br /&gt;
dolby.com&lt;br /&gt;
repo.webosbrew.org&lt;br /&gt;
qt.io&lt;br /&gt;
cursor.com&lt;br /&gt;
cursor.sh&lt;br /&gt;
cursorapi.com&lt;br /&gt;
duckdns.org&lt;br /&gt;
funpay.com&lt;br /&gt;
anidub.vip&lt;br /&gt;
anidub.shop&lt;br /&gt;
skillsmp.com&lt;br /&gt;
reactbits.dev&lt;br /&gt;
mediaarea.net&lt;br /&gt;
mkvtoolnix.download&lt;br /&gt;
excalidraw.com&lt;br /&gt;
claudeusercontent.com&lt;br /&gt;
kodi.tv&lt;br /&gt;
githubusercontent.com&lt;br /&gt;
qbittorrent.org&lt;br /&gt;
audioz.download&lt;br /&gt;
ishosting.com&lt;br /&gt;
netgrid.host&lt;br /&gt;
infomaniak.com&lt;br /&gt;
endcore.cloud&lt;br /&gt;
breakfastquay.com&lt;br /&gt;
pythonhosted.org&lt;br /&gt;
pypi.org&lt;br /&gt;
fbaipublicfiles.com&lt;br /&gt;
he.net&lt;br /&gt;
googleadservices.com&lt;br /&gt;
overwolf.com&lt;br /&gt;
shields.io&lt;br /&gt;
curseforge.com&lt;br /&gt;
bisecthosting.com&lt;br /&gt;
forgecdn.net&lt;br /&gt;
freshworks.com&lt;br /&gt;
digitaloceanspaces.com&lt;br /&gt;
adriver.ru&lt;br /&gt;
arxiv.org&lt;br /&gt;
thecvf.com&lt;br /&gt;
openreview.net&lt;br /&gt;
paperswithcode.com&lt;br /&gt;
meta.com&lt;br /&gt;
facebook.com&lt;br /&gt;
semanticscholar.org&lt;br /&gt;
neurips.cc&lt;br /&gt;
mlr.press&lt;br /&gt;
acm.org&lt;br /&gt;
ieee.org&lt;br /&gt;
researchhub.blog&lt;br /&gt;
uvnc.eu&lt;br /&gt;
iedem.tv&lt;br /&gt;
designingsound.org&lt;br /&gt;
pypi.org&lt;br /&gt;
yani.tv&lt;br /&gt;
animetop.info&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Шаг 2: Добавьте список в Podkop ====&lt;br /&gt;
&lt;br /&gt;
# Откройте веб-интерфейс Podkop: &#039;&#039;&#039;Services → Podkop&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Перейдите в раздел конфигурации секции (обычно &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
# Найдите параметр &#039;&#039;&#039;Custom Domain Lists&#039;&#039;&#039; (Список пользовательских доменов)&lt;br /&gt;
&lt;br /&gt;
# Убедитесь, что &#039;&#039;&#039;Тип пользовательского списка доменов&#039;&#039;&#039; установлен в &amp;lt;code&amp;gt;Текстовый список&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Скопируйте весь список из спойлера выше&lt;br /&gt;
&lt;br /&gt;
# Вставьте его в поле &#039;&#039;&#039;Список пользовательских доменов&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Нажмите &#039;&#039;&#039;Save &amp;amp; Apply&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Самостоятельная генерация списка (опционально) ===&lt;br /&gt;
Если вы хотите &#039;&#039;&#039;самостоятельно сгенерировать&#039;&#039;&#039; актуальный список или добавить свои домены, используйте наш скрипт.&lt;br /&gt;
&lt;br /&gt;
==== Требования ====&lt;br /&gt;
&lt;br /&gt;
* Python 3.6 или выше&lt;br /&gt;
&lt;br /&gt;
* 4 файла:&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;inside-raw.lst&amp;lt;/code&amp;gt; — актуальный список Russian inside&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;zapret-hosts-google.txt&amp;lt;/code&amp;gt; — скопируйте с роутера из &amp;lt;code&amp;gt;/opt/zapret/ipset/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;zapret-hosts-user.txt&amp;lt;/code&amp;gt; — скопируйте с роутера из &amp;lt;code&amp;gt;/opt/zapret/ipset/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;lt;code&amp;gt;custom-user-list.txt&amp;lt;/code&amp;gt; — ваши дополнительные домены (по одному на строку)&lt;br /&gt;
&lt;br /&gt;
==== Исходный код скрипта ====&lt;br /&gt;
Создайте файл &amp;lt;code&amp;gt;generate_podkop_list.py&amp;lt;/code&amp;gt;:&amp;lt;div class=&amp;quot;toccolours mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%; overflow:auto;&amp;quot;&amp;gt;&amp;lt;div style=&amp;quot;font-weight:bold;line-height:1.6;&amp;quot;&amp;gt;Список доменов для Podkop (нажмите, чтобы развернуть)&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Domain List Generator for Podkop&lt;br /&gt;
&lt;br /&gt;
Merges inside-raw.lst with custom domains while removing duplicates from Zapret lists&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
import sys&lt;br /&gt;
&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
def read_domains(filepath):&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Read domains from file, cleaning and normalizing them&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    domains = set()&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
&lt;br /&gt;
        with open(filepath, &#039;r&#039;, encoding=&#039;utf-8&#039;) as f:&lt;br /&gt;
&lt;br /&gt;
            for line in f:&lt;br /&gt;
&lt;br /&gt;
                # Remove comments, whitespace, and normalize&lt;br /&gt;
&lt;br /&gt;
                domain = line.split(&#039;#&#039;)[0].strip().lower()&lt;br /&gt;
&lt;br /&gt;
                if domain and not domain.startswith(&#039;[&#039;):&lt;br /&gt;
&lt;br /&gt;
                    # Remove trailing dots&lt;br /&gt;
&lt;br /&gt;
                    domain = domain.rstrip(&#039;.&#039;)&lt;br /&gt;
&lt;br /&gt;
                    domains.add(domain)&lt;br /&gt;
&lt;br /&gt;
    except FileNotFoundError:&lt;br /&gt;
&lt;br /&gt;
        print(f&amp;quot;Warning: File {filepath} not found, skipping&amp;quot;, file=sys.stderr)&lt;br /&gt;
&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
&lt;br /&gt;
        print(f&amp;quot;Error reading {filepath}: {e}&amp;quot;, file=sys.stderr)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    return domains&lt;br /&gt;
&lt;br /&gt;
def main():&lt;br /&gt;
&lt;br /&gt;
    # Define input files&lt;br /&gt;
&lt;br /&gt;
    inside_raw = Path(&#039;inside-raw.lst&#039;)&lt;br /&gt;
&lt;br /&gt;
    zapret_google = Path(&#039;zapret-hosts-google.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    zapret_user = Path(&#039;zapret-hosts-user.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    custom_list = Path(&#039;custom-user-list.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    output_file = Path(&#039;podkop-final-list.txt&#039;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;=== Podkop Domain List Generator ===\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Read all domain lists&lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading inside-raw.lst...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    inside_domains = read_domains(inside_raw)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(inside_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading zapret-hosts-google.txt...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    zapret_google_domains = read_domains(zapret_google)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(zapret_google_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading zapret-hosts-user.txt...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    zapret_user_domains = read_domains(zapret_user)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(zapret_user_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;Reading custom-user-list.txt...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    custom_domains = read_domains(custom_list)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Loaded {len(custom_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Combine Zapret lists (domains to exclude from inside-raw)&lt;br /&gt;
&lt;br /&gt;
    zapret_combined = zapret_google_domains | zapret_user_domains&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\nTotal Zapret domains to exclude: {len(zapret_combined)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Remove Zapret domains from inside-raw&lt;br /&gt;
&lt;br /&gt;
    filtered_inside = inside_domains - zapret_combined&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;Inside-raw after filtering: {len(filtered_inside)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Combine filtered inside-raw with custom domains&lt;br /&gt;
&lt;br /&gt;
    final_domains = filtered_inside | custom_domains&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\nFinal domain count: {len(final_domains)} domains&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    # Sort and save&lt;br /&gt;
&lt;br /&gt;
    sorted_domains = sorted(final_domains)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    with open(output_file, &#039;w&#039;, encoding=&#039;utf-8&#039;) as f:&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;# Podkop Domain List\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;# Generated automatically from inside-raw.lst (filtered) + custom domains\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(f&amp;quot;# Total: {len(sorted_domains)} domains\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;#\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;# Excludes domains already handled by Zapret:\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(f&amp;quot;#   - zapret-hosts-google.txt ({len(zapret_google_domains)} domains)\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(f&amp;quot;#   - zapret-hosts-user.txt ({len(zapret_user_domains)} domains)\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        f.write(&amp;quot;#\n\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
        for domain in sorted_domains:&lt;br /&gt;
&lt;br /&gt;
            f.write(f&amp;quot;{domain}\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\n✓ Successfully created {output_file}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;  Ready to use in Podkop Custom Domain Lists&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
&lt;br /&gt;
    main()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Использование скрипта ====&lt;br /&gt;
&lt;br /&gt;
# Скачайте актуальный &amp;lt;code&amp;gt;inside-raw.lst&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wget https://community.antifilter.download/list/inside-raw.lst&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Скопируйте с роутера файлы &amp;lt;code&amp;gt;zapret-hosts-google.txt&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;zapret-hosts-user.txt&amp;lt;/code&amp;gt; из &amp;lt;code&amp;gt;/opt/zapret/ipset/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Создайте файл &amp;lt;code&amp;gt;custom-user-list.txt&amp;lt;/code&amp;gt; со своими доменами (если нужно)&lt;br /&gt;
&lt;br /&gt;
# Запустите скрипт:&lt;br /&gt;
&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
python3 generate_podkop_list.py&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Используйте полученный файл &amp;lt;code&amp;gt;podkop-final-list.txt&amp;lt;/code&amp;gt; в Podkop&lt;br /&gt;
&lt;br /&gt;
=== Настройка прокси/VPN ===&lt;br /&gt;
После добавления списка доменов настройте подключение к вашему VPN/прокси-серверу.&lt;br /&gt;
&lt;br /&gt;
В разделе &#039;&#039;&#039;Outbounds&#039;&#039;&#039; (в интерфейсе Podkop) настройте:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Connection Type&#039;&#039;&#039;: &amp;lt;code&amp;gt;proxy&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (в зависимости от вашего сервера)&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Proxy String&#039;&#039;&#039;: вставьте ссылку на ваш VPN/прокси (формат: &amp;lt;code&amp;gt;protocol://user:pass@server:port&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
# Или настройте &#039;&#039;&#039;Interface&#039;&#039;&#039; для WireGuard/OpenVPN&lt;br /&gt;
&lt;br /&gt;
=== Применение настроек ===&lt;br /&gt;
Нажмите &#039;&#039;&#039;Save &amp;amp; Restart&#039;&#039;&#039; для применения конфигурации Podkop.&lt;br /&gt;
&lt;br /&gt;
== Проверка работы ==&lt;br /&gt;
&lt;br /&gt;
=== Тестирование Zapret ===&lt;br /&gt;
&lt;br /&gt;
# Откройте YouTube — должен работать &#039;&#039;&#039;без VPN&#039;&#039;&#039; (регион РФ, русскоязычные рекомендации)&lt;br /&gt;
&lt;br /&gt;
# Проверьте Discord — должен открываться &#039;&#039;&#039;через Zapret&#039;&#039;&#039; (без VPN)&lt;br /&gt;
&lt;br /&gt;
=== Тестирование Podkop ===&lt;br /&gt;
&lt;br /&gt;
# Откройте домен из вашего списка Podkop (например, &amp;lt;code&amp;gt;rutracker.org&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
# Проверьте смену IP (через &amp;lt;code&amp;gt;2ip.ru&amp;lt;/code&amp;gt; или аналог) — должен показать IP вашего VPN-сервера&lt;br /&gt;
&lt;br /&gt;
=== Диагностика ===&lt;br /&gt;
Если что-то не работает:&lt;br /&gt;
&lt;br /&gt;
# Проверьте логи Zapret: &#039;&#039;&#039;Services → Zapret → Log Viewer&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Проверьте статус Podkop: &#039;&#039;&#039;Services → Podkop&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Убедитесь, что домен &#039;&#039;&#039;не находится одновременно&#039;&#039;&#039; в списках Zapret и Podkop&lt;br /&gt;
&lt;br /&gt;
== Заключение ==&lt;br /&gt;
Связка Zapret + Podkop — это оптимальное решение для обхода блокировок на OpenWRT, которое позволяет:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Использовать DPI-обход&#039;&#039;&#039; (Zapret) для потоковых сервисов (YouTube, Twitch, Discord, Steam)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Оставаться в регионе РФ&#039;&#039;&#039; на YouTube и других сервисах (получать русскоязычный контент и рекомендации)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Экономить VPN-трафик&#039;&#039;&#039; (95% трафика идёт через Zapret без шифрования)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Использовать VPN&#039;&#039;&#039; (Podkop) только для действительно заблокированных ресурсов (торрент-трекеры, специализированные сервисы)&lt;br /&gt;
&lt;br /&gt;
Таким образом, вы получаете &#039;&#039;&#039;лучшее из обоих миров&#039;&#039;&#039;: быстрый доступ к популярным сервисам без потери региона и полноценный VPN для всего остального.&lt;br /&gt;
&lt;br /&gt;
== Полезные ссылки ==&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/remittor/zapret-openwrt Zapret для OpenWRT (GitHub)]&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/itdoginfo/podkop Podkop (GitHub)]&lt;br /&gt;
&lt;br /&gt;
* [https://podkop.net/ Документация Podkop]&lt;br /&gt;
&lt;br /&gt;
* [https://community.antifilter.download/ Списки Russian inside]&lt;br /&gt;
&lt;br /&gt;
== Дополнительно ==&lt;br /&gt;
&lt;br /&gt;
=== Автоматизация обновления списков ===&lt;br /&gt;
Для автоматического обновления пользовательских списков доменов можно настроить cron-задачу, которая будет периодически скачивать обновлённый список и перезапускать сервисы.&lt;br /&gt;
&lt;br /&gt;
=== Мониторинг ===&lt;br /&gt;
Рекомендуется периодически проверять:&lt;br /&gt;
&lt;br /&gt;
* Логи Zapret на наличие ошибок&lt;br /&gt;
&lt;br /&gt;
* Статус подключения Podkop к VPN-серверу&lt;br /&gt;
&lt;br /&gt;
* Актуальность версий обоих сервисов&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1004</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1004"/>
		<updated>2026-05-13T05:04:44Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* Подключитесь по SSH к роутеру и выполните одну команду: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Так как у нас первый чистый запуск нужно установить несколько стандартных вещей:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install wget-ssl curl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&lt;br /&gt;
Такие можно брать на &#039;&#039;&#039;[https://github.com/itdoginfo/allow-domains allow-domains]&#039;&#039;&#039; - тот же podckop использует именно эти списки &amp;quot;Russia inside, Telegram и так далее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Дополнение:&#039;&#039;&#039; &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; это первый список что мы создали, их может быть сколько угодно, можно добавлять несколько. Например создать Russia inside на основе raw ссылки, и Telegram на основе Subnets.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка запустит скрипт в первый раз принудительно.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1003</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1003"/>
		<updated>2026-05-12T20:37:22Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* Шаг 3. Создать правило маршрутизации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Так как у нас первый чистый запуск нужно установить несколько стандартных вещей:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install wget-ssl curl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&lt;br /&gt;
Такие можно брать на &#039;&#039;&#039;[https://github.com/itdoginfo/allow-domains allow-domains]&#039;&#039;&#039; - тот же podckop использует именно эти списки &amp;quot;Russia inside, Telegram и так далее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Дополнение:&#039;&#039;&#039; &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; это первый список что мы создали, их может быть сколько угодно, можно добавлять несколько. Например создать Russia inside на основе raw ссылки, и Telegram на основе Subnets.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1002</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1002"/>
		<updated>2026-05-12T20:33:37Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* Шаг 2. Создать список доменов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Так как у нас первый чистый запуск нужно установить несколько стандартных вещей:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install wget-ssl curl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&lt;br /&gt;
Такие можно брать на &#039;&#039;&#039;[https://github.com/itdoginfo/allow-domains allow-domains]&#039;&#039;&#039; - тот же podckop использует именно эти списки &amp;quot;Russia inside, Russia Outside и так далее.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1001</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1001"/>
		<updated>2026-05-12T20:28:58Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* 2.3. Применение узла */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Так как у нас первый чистый запуск нужно установить несколько стандартных вещей:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install wget-ssl curl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1000</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=1000"/>
		<updated>2026-05-12T20:25:53Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* 2.1. Подключение фида singbox-helper и установка */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Так как у нас первый чистый запуск нужно установить несколько стандартных вещей:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install wget-ssl curl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. UI покажет жёлтое предупреждение под распознанными параметрами. Решение — попросить владельца сервера переключить транспорт на &amp;lt;code&amp;gt;ws&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grpc&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;tcp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=999</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=999"/>
		<updated>2026-05-12T18:47:49Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* 3.4. Скрипт правил NAT и FORWARD */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Так как у нас первый чистый запуск нужно установить несколько стандартных вещей:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install wget-ssl curl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про место:&#039;&#039;&#039; &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; занимает ~62 МБ распакованных, плюс ~7 МБ сам helper, плюс Entware-окружение. На встроенной флэш-памяти роутера не помещается — Entware &#039;&#039;&#039;должен&#039;&#039;&#039; быть на USB (см. Этап 1).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. UI покажет жёлтое предупреждение под распознанными параметрами. Решение — попросить владельца сервера переключить транспорт на &amp;lt;code&amp;gt;ws&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grpc&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;tcp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=998</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=998"/>
		<updated>2026-05-12T18:46:08Z</updated>

		<summary type="html">&lt;p&gt;Владимир: /* 2.1. Подключение фида singbox-helper и установка */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Так как у нас первый чистый запуск нужно установить несколько стандартных вещей:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install wget-ssl curl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про место:&#039;&#039;&#039; &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; занимает ~62 МБ распакованных, плюс ~7 МБ сам helper, плюс Entware-окружение. На встроенной флэш-памяти роутера не помещается — Entware &#039;&#039;&#039;должен&#039;&#039;&#039; быть на USB (см. Этап 1).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. UI покажет жёлтое предупреждение под распознанными параметрами. Решение — попросить владельца сервера переключить транспорт на &amp;lt;code&amp;gt;ws&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grpc&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;tcp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок.&lt;br /&gt;
&lt;br /&gt;
== 3.5. Тестирование ==&lt;br /&gt;
На клиентском устройстве, подключённом к Keenetic:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Очистите кеш DNS&#039;&#039;&#039; (важно — без этого старые ответы остаются):&lt;br /&gt;
#* Windows: &amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; в PowerShell.&lt;br /&gt;
#* Android / iOS: переподключиться к Wi-Fi (off → on).&lt;br /&gt;
#* macOS: &amp;lt;code&amp;gt;sudo dscacheutil -flushcache&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Перезапустите браузер (у него свой DNS-кеш).&lt;br /&gt;
# Откройте сайт из списка bypass.&lt;br /&gt;
# Параллельно откройте &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; в браузере. Адрес должен быть тот же, который вы видели в шаге «IP через TUN» на вкладке Главная UI singbox-helper (т.е. IP вашего VPN-провайдера, а не домашний WAN-IP).&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=997</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=997"/>
		<updated>2026-05-12T18:14:43Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
[[Файл:Keenetic architecture overview.png|центр|мини|633x633пкс|Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про место:&#039;&#039;&#039; &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; занимает ~62 МБ распакованных, плюс ~7 МБ сам helper, плюс Entware-окружение. На встроенной флэш-памяти роутера не помещается — Entware &#039;&#039;&#039;должен&#039;&#039;&#039; быть на USB (см. Этап 1).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. UI покажет жёлтое предупреждение под распознанными параметрами. Решение — попросить владельца сервера переключить транспорт на &amp;lt;code&amp;gt;ws&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grpc&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;tcp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок.&lt;br /&gt;
&lt;br /&gt;
== 3.5. Тестирование ==&lt;br /&gt;
На клиентском устройстве, подключённом к Keenetic:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Очистите кеш DNS&#039;&#039;&#039; (важно — без этого старые ответы остаются):&lt;br /&gt;
#* Windows: &amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; в PowerShell.&lt;br /&gt;
#* Android / iOS: переподключиться к Wi-Fi (off → on).&lt;br /&gt;
#* macOS: &amp;lt;code&amp;gt;sudo dscacheutil -flushcache&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Перезапустите браузер (у него свой DNS-кеш).&lt;br /&gt;
# Откройте сайт из списка bypass.&lt;br /&gt;
# Параллельно откройте &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; в браузере. Адрес должен быть тот же, который вы видели в шаге «IP через TUN» на вкладке Главная UI singbox-helper (т.е. IP вашего VPN-провайдера, а не домашний WAN-IP).&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
[[Файл:Keenetic architecture detailed.png|центр|мини|1031x1031пкс|Полная схема трафика через роутер: DNS-запросы клиента идут в keen-pbr dnsmasq → Quad9, ответы накапливаются в ipset; TCP-трафик в iptables маркируется и уходит либо в singtun (sing-box → VPN-узел), либо через WAN с DPI-обходом nfqws2.]]&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_architecture_overview.png&amp;diff=996</id>
		<title>Файл:Keenetic architecture overview.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_architecture_overview.png&amp;diff=996"/>
		<updated>2026-05-12T18:14:09Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_architecture_detailed.png&amp;diff=995</id>
		<title>Файл:Keenetic architecture detailed.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_architecture_detailed.png&amp;diff=995"/>
		<updated>2026-05-12T18:13:17Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Общая схема: keen-pbr по адресу домена решает, отправить трафик в туннель singtun (sing-box → VPN-сервер) или пустить через nfqws2 + прямой WAN.&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=994</id>
		<title>Гибкая маршрутизация и обход DPI на Keenetic (sing-box + keen-pbr + nfqws2)</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%93%D0%B8%D0%B1%D0%BA%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8_%D0%BE%D0%B1%D1%85%D0%BE%D0%B4_DPI_%D0%BD%D0%B0_Keenetic_(sing-box_%2B_keen-pbr_%2B_nfqws2)&amp;diff=994"/>
		<updated>2026-05-12T18:02:07Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Установка стека: гибкая маршрутизация и обход DPI на роутере Keenetic =&lt;br /&gt;
Подробная пошаговая инструкция для пользователя, который умеет работать в терминале, но не знаком с устройством роутерных систем. Проверено на &#039;&#039;&#039;Xiaomi Mi Router 3G&#039;&#039;&#039; с KeeneticOS &#039;&#039;&#039;5.0.7&#039;&#039;&#039; (порт сообщества). Подходит для любого Keenetic-устройства с поддержкой OPKG/Entware.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Что мы построим ==&lt;br /&gt;
Связка из четырёх компонентов, каждый из которых решает свою задачу:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Entware&#039;&#039;&#039;&lt;br /&gt;
|Менеджер пакетов на роутере. Позволяет ставить любой Linux-софт через &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039; + &#039;&#039;&#039;singbox-helper&#039;&#039;&#039;&lt;br /&gt;
|sing-box — универсальный туннельный движок, поддерживает VLESS, Hysteria2, Trojan, Shadowsocks, WireGuard и др. Создаёт виртуальный интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, в который заворачивается выборочный трафик. singbox-helper — обёртка над ним: ставит как зависимость, даёт веб-UI на &amp;lt;code&amp;gt;:8765&amp;lt;/code&amp;gt;, по вставленной ссылке (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, ...) сама генерирует &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Маршрутизатор по доменам. Решает, какой трафик отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какой пускать напрямую. Имеет полноценный веб-интерфейс.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039; (zapret v2)&lt;br /&gt;
|Утилита модификации сетевых пакетов. Обходит DPI-фильтрацию для сервисов, которые &#039;&#039;&#039;не нужно&#039;&#039;&#039; заворачивать в туннель (например, потоковое видео — чтобы не нагружать тоннель и не терять скорость).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Общая архитектура ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                          KEENETIC РОУТЕР&lt;br /&gt;
&lt;br /&gt;
  ┌──────────────────────────────────────────────────────────────┐&lt;br /&gt;
  │                                                              │&lt;br /&gt;
  │  ┌────────────────┐                                          │&lt;br /&gt;
  │  │   keen-pbr     │  --- адрес домена в списке? ---          │&lt;br /&gt;
  │  │   (по доменам) │            │           │                 │&lt;br /&gt;
  │  └────────────────┘            │ ДА        │ НЕТ             │&lt;br /&gt;
  │                                ▼           ▼                 │&lt;br /&gt;
  │                       ┌────────────┐  ┌────────────┐         │&lt;br /&gt;
  │                       │  singtun   │  │   nfqws2   │         │&lt;br /&gt;
  │                       │ (sing-box) │  │  DPI fix   │         │&lt;br /&gt;
  │                       └────────────┘  └────────────┘         │&lt;br /&gt;
  │                            │                │                │&lt;br /&gt;
  └────────────────────────────┼────────────────┼────────────────┘&lt;br /&gt;
                               │                │&lt;br /&gt;
                          ┌────▼────┐      ┌────▼────┐&lt;br /&gt;
                          │  VPN-   │      │  Прямой │&lt;br /&gt;
                          │  сервер │      │  WAN    │&lt;br /&gt;
                          └─────────┘      └─────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;(Здесь схему можно перерисовать в Mermaid или Excalidraw и заменить блок.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Требования ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Требование&lt;br /&gt;
!Описание&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Роутер&#039;&#039;&#039;&lt;br /&gt;
|Keenetic с поддержкой Entware/OPKG (KeeneticOS 3.7 и новее) и хотя бы одним свободным USB-портом.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;USB-накопитель&#039;&#039;&#039;&lt;br /&gt;
|Любая флешка от &#039;&#039;&#039;8 ГБ&#039;&#039;&#039;. Будет отформатирована в EXT4. Без флешки не получится — встроенной памяти роутера не хватит.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Доступ к веб-интерфейсу&#039;&#039;&#039;&lt;br /&gt;
|Адрес роутера в браузере (обычно &amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;my.keenetic.net&amp;lt;/code&amp;gt;), логин администратора.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;SSH-клиент&#039;&#039;&#039;&lt;br /&gt;
|PuTTY, Windows Terminal с OpenSSH, или любой другой.&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;VPN-узел&#039;&#039;&#039;&lt;br /&gt;
|Любой провайдер с протоколами VLESS, Hysteria2, Trojan и т.п. (необходим только для туннеля; обход DPI работает без него).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; Полный стек (sing-box + keen-pbr + nfqws2 + зависимости) занимает &#039;&#039;&#039;около 110 МБ&#039;&#039;&#039; распакованного места. На встроенной флэш-памяти большинства роутеров этого нет — поэтому USB-накопитель &#039;&#039;&#039;обязателен&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 1. Установка Entware =&lt;br /&gt;
Entware — это система пакетов на основе OpenWrt-репозитория. После установки на роутере появится Linux-окружение в каталоге &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, в которое можно ставить пакеты командой &amp;lt;code&amp;gt;opkg install&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Какая у вас прошивка ==&lt;br /&gt;
KeeneticOS существует в двух вариантах, и от этого зависит, нужен ли вам отдельный шаг включения компонентов прошивки.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Вариант прошивки&lt;br /&gt;
!Нужно ли включать компоненты?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Оригинальный Keenetic&#039;&#039;&#039; (продавался как Keenetic с завода: KN-..., Hero, Ultra, Giga, Viva, Hopper, Peak и т.п.)&lt;br /&gt;
|&#039;&#039;&#039;Да&#039;&#039;&#039;, через веб-интерфейс — см. подсекцию «Только для оригинального Keenetic» ниже&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Порт сообщества&#039;&#039;&#039; (например, [https://keeneticported.dev keeneticported.dev]) на роутере другой марки (Xiaomi Mi Router 3G/3/Pro, TP-Link, Mercusys и др.)&lt;br /&gt;
|&#039;&#039;&#039;Нет&#039;&#039;&#039;, всё уже в образе прошивки порта — пропускайте подсекцию ниже и сразу переходите к «Выбор URL установщика»&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как понять, какая у вас:&#039;&#039;&#039; На странице &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; видна модель устройства. Маркетинговое имя &amp;quot;Keenetic Hero&amp;quot; и модель &amp;quot;KN-...&amp;quot; — это оригинальный Keenetic. Имя другого производителя (например, &amp;quot;Xiaomi R3G&amp;quot;) + KeeneticOS — это порт сообщества. Дополнительный признак порта — на странице компонентов сообщение «Не удалось получить список компонентов от сервера» (своего сервера обновлений у порта нет).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Только для оригинального Keenetic ==&lt;br /&gt;
В веб-интерфейсе: &#039;&#039;&#039;Управление → Общие настройки&#039;&#039;&#039; → блок &#039;&#039;&#039;Обновления и компоненты&#039;&#039;&#039; → кнопка &#039;&#039;&#039;Показать компоненты&#039;&#039;&#039; (в старых версиях UI — &#039;&#039;&#039;Изменить набор компонентов&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Включите следующие компоненты:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Компонент&lt;br /&gt;
!Зачем&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Поддержка открытых пакетов&#039;&#039;&#039; (OPKG)&lt;br /&gt;
|База Entware&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Файловая система Ext&#039;&#039;&#039;&lt;br /&gt;
|Монтирование ext4-флешки&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Сервер SSH&#039;&#039;&#039;&lt;br /&gt;
|Командная строка роутера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Netfilter&amp;quot; / &amp;quot;Модули ядра Netfilter&amp;quot;&lt;br /&gt;
|База iptables для Entware&lt;br /&gt;
|-&lt;br /&gt;
|&amp;quot;Расширенные модули Netfilter&amp;quot; / &amp;quot;Netfilter addons&amp;quot;&lt;br /&gt;
|Доп. модули (&amp;lt;code&amp;gt;xt_set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xt_multiport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conntrack-extra&amp;lt;/code&amp;gt;) — &#039;&#039;&#039;критично&#039;&#039;&#039; для keen-pbr&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После выбора — &#039;&#039;&#039;Установить обновление&#039;&#039;&#039; и ждите 2–5 минут, роутер перезагрузится сам.&lt;br /&gt;
&lt;br /&gt;
== Выбор URL установщика ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Архитектура роутера&lt;br /&gt;
!URL установщика (одной строкой)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;MIPS little-endian (mipsel)&#039;&#039;&#039; — большинство Keenetic и портов на Xiaomi/TP-Link/Mercusys (Mi 3G, Mi 3, Mi 4A, Hero, Ultra, Giga, Viva, Air и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;ARM 64-bit (aarch64)&#039;&#039;&#039; — современные Keenetic (Hopper KN-3810, Peak KN-2710 и др.)&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/aarch64-k3.10/installer/aarch64-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Как узнать архитектуру:&#039;&#039;&#039; &#039;&#039;&#039;Системный монитор&#039;&#039;&#039; → видна модель и процессор. Большинство бытовых моделей — MIPS (верхняя строка). Сомневаетесь — берите mipsel; если не подходит, установщик ругнётся, роутер от этого не сломается.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Что вам понадобится ==&lt;br /&gt;
Перед тем как идти в командную строку, держите под рукой два значения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Откуда взять&lt;br /&gt;
!Пример&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;UUID USB-диска&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;show media&amp;lt;/code&amp;gt; в веб-CLI Keenetic (см. шаг 3 в 1.1), поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;d5f39712-7ae1-dc01-00f3-97127ae1dc01&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;URL установщика&#039;&#039;&#039;&lt;br /&gt;
|Из таблицы «Выбор URL установщика» выше — по архитектуре&lt;br /&gt;
|&amp;lt;code&amp;gt;https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1.1. Установка Entware через командную строку ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Подключите USB-флешку.&#039;&#039;&#039; Воткните флешку в USB-порт роутера. Откройте веб-интерфейс: &#039;&#039;&#039;Приложения → USB-диски и принтеры&#039;&#039;&#039; — диск должен появиться в списке. Если файловая система не &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; — нажмите &#039;&#039;&#039;Форматировать&#039;&#039;&#039; → &#039;&#039;&#039;EXT4&#039;&#039;&#039;.&lt;br /&gt;
#: [[File:keenetic_usb_disks.png|center|Страница «USB-диски и принтеры» в веб-интерфейсе Keenetic с подключённой USB-флешкой и опцией «Форматировать» в EXT4.|мини|994x994пкс]]&lt;br /&gt;
# &#039;&#039;&#039;Откройте встроенный веб-CLI Keenetic:&#039;&#039;&#039; &amp;lt;code&amp;gt;http://192.168.1.1/a&amp;lt;/code&amp;gt; (или подставьте свой LAN-IP роутера, если он другой). Должно появиться приглашение &amp;lt;code&amp;gt;(config)&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Узнайте UUID диска:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;show media&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: В ответе найдите блок с вашей флешкой и поле &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; внутри &amp;lt;code&amp;gt;partition&amp;lt;/code&amp;gt;. Длинная строка вида &amp;lt;code&amp;gt;d5f39712-...&amp;lt;/code&amp;gt; — это и есть нужное значение, скопируйте его.&lt;br /&gt;
# &#039;&#039;&#039;Запустите установку Entware.&#039;&#039;&#039; Подставьте &#039;&#039;&#039;свой UUID&#039;&#039;&#039; и &#039;&#039;&#039;URL по архитектуре&#039;&#039;&#039; (из таблицы «Выбор URL установщика» выше):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk &amp;lt;ВАШ_UUID_ДИСКА&amp;gt;:/ &amp;lt;URL_УСТАНОВЩИКА_ДЛЯ_ВАШЕЙ_АРХИТЕКТУРЫ&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Пример с реальными подставленными значениями (mipsel):&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg disk d5f39712-7ae1-dc01-00f3-97127ae1dc01:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &#039;&#039;&#039;Угловых скобок &amp;lt;code&amp;gt;&amp;lt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;&amp;gt;&amp;lt;/code&amp;gt; в финальной команде остаться не должно.&#039;&#039;&#039; Двоеточие и слэш &amp;lt;code&amp;gt;:/&amp;lt;/code&amp;gt; сразу после UUID — обязательны. После UUID и перед URL — ровно один пробел.&lt;br /&gt;
# &#039;&#039;&#039;Сразу включите DNS-override и сохраните конфиг:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; заставит KeeneticOS отдавать DNS-запросы клиентов в Entware-овский dnsmasq, который потом будет управляться keen-pbr. Без этого keen-pbr не сможет перехватывать DNS и маршрутизация по доменам работать не будет. Опция применится после reboot ниже.&lt;br /&gt;
# &#039;&#039;&#039;Дождитесь окончания установки.&#039;&#039;&#039; Откройте журнал в веб-интерфейсе: &amp;lt;code&amp;gt;http://192.168.1.1/diagnostics/general/log&amp;lt;/code&amp;gt; (если LAN-IP другой — подставьте). Установщик пишет туда что делает: качает &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;, распаковывает в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, ставит базовые пакеты. Это занимает 2–5 минут. Ждите финальной строки &amp;lt;code&amp;gt;Entware installed&amp;lt;/code&amp;gt;.&lt;br /&gt;
#: [[File:keenetic_log_entware_installed.png|center|Системный журнал Keenetic (/diagnostics/general/log) с финальной строкой «Entware installed» — установка завершилась успешно, можно перезагружать роутер.|мини|804x804пкс]]&lt;br /&gt;
#: &amp;lt;div style=&amp;quot;padding: 8px 12px; margin: 8px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&#039;&#039;&#039;Не перезагружайте роутер до этой строки в журнале!&#039;&#039;&#039; Если ребутнуть роутер посреди установки, файлы Entware на флешке останутся неконсистентными, и на следующих этапах посыпятся странные ошибки.&amp;lt;/div&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;Перезагрузите роутер из веб-CLI:&#039;&#039;&#039;&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;system reboot&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#: Через 1–2 минуты роутер поднимется обратно. После reboot применятся: монтирование Entware с флешки в &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;, автозапуск его сервисов, а главное — &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; из шага 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Если установщик молчит:&#039;&#039;&#039; В некоторых сборках команда &amp;lt;code&amp;gt;opkg disk ... URL&amp;lt;/code&amp;gt; молчит, если OPKG-диск уже был назначен раньше. В журнале нет строк про загрузку &amp;lt;code&amp;gt;installer.tar.gz&amp;lt;/code&amp;gt;? Сбросьте назначение диска и повторите шаги 4–5:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
no opkg disk&lt;br /&gt;
opkg disk &amp;lt;ВАШ_UUID&amp;gt;:/ &amp;lt;URL&amp;gt;&lt;br /&gt;
opkg dns-override&lt;br /&gt;
system configuration save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 1.2. Первое подключение по SSH ==&lt;br /&gt;
&lt;br /&gt;
После перезагрузки Entware поднимает встроенный SSH-сервер на порту &#039;&#039;&#039;222&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
На Windows — открываете PowerShell и подключаетесь одной командой:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh -p 222 root@192.168.1.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(LAN-IP роутера другой — подставьте.)&lt;br /&gt;
&lt;br /&gt;
Параметры подключения:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Параметр&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Хост&lt;br /&gt;
|LAN-IP роутера (&amp;lt;code&amp;gt;192.168.1.1&amp;lt;/code&amp;gt; по умолчанию)&lt;br /&gt;
|-&lt;br /&gt;
|Порт&lt;br /&gt;
|&#039;&#039;&#039;222&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Логин&lt;br /&gt;
|&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Пароль&lt;br /&gt;
|&amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; (заводской)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
При первом подключении SSH спросит fingerprint — ответьте &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Смена пароля (обязательно!) ===&lt;br /&gt;
Сразу после входа:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Введите новый пароль дважды.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание:&#039;&#039;&#039; дефолтный &amp;lt;code&amp;gt;keenetic&amp;lt;/code&amp;gt; известен всем. Если SSH когда-нибудь будет проброшен наружу — роутер взломают за минуты.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Хотите ходить по ключу, без ввода пароля?&#039;&#039;&#039; См. &#039;&#039;&#039;[[#Приложение А. OpenSSH с ключом (опционально)|Приложение А]]&#039;&#039;&#039; в конце документа. Для базовой установки это &#039;&#039;&#039;не нужно&#039;&#039;&#039; — переходите к Этапу 2.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Всё готово для установки утилит.&#039;&#039;&#039; Переходите к Этапу 2.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 2. Установка sing-box (через singbox-helper) =&lt;br /&gt;
sing-box — движок туннеля. Раньше его ставили вручную (&amp;lt;code&amp;gt;opkg install sing-box-go&amp;lt;/code&amp;gt;) и сами писали &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; на 60+ строк JSON. Теперь весь этот шаг делает утилита &amp;lt;b&amp;gt;singbox-helper&amp;lt;/b&amp;gt; — она:&lt;br /&gt;
&lt;br /&gt;
* подтягивает &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; как зависимость opkg (62 МБ, ставится автоматически);&lt;br /&gt;
* даёт веб-интерфейс на &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; &lt;br /&gt;
* по вставленной ссылке узла (&amp;lt;code&amp;gt;vless://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hysteria2://&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;socks5://&amp;lt;/code&amp;gt;) сама собирает корректный &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt;, бэкапит старый, рестартит sing-box и прогоняет диагностику.&lt;br /&gt;
&lt;br /&gt;
== 2.1. Подключение фида singbox-helper и установка ==&lt;br /&gt;
Подключитесь по SSH и добавьте репозиторий пакетов:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
opkg увидит зависимость &amp;lt;code&amp;gt;Depends: sing-box-go&amp;lt;/code&amp;gt; и поставит обе утилиты сразу. На выходе:&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/bin/singbox-helper&amp;lt;/code&amp;gt; — наш бинарь (~7 МБ);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/sbin/sing-box&amp;lt;/code&amp;gt; — sing-box-go от Entware (~62 МБ распакованных);&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; — init-скрипты, прописаны в автозагрузку;&lt;br /&gt;
* демон singbox-helper уже стартует и слушает &amp;lt;code&amp;gt;0.0.0.0:8765&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про место:&#039;&#039;&#039; &amp;lt;code&amp;gt;sing-box-go&amp;lt;/code&amp;gt; занимает ~62 МБ распакованных, плюс ~7 МБ сам helper, плюс Entware-окружение. На встроенной флэш-памяти роутера не помещается — Entware &#039;&#039;&#039;должен&#039;&#039;&#039; быть на USB (см. Этап 1).&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет — другая архитектура:&#039;&#039;&#039; Команда выше — для &amp;lt;code&amp;gt;mipsel-3.4&amp;lt;/code&amp;gt; (Keenetic / Xiaomi на MT7621/MT7620 — основная масса устройств). Для новых Keenetic Hopper/Hero/Giga на ARM64 — поменяйте URL на &amp;lt;code&amp;gt;...github.io/sing-box-helper/aarch64-3.10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2.2. Открытие веб-интерфейса ==&lt;br /&gt;
В браузере: &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP-вашего-роутера&amp;amp;gt;:8765&amp;lt;/code&amp;gt; (например, &amp;lt;code&amp;gt;http://192.168.10.1:8765&amp;lt;/code&amp;gt;; LAN-IP видно в веб-интерфейсе Keenetic в карточке «Домашняя сеть»).&lt;br /&gt;
&lt;br /&gt;
Откроется страница с четырьмя вкладками: &#039;&#039;&#039;Главная&#039;&#039;&#039;, &#039;&#039;&#039;Настройки&#039;&#039;&#039;, &#039;&#039;&#039;Логи&#039;&#039;&#039;, &#039;&#039;&#039;Бэкапы&#039;&#039;&#039;.&lt;br /&gt;
[[Файл:Singbox-helper-g.png|центр|мини|1034x1034пкс|Главный экран приложения Singbox-helper]]&lt;br /&gt;
На «Главной» сверху видно состояние:&lt;br /&gt;
* &#039;&#039;&#039;sing-box&#039;&#039;&#039; — running / не запущен (после свежей установки — running с заглушечным конфигом);&lt;br /&gt;
* &#039;&#039;&#039;TUN-интерфейс&#039;&#039;&#039; — UP / down;&lt;br /&gt;
* &#039;&#039;&#039;Текущий узел&#039;&#039;&#039; — пусто (мы ещё не применили ссылку).&lt;br /&gt;
&lt;br /&gt;
== 2.3. Применение узла ==&lt;br /&gt;
В блоке «Применить узел» вставьте ссылку из вашей VPN-панели (3x-ui, Marzban, Hiddify и т.п.). Поддерживаются:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Схема&lt;br /&gt;
!Что это&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;hysteria2://пароль@сервер:порт&amp;lt;/code&amp;gt;&lt;br /&gt;
|Hysteria2 (синоним &amp;lt;code&amp;gt;hy2://&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://uuid@сервер:порт?type=tcp&amp;amp;security=tls&amp;amp;...&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS vanilla (TCP / WS / gRPC / h2 / httpupgrade)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;vless://...?security=reality&amp;amp;pbk=...&amp;amp;flow=xtls-rprx-vision&amp;lt;/code&amp;gt;&lt;br /&gt;
|VLESS + REALITY (+ Vision)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;socks5://127.0.0.1:1080&amp;lt;/code&amp;gt;&lt;br /&gt;
|Локальный SOCKS-upstream — если на роутере уже работает naive-proxy / mieru-client, sing-box можно использовать как обёртку с TUN над ним&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Жмёте &#039;&#039;&#039;[ Применить ]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Через несколько секунд под формой появится блок &#039;&#039;&#039;«Проверка работы»&#039;&#039;&#039; с пятью шагами:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Доступность узла&#039;&#039;&#039; — TCP-connect &amp;lt;code&amp;gt;server:port&amp;lt;/code&amp;gt; со временем отклика.&lt;br /&gt;
# &#039;&#039;&#039;sing-box процесс&#039;&#039;&#039; — PID и версия.&lt;br /&gt;
# &#039;&#039;&#039;Интерфейс singtun&#039;&#039;&#039; — UP / down.&lt;br /&gt;
# &#039;&#039;&#039;Прямой IP&#039;&#039;&#039; — что отвечает &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; через WAN.&lt;br /&gt;
# &#039;&#039;&#039;IP через TUN&#039;&#039;&#039; — то же, но &amp;lt;code&amp;gt;curl --interface singtun&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Должен отличаться&#039;&#039;&#039; от прямого.&lt;br /&gt;
[[Файл:Singbox-helper-con.png|центр|мини|850x850пкс|Пример успешного подключения в Singbox-helper]]&lt;br /&gt;
Если все 5 шагов зелёные — туннель работает.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Особый случай — xhttp:&#039;&#039;&#039; Узлы с параметром &amp;lt;code&amp;gt;type=xhttp&amp;lt;/code&amp;gt; на REALITY-серверах из 3x-ui &#039;&#039;&#039;не подключатся&#039;&#039;&#039;. sing-box 1.13 не имеет нативного xhttp-транспорта; helper маппит его в &amp;lt;code&amp;gt;httpupgrade&amp;lt;/code&amp;gt; как best-effort, но реальные xhttp-серверы отвечают &amp;lt;code&amp;gt;400 Bad Request&amp;lt;/code&amp;gt;. UI покажет жёлтое предупреждение под распознанными параметрами. Решение — попросить владельца сервера переключить транспорт на &amp;lt;code&amp;gt;ws&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grpc&amp;lt;/code&amp;gt; или &amp;lt;code&amp;gt;tcp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обновление в будущем — обычным opkg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
state.json и config.yaml &#039;&#039;&#039;переживают&#039;&#039;&#039; обновление.&lt;br /&gt;
&lt;br /&gt;
Если IP совпадают — туннель не работает. Зайдите на вкладку &#039;&#039;&#039;Логи&#039;&#039;&#039; в UI, выберите источник &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt; — там будет конкретная причина.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Этап 3. Установка keen-pbr =&lt;br /&gt;
keen-pbr решает &#039;&#039;&#039;одну задачу&#039;&#039;&#039;: какие домены отправлять в туннель &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;, а какие пускать напрямую. Имеет современный веб-интерфейс на отдельном порту.&lt;br /&gt;
&lt;br /&gt;
== 3.1. Подключение репозитория и установка ==&lt;br /&gt;
Добавьте feed keen-pbr к конфигу OPKG и установите пакет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; Для других архитектур URL feed-а отличается. Список архитектур и URL — на странице репозитория проекта: &amp;lt;code&amp;gt;https://repo.keen-pbr.fyi/repository/stable/&amp;lt;/code&amp;gt; (выбрать OS = &#039;&#039;&#039;Keenetic / NetCraze&#039;&#039;&#039;, version = &#039;&#039;&#039;current&#039;&#039;&#039;, архитектуру — свою). Узнать архитектуру роутера можно командой &amp;lt;code&amp;gt;opkg print-architecture&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет за собой зависимости: &amp;lt;code&amp;gt;dnsmasq-full&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ipset&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;libnl-*&amp;lt;/code&amp;gt; и др. — это нормально.&lt;br /&gt;
&lt;br /&gt;
=== Интерактивный диалог при установке ===&lt;br /&gt;
В процессе установки появится вопрос:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuring keen-pbr.&lt;br /&gt;
keen-pbr installation&lt;br /&gt;
------------------------&lt;br /&gt;
Tip: set KEEN_PBR_REPLACE_DNSMASQ_DEFAULTS=Y or N to automate keen-pbr installation.&lt;br /&gt;
Replace /opt/etc/dnsmasq.conf with recommended keen-pbr dnsmasq defaults? [y/n]:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответьте &#039;&#039;&#039;&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;&#039;&#039;&#039; и нажмите Enter. Это критически важно — keen-pbr заменит дефолтный &amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; на свой шаблон, который позволяет keen-pbr динамически наполнять ipset-списки доменами. Без этого ничего работать не будет.&lt;br /&gt;
&lt;br /&gt;
Ожидаемый ответ после ввода &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Backing up existing /opt/etc/dnsmasq.conf to /opt/etc/dnsmasq.conf.backup-pre-keen-pbr&lt;br /&gt;
Installing recommended keen-pbr dnsmasq base config to /opt/etc/dnsmasq.conf&lt;br /&gt;
&lt;br /&gt;
keen-pbr: Disabling HW NAT...&lt;br /&gt;
net.netfilter.nf_conntrack_fastnat = 0&lt;br /&gt;
[local_list] Skipped (no URL)&lt;br /&gt;
 Starting keen-pbr...              done.&lt;br /&gt;
&lt;br /&gt;
Installation complete!&lt;br /&gt;
Config path: /opt/etc/keen-pbr/config.json&lt;br /&gt;
Web API: http://my.keenetic.net:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3.2. Доступ к веб-интерфейсу ==&lt;br /&gt;
keen-pbr слушает на порту &#039;&#039;&#039;12121&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Изнутри LAN роутера ===&lt;br /&gt;
В браузере устройства, подключённого к Keenetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:12121&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;http://192.168.1.1:12121&amp;lt;/code&amp;gt;, или &amp;lt;code&amp;gt;http://my.keenetic.net:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
[[Файл:Keen-pbr-g.png|центр|мини|1043x1043пкс|Keen-pbr - пример главного экрана]]&lt;br /&gt;
&lt;br /&gt;
== 3.3. Базовая настройка через веб-интерфейс ==&lt;br /&gt;
Все пять шагов ниже делаются в UI keen-pbr. После них трафик пойдёт в туннель и DNS будет давать реальные IP, а не подменённые.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1. Создать outbound (точку выхода в туннель) ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;Outbounds (выходы)&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить outbound&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Tag&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (или любое короткое имя)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Interface&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Interface&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Gateway IPv6&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;оставить пустым&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить. В списке outbounds появится строка &amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; со статусом &#039;&#039;&#039;Исправен&#039;&#039;&#039; и активным интерфейсом.&lt;br /&gt;
&lt;br /&gt;
[[File:keenpbr_outbounds.png|center|Страница «Outbounds (выходы)» в веб-интерфейсе keen-pbr со списком из трёх выходов: wan, block, vpn (interface=singtun).|мини|1005x1005пкс]]&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2. Создать список доменов ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить список&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Имя&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Тип&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Домены&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Источник&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;Inline&amp;lt;/code&amp;gt; (вкладка с textarea)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Domains&#039;&#039;&#039;&lt;br /&gt;
|вставьте список доменов, &#039;&#039;&#039;по одному на строку&#039;&#039;&#039;. Например:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
example.com&lt;br /&gt;
streaming-service.net&lt;br /&gt;
another-site.org&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Совет:&#039;&#039;&#039; В режиме Inline можно вставить тысячи строк сразу. Один домен покрывает и сам сайт, и все его поддомены. Если вместо Inline хочется автоматическую подписку — выберите вариант &#039;&#039;&#039;URL&#039;&#039;&#039; и укажите адрес внешнего списка; keen-pbr будет обновлять его по расписанию.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3. Создать правило маршрутизации ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Правила маршрутизации&#039;&#039;&#039; → &#039;&#039;&#039;+ Добавить правило маршрутизации&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;List(s)&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; (выбрать из выпадающего)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Outbound&#039;&#039;&#039;&lt;br /&gt;
|&amp;lt;code&amp;gt;vpn&amp;lt;/code&amp;gt; (созданный на шаге 1)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Enabled&#039;&#039;&#039;&lt;br /&gt;
|включить&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Сохранить.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 4. Убрать встроенный keenetic_dns ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Интернет&#039;&#039;&#039; → &#039;&#039;&#039;DNS-серверы&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
В таблице вы увидите две строки: &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;. &#039;&#039;&#039;Удалите &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt;&#039;&#039;&#039; (иконка корзины в столбце «Действия»).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем это нужно:&#039;&#039;&#039; Сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; идёт через встроенный DNS-резолвер прошивки KeeneticOS. На некоторых конфигурациях (включённые «защитные» сервисы, community-порты) этот резолвер подменяет ответы для конкретных доменов на fake-IP из диапазона &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;. Эти fake-IP попадают в ipset keen-pbr → маршрутизация формально срабатывает → но трафик улетает на несуществующий адрес и клиент получает таймаут.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Шаг 5. Назначить основной DNS-сервер ===&lt;br /&gt;
В меню слева: &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;DNS-правила&#039;&#039;&#039; → блок &#039;&#039;&#039;«Основные DNS сервера»&#039;&#039;&#039; (вверху страницы).&lt;br /&gt;
&lt;br /&gt;
Нажмите &#039;&#039;&#039;+ Добавить основной DNS сервер&#039;&#039;&#039; и выберите из списка &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Что это такое:&#039;&#039;&#039; «Основные DNS сервера» — это fallback dnsmasq, то есть сервер, который используется, когда ни одно DNS-правило не подходит. После удаления &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; на шаге 4 он стал пустым — назначаем &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; (9.9.9.9), чтобы все запросы шли на публичный DNS, а не на DNS прошивки.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Применить изменения ===&lt;br /&gt;
В правом нижнем углу горит оранжевый баннер &#039;&#039;&#039;«Несохранённые изменения»&#039;&#039;&#039;. Нажмите &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; — keen-pbr перестроит конфигурацию dnsmasq и применит правила маршрутизации.&lt;br /&gt;
&lt;br /&gt;
== 3.4. Скрипт правил NAT и FORWARD ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Зачем нужен этот шаг:&#039;&#039;&#039; keen-pbr настраивает маркировку пакетов, ip-rule и таблицу маршрутизации, но &#039;&#039;&#039;не трогает&#039;&#039;&#039; таблицы NAT и FORWARD. Для интерфейсов, которые KeeneticOS знает (например, штатный WireGuard &amp;lt;code&amp;gt;nwg0&amp;lt;/code&amp;gt;), правила NAT/FORWARD добавляет сам ndm. Но &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; от sing-box — userspace-интерфейс, ndm о нём не знает. Без MASQUERADE и FORWARD ACCEPT трафик клиентов LAN до туннеля не доходит. Создадим один маленький скрипт, который добавляет эти правила автоматически.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Подключитесь по SSH к роутеру и выполните одну команду: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
sh /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Последняя строка покажет файл — должно быть &amp;lt;code&amp;gt;-rwxr-xr-x ... 40-singtun-nat.sh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Почему именно &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt;, а не &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt; ===&lt;br /&gt;
KeeneticOS-овский ndm периодически перестраивает таблицы iptables «с нуля» (при изменении сети, новых клиентах, реконфигурации). Всё, что добавлено обычным &amp;lt;code&amp;gt;init.d&amp;lt;/code&amp;gt;-скриптом, при этом стирается. А скрипты в &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; ndm &#039;&#039;&#039;сам дёргает&#039;&#039;&#039; после каждого перестроения. Так что наш хук переживает любые события сети без вмешательства пользователя.&lt;br /&gt;
&lt;br /&gt;
=== Переменные в скрипте ===&lt;br /&gt;
В скрипте всего два места, которые &#039;&#039;&#039;теоретически&#039;&#039;&#039; могут отличаться у разных пользователей:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Переменная&lt;br /&gt;
!Значение по умолчанию&lt;br /&gt;
!Что подставить, если у вас иначе&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;TUN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя TUN-интерфейса sing-box. singbox-helper в сгенерированном &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; по умолчанию ставит &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt; — это совпадает с тем, что ожидают keen-pbr и этот NAT-скрипт. Менять не нужно. Если очень хочется — переименуйте в &#039;&#039;&#039;Настройках&#039;&#039;&#039; UI singbox-helper (поле «Имя интерфейса») и здесь синхронно.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;LAN&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;&lt;br /&gt;
|Имя LAN-bridge KeeneticOS. &#039;&#039;&#039;На всех Keenetic-роутерах&#039;&#039;&#039; (включая community-порты на Xiaomi/TP-Link/Mercusys) это &amp;lt;code&amp;gt;br0&amp;lt;/code&amp;gt;. Проверить: &amp;lt;code&amp;gt;ip link show \| grep &amp;quot;state UP&amp;quot;&amp;lt;/code&amp;gt; на роутере или раздел &amp;quot;Домашняя сеть&amp;quot; в веб-UI Keenetic.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Архитектура (mipsel/aarch64) на этот скрипт &#039;&#039;&#039;не влияет&#039;&#039;&#039; — пути &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;/opt/sbin/iptables&amp;lt;/code&amp;gt; одинаковы для всех Entware-сборок.&lt;br /&gt;
&lt;br /&gt;
== 3.5. Тестирование ==&lt;br /&gt;
На клиентском устройстве, подключённом к Keenetic:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Очистите кеш DNS&#039;&#039;&#039; (важно — без этого старые ответы остаются):&lt;br /&gt;
#* Windows: &amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; в PowerShell.&lt;br /&gt;
#* Android / iOS: переподключиться к Wi-Fi (off → on).&lt;br /&gt;
#* macOS: &amp;lt;code&amp;gt;sudo dscacheutil -flushcache&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Перезапустите браузер (у него свой DNS-кеш).&lt;br /&gt;
# Откройте сайт из списка bypass.&lt;br /&gt;
# Параллельно откройте &amp;lt;code&amp;gt;https://api.ipify.org&amp;lt;/code&amp;gt; в браузере. Адрес должен быть тот же, который вы видели в шаге «IP через TUN» на вкладке Главная UI singbox-helper (т.е. IP вашего VPN-провайдера, а не домашний WAN-IP).&lt;br /&gt;
&lt;br /&gt;
= Этап 4. Установка nfqws2 =&lt;br /&gt;
nfqws2 — это userspace-демон, который через очередь NFQUEUE перехватывает первые пакеты соединений и модифицирует их так, чтобы DPI-фильтры не распознали трафик. Используется для обхода замедления сервисов потокового видео, голосовой связи и т.п. — &#039;&#039;&#039;без необходимости заворачивать их в туннель&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 4.1. Установка ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После установки сервис стартует автоматически — вмешательства не требуется.&lt;br /&gt;
&lt;br /&gt;
=== Проверка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ожидаемый результат:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Service NFQWS2 is running&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Что обрабатывается по умолчанию ===&lt;br /&gt;
В заводском конфиге работает режим &amp;lt;code&amp;gt;MODE_AUTO&amp;lt;/code&amp;gt; — комбинация:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Список&lt;br /&gt;
!Назначение&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/user.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, для которых &#039;&#039;&#039;всегда&#039;&#039;&#039; применяется обход (по умолчанию ~280 строк: Google/YouTube/CDN сервисы).&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/auto.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые nfqws2 &#039;&#039;&#039;сам обнаружил&#039;&#039;&#039; как замедленные/блокируемые. Заполняется автоматически.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/nfqws2/lists/exclude.list&amp;lt;/code&amp;gt;&lt;br /&gt;
|Домены, которые &#039;&#039;&#039;нельзя&#039;&#039;&#039; трогать (по умолчанию: домены KeeneticOS, чтобы не сломать облачные сервисы прошивки).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Покрываемые протоколы: HTTPS (TCP/443), HTTP (TCP/80), QUIC (UDP/443), Discord voice, WireGuard handshake, STUN/TURN, Telegram MTProto.&lt;br /&gt;
&lt;br /&gt;
== 4.2. (опционально) Веб-интерфейс ==&lt;br /&gt;
Для удобной работы со списками и стратегиями есть отдельный пакет с веб-UI.&lt;br /&gt;
&lt;br /&gt;
=== Установка ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пакет потянет lighttpd + PHP 8 — общий размер около &#039;&#039;&#039;10 МБ&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Доступ ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
http://LAN-АДРЕС-РОУТЕРА:90&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Например, &amp;lt;code&amp;gt;[http://192.168.10.1:90 http://192.168.1.1:90]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ввести логин и пароль,  те же что применяются для входа по ssh.&lt;br /&gt;
[[Файл:Nfqws2-g-p.png|центр|мини|914x914пкс|Пример главного окна Nfqws2]]&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Финальная архитектура =&lt;br /&gt;
После завершения всех этапов на роутере работают четыре сервиса:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Порт / Управление&lt;br /&gt;
!Что делает&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;sing-box&#039;&#039;&#039;&lt;br /&gt;
|TUN-интерфейс &amp;lt;code&amp;gt;singtun&amp;lt;/code&amp;gt;; Clash API на &amp;lt;code&amp;gt;:9090&amp;lt;/code&amp;gt;&lt;br /&gt;
|Шифрует и пересылает трафик в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;keen-pbr&#039;&#039;&#039;&lt;br /&gt;
|Web UI на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;&lt;br /&gt;
|Решает, какой трафик отправить в туннель&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;dnsmasq&#039;&#039;&#039;&lt;br /&gt;
|UDP/TCP :53 на LAN-адресе роутера&lt;br /&gt;
|Принимает DNS-запросы от LAN и наполняет ipset keen-pbr&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;nfqws2&#039;&#039;&#039;&lt;br /&gt;
|Демон, без отдельного порта (управление через файлы или nfqws-keenetic-web на &amp;lt;code&amp;gt;:90&amp;lt;/code&amp;gt;)&lt;br /&gt;
|Обходит DPI-фильтры для прямого трафика&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                  K E E N E T I C   Р О У Т Е Р&lt;br /&gt;
   ┌───────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
   │                                                                           │&lt;br /&gt;
   │   ┌──────────────┐                                                        │&lt;br /&gt;
LAN│   │  DNS-запрос  │                                                        │&lt;br /&gt;
───┼──►│  от клиента  │                                                        │&lt;br /&gt;
   │   └──────┬───────┘                                                        │&lt;br /&gt;
   │          │                                                                │&lt;br /&gt;
   │          ▼                                                                │&lt;br /&gt;
   │   ┌──────────────┐  upstream  ┌────────────────┐                          │&lt;br /&gt;
   │   │  keen-pbr    │  через     │  Quad9 9.9.9.9 │                          │&lt;br /&gt;
   │   │  dnsmasq     │ ─────────► │                │                          │&lt;br /&gt;
   │   │  на :53      │            │                │                          │&lt;br /&gt;
   │   └──────┬───────┘            └────────────────┘                          │&lt;br /&gt;
   │          │ IP → ipset                                                     │&lt;br /&gt;
   │          │                                                                │&lt;br /&gt;
   │   ┌──────▼──────────────────────────────────────────────┐                 │&lt;br /&gt;
LAN│   │  iptables / маршрутизация                           │                 │&lt;br /&gt;
───┼──►│  ├ если dst-IP в ipset bypass:                      │                 │&lt;br /&gt;
TCP│   │  │   fwmark → ip rule → table 151 → singtun         │                 │&lt;br /&gt;
   │   │  │                                                  │                 │&lt;br /&gt;
   │   │  └ иначе:                                           │                 │&lt;br /&gt;
   │   │      обычный WAN-маршрут                            │                 │&lt;br /&gt;
   │   │      └ nfqws2 ловит первые пакеты                   │                 │&lt;br /&gt;
   │   │        и модифицирует для обхода DPI                │                 │&lt;br /&gt;
   │   └────┬──────────────────────────────────────────┬─────┘                 │&lt;br /&gt;
   │        │                                          │                       │&lt;br /&gt;
   │        ▼                                          ▼                       │&lt;br /&gt;
   │   ┌─────────┐                            ┌────────────────┐               │&lt;br /&gt;
   │   │ singtun │                            │ WAN-интерфейс  │               │&lt;br /&gt;
   │   │ (TUN)   │                            │ (ваш провайдер)│               │&lt;br /&gt;
   │   └────┬────┘                            └────────┬───────┘               │&lt;br /&gt;
   │        │                                          │                       │&lt;br /&gt;
   │        ▼                                          │                       │&lt;br /&gt;
   │   ┌─────────────┐                                 │                       │&lt;br /&gt;
   │   │  sing-box   │                                 │                       │&lt;br /&gt;
   │   │  outbound:  │                                 │                       │&lt;br /&gt;
   │   │  hysteria2  │                                 │                       │&lt;br /&gt;
   │   └──────┬──────┘                                 │                       │&lt;br /&gt;
   │          │                                        │                       │&lt;br /&gt;
   └──────────┼────────────────────────────────────────┼───────────────────────┘&lt;br /&gt;
              │                                        │&lt;br /&gt;
              ▼                                        ▼&lt;br /&gt;
        ┌───────────┐                            ┌───────────┐&lt;br /&gt;
        │ VPN-узел  │                            │ Любой     │&lt;br /&gt;
        │ провайдера│                            │ интернет  │&lt;br /&gt;
        └───────────┘                            └───────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(Здесь можно перерисовать в Mermaid/Excalidraw для финальной картинки.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Эксплуатация =&lt;br /&gt;
== Добавление новых доменов в список keen-pbr ==&lt;br /&gt;
# Открыть UI keen-pbr на &amp;lt;code&amp;gt;:12121&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Правила трафика&#039;&#039;&#039; → &#039;&#039;&#039;Списки&#039;&#039;&#039; → выбрать список &amp;lt;code&amp;gt;bypass&amp;lt;/code&amp;gt; → &#039;&#039;&#039;Изменить&#039;&#039;&#039;.&lt;br /&gt;
# Добавить новые домены в textarea, &#039;&#039;&#039;Сохранить&#039;&#039;&#039;.&lt;br /&gt;
# Нажать &#039;&#039;&#039;«Применить и перезапустить»&#039;&#039;&#039; внизу страницы.&lt;br /&gt;
&lt;br /&gt;
== Добавление новых доменов в список nfqws2 ==&lt;br /&gt;
=== Через файл ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;new-site.com&#039; &amp;gt;&amp;gt; /opt/etc/nfqws2/lists/user.list&lt;br /&gt;
/opt/etc/init.d/S51nfqws2 restart&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Через веб-UI ===&lt;br /&gt;
Открыть &amp;lt;code&amp;gt;http://LAN-АДРЕС-РОУТЕРА:90&amp;lt;/code&amp;gt;, секция работы со списками.&lt;br /&gt;
&lt;br /&gt;
== Обновление компонентов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg upgrade singbox-helper sing-box-go keen-pbr nfqws2-keenetic&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postinst-хуки соответствующих пакетов перезапускают свои сервисы сами; runtime-данные (&amp;lt;code&amp;gt;state.json&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;, бэкапы) переживают &amp;lt;code&amp;gt;opkg upgrade&amp;lt;/code&amp;gt;. Если что-то осталось висеть со старой версией — перезапустите вручную (&amp;lt;code&amp;gt;/opt/etc/init.d/S98singbox-helper restart&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S99sing-box restart&amp;lt;/code&amp;gt;) или перезагрузите роутер.&lt;br /&gt;
&lt;br /&gt;
== Просмотр логов ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Сервис&lt;br /&gt;
!Команда&lt;br /&gt;
|-&lt;br /&gt;
|sing-box&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr&lt;br /&gt;
|&amp;lt;code&amp;gt;logread \| grep keen-pbr&amp;lt;/code&amp;gt; + системный журнал в веб-UI Keenetic&lt;br /&gt;
|-&lt;br /&gt;
|nfqws2&lt;br /&gt;
|&amp;lt;code&amp;gt;tail -f /opt/var/log/nfqws2.log&amp;lt;/code&amp;gt; (если включено логирование в конфиге)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Диагностика =&lt;br /&gt;
== Проверка статусов всех сервисов ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S98singbox-helper check     # рекомендую: дальше его UI покажет состояние sing-box&lt;br /&gt;
/opt/etc/init.d/S99sing-box       status&lt;br /&gt;
/opt/etc/init.d/S80keen-pbr       status&lt;br /&gt;
/opt/etc/init.d/S56dnsmasq        status&lt;br /&gt;
/opt/etc/init.d/S51nfqws2         status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Подсказка:&#039;&#039;&#039; На &amp;lt;code&amp;gt;http://&amp;amp;lt;LAN-IP&amp;amp;gt;:8765&amp;lt;/code&amp;gt; на вкладке &#039;&#039;&#039;Главная&#039;&#039;&#039; сразу видно состояние sing-box (PID, версия) и TUN-интерфейса. На вкладке &#039;&#039;&#039;Логи&#039;&#039;&#039; можно почитать &amp;lt;code&amp;gt;sing-box&amp;lt;/code&amp;gt;-лог (фильтр поверх &amp;lt;code&amp;gt;ndmc show log&amp;lt;/code&amp;gt;) и собственный лог helper&#039;а, не залезая в SSH.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка keen-pbr ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
keen-pbr --config /opt/etc/keen-pbr/config.json status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В выводе должно быть &#039;&#039;&#039;&amp;lt;code&amp;gt;Overall: OK&amp;lt;/code&amp;gt;&#039;&#039;&#039;, а у outbound &amp;lt;code&amp;gt;vpn [interface] iface=singtun&amp;lt;/code&amp;gt; — все строки со статусом &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Проверка туннеля ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Через локальный mixed-proxy sing-box&lt;br /&gt;
curl -sS --socks5-hostname 192.168.10.1:7891 https://api.ipify.org&lt;br /&gt;
&lt;br /&gt;
# Прямо через TUN&lt;br /&gt;
curl -sS --interface singtun https://api.ipify.org&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба должны вернуть IP вашего VPN-узла.&lt;br /&gt;
&lt;br /&gt;
== Проверка DNS ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Должен вернуться реальный публичный IP (НЕ из 198.18.x.x)&lt;br /&gt;
nslookup example.com 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Self-test keen-pbr — должен вернуть 127.0.0.88&lt;br /&gt;
nslookup check.keen.pbr 127.0.0.1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Проверка ipset и маршрутизации ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Какие IP уложены в bypass-список (должны быть реальные публичные)&lt;br /&gt;
ipset list kpbr4d_bypass | tail -20&lt;br /&gt;
&lt;br /&gt;
# Маршрутная таблица для помеченного трафика&lt;br /&gt;
ip route show table 151&lt;br /&gt;
# Должна быть строка вида: default dev singtun scope link&lt;br /&gt;
&lt;br /&gt;
# Помечает ли iptables пакеты&lt;br /&gt;
iptables -t mangle -L KeenPbrTable -n -v&lt;br /&gt;
# Счётчики MARK должны увеличиваться при тестовом запросе&lt;br /&gt;
&lt;br /&gt;
# NAT и FORWARD для singtun (наш хук)&lt;br /&gt;
iptables -t nat -S POSTROUTING | grep singtun&lt;br /&gt;
iptables -S FORWARD | grep singtun&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Частые проблемы ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Симптом&lt;br /&gt;
!Причина&lt;br /&gt;
!Решение&lt;br /&gt;
|-&lt;br /&gt;
|У клиентов нет интернета совсем&lt;br /&gt;
|dnsmasq keen-pbr остановлен или поломан&lt;br /&gt;
|Откатить &amp;lt;code&amp;gt;opkg dns-override&amp;lt;/code&amp;gt; через &amp;lt;code&amp;gt;192.168.1.1/a&amp;lt;/code&amp;gt;: &amp;lt;pre&amp;gt;no opkg dns-override&lt;br /&gt;
system configuration save&amp;lt;/pre&amp;gt;и перезагрузить роутер&lt;br /&gt;
|-&lt;br /&gt;
|Сайт из списка не открывается, но другие работают&lt;br /&gt;
|Старый DNS-кеш на клиенте&lt;br /&gt;
|&amp;lt;code&amp;gt;ipconfig /flushdns&amp;lt;/code&amp;gt; + перезапуск браузера&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;nslookup site.com 127.0.0.1&amp;lt;/code&amp;gt; возвращает &amp;lt;code&amp;gt;198.18.x.x&amp;lt;/code&amp;gt;&lt;br /&gt;
|DNS прошивки подменяет ответы — сервер &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; не удалён в UI keen-pbr&lt;br /&gt;
|Проверьте Шаги 4 и 5 раздела &#039;&#039;&#039;3.3&#039;&#039;&#039; — должен быть удалён &amp;lt;code&amp;gt;keenetic_dns&amp;lt;/code&amp;gt; и добавлен &amp;lt;code&amp;gt;quad9&amp;lt;/code&amp;gt; в &amp;quot;Основные DNS сервера&amp;quot;. После — &#039;&#039;&#039;Применить и перезапустить&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|keen-pbr UI показывает &amp;quot;dnsmasq недоступен&amp;quot;&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/dnsmasq.conf&amp;lt;/code&amp;gt; не содержит секцию &amp;lt;code&amp;gt;conf-script=...&amp;lt;/code&amp;gt; keen-pbr (вы ответили &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; на вопрос при установке)&lt;br /&gt;
|Восстановить шаблон: &amp;lt;code&amp;gt;cp /opt/usr/lib/keen-pbr/dnsmasq.conf.template /opt/etc/dnsmasq.conf &amp;amp;&amp;amp; /opt/etc/init.d/S56dnsmasq restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Домены в списке резолвятся в реальный IP, но не открываются (таймаут с клиента)&lt;br /&gt;
|Нет NAT/FORWARD-правил для singtun (хук не создан или не выполняется)&lt;br /&gt;
|Проверить &amp;lt;code&amp;gt;ls /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;. Если нет — пересоздать (раздел &#039;&#039;&#039;3.4&#039;&#039;&#039;). Если есть — выполнить вручную: &amp;lt;code&amp;gt;/opt/etc/ndm/netfilter.d/40-singtun-nat.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|sing-box не стартует, в логе &amp;quot;no firewall backend&amp;quot;&lt;br /&gt;
|В PATH процесса нет &amp;lt;code&amp;gt;iptables&amp;lt;/code&amp;gt;&lt;br /&gt;
|Запускать только через init-скрипт &amp;lt;code&amp;gt;/opt/etc/init.d/S99sing-box&amp;lt;/code&amp;gt; (он сам задаёт PATH)&lt;br /&gt;
|-&lt;br /&gt;
|YouTube/Discord работают медленно или не работают&lt;br /&gt;
|nfqws2 не запущен или его правила не применены&lt;br /&gt;
|&amp;lt;code&amp;gt;/opt/etc/init.d/S51nfqws2 status&amp;lt;/code&amp;gt; и при необходимости &amp;lt;code&amp;gt;restart&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|После установки nfqws-keenetic-web видна строка &amp;quot;Starting lighttpd... failed&amp;quot;&lt;br /&gt;
|Не ошибка — lighttpd стартует до полной регистрации модулей и поднимается на следующей попытке&lt;br /&gt;
|Проверить, что lighttpd сейчас запущен: &amp;lt;code&amp;gt;pgrep -af lighttpd&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;netstat -tlnup \| grep :90&amp;lt;/code&amp;gt;. Если процесс есть и порт слушается — всё в порядке.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Шпаргалка (все команды на одной странице) =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# === 1. ENTWARE (в командной строке Keenetic /a) ===&lt;br /&gt;
show media                                                # узнать UUID&lt;br /&gt;
opkg dns-override                                          # сразу включаем DNS-override&lt;br /&gt;
system configuration save&lt;br /&gt;
opkg disk ВАШ_UUID_ДИСКА:/ https://bin.entware.net/mipselsf-k3.4/installer/mipsel-installer.tar.gz&lt;br /&gt;
system configuration save&lt;br /&gt;
system reboot&lt;br /&gt;
&lt;br /&gt;
# === 2. После SSH-входа на :222 (пароль keenetic) ===&lt;br /&gt;
passwd  # сменить пароль root&lt;br /&gt;
&lt;br /&gt;
# === 3. sing-box (через singbox-helper, подтянет sing-box-go как зависимость) ===&lt;br /&gt;
echo &#039;src/gz singbox-helper https://wolfram0108.github.io/sing-box-helper/mipsel-3.4&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install singbox-helper curl nano wget-ssl&lt;br /&gt;
# UI: http://&amp;lt;LAN-IP&amp;gt;:8765 → вставить URI узла → Применить&lt;br /&gt;
&lt;br /&gt;
# === 4. keen-pbr ===&lt;br /&gt;
echo &#039;src/gz keen-pbr https://repo.keen-pbr.fyi/repository/stable/keenetic/current/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install keen-pbr                                      # на вопрос про dnsmasq.conf — ответить y&lt;br /&gt;
&lt;br /&gt;
# Дальше — UI на http://my.keenetic.net:12121:&lt;br /&gt;
#   * Outbounds: создать vpn (type=Interface, interface=singtun)&lt;br /&gt;
#   * Списки: создать bypass с доменами&lt;br /&gt;
#   * Правила маршрутизации: bypass → vpn&lt;br /&gt;
#   * DNS-серверы: удалить keenetic_dns&lt;br /&gt;
#   * DNS-правила → Основные DNS сервера: добавить quad9&lt;br /&gt;
#   * Применить и перезапустить&lt;br /&gt;
&lt;br /&gt;
# Скрипт NAT/FORWARD для singtun:&lt;br /&gt;
cat &amp;gt; /opt/etc/ndm/netfilter.d/40-singtun-nat.sh &amp;lt;&amp;lt; &#039;EOF&#039;&lt;br /&gt;
#!/opt/bin/sh&lt;br /&gt;
[ &amp;quot;$table&amp;quot; = &amp;quot;nat&amp;quot; ] || [ &amp;quot;$table&amp;quot; = &amp;quot;filter&amp;quot; ] || [ -z &amp;quot;$table&amp;quot; ] || exit 0&lt;br /&gt;
TUN=singtun&lt;br /&gt;
LAN=br0&lt;br /&gt;
PATH=/opt/sbin:/opt/usr/sbin:$PATH&lt;br /&gt;
ip link show &amp;quot;$TUN&amp;quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || exit 0&lt;br /&gt;
iptables -t nat -C POSTROUTING -o $TUN -j MASQUERADE 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -t nat -A POSTROUTING -o $TUN -j MASQUERADE&lt;br /&gt;
iptables -C FORWARD -i $LAN -o $TUN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $LAN -o $TUN -j ACCEPT&lt;br /&gt;
iptables -C FORWARD -i $TUN -o $LAN -j ACCEPT 2&amp;gt;/dev/null || \&lt;br /&gt;
  iptables -I FORWARD 1 -i $TUN -o $LAN -j ACCEPT&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /opt/etc/ndm/netfilter.d/40-singtun-nat.sh&lt;br /&gt;
&lt;br /&gt;
# === 5. nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws2-keenetic https://nfqws.github.io/nfqws2-keenetic/mipsel&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws2-keenetic                               # стартует автоматически&lt;br /&gt;
&lt;br /&gt;
# === 6. (опц) Веб-UI для nfqws2 ===&lt;br /&gt;
echo &#039;src/gz nfqws-keenetic-web https://nfqws.github.io/nfqws-keenetic-web/all&#039; &amp;gt;&amp;gt; /opt/etc/opkg.conf&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install nfqws-keenetic-web                            # &amp;quot;failed&amp;quot; в конце — нормально, см. 4.2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #d4edda; border-left: 4px solid #28a745; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Ссылки на проекты:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Entware: &amp;lt;code&amp;gt;https://github.com/Entware/Entware&amp;lt;/code&amp;gt;&lt;br /&gt;
* sing-box: &amp;lt;code&amp;gt;https://sing-box.sagernet.org&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/SagerNet/sing-box&amp;lt;/code&amp;gt;&lt;br /&gt;
* keen-pbr: &amp;lt;code&amp;gt;https://keen-pbr.fyi&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;https://github.com/maksimkurb/keen-pbr&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws2-keenetic: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws2-keenetic&amp;lt;/code&amp;gt;&lt;br /&gt;
* nfqws-keenetic-web: &amp;lt;code&amp;gt;https://github.com/nfqws/nfqws-keenetic-web&amp;lt;/code&amp;gt;&lt;br /&gt;
* zapret (upstream): &amp;lt;code&amp;gt;https://github.com/bol-van/zapret&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
= Приложение А. OpenSSH с ключом (опционально) =&lt;br /&gt;
Этот раздел описывает &#039;&#039;&#039;продвинутую&#039;&#039;&#039; настройку SSH-доступа: установку полноценного OpenSSH-сервера в Entware и настройку входа по публичному ключу вместо пароля.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание — этот раздел не нужен для обычной установки.&#039;&#039;&#039; Для всего, что описано в Этапах 1–4 (sing-box, keen-pbr, nfqws2), хватает стандартного входа по паролю через встроенный SSH-сервер Entware (dropbear) на порту &#039;&#039;&#039;222&#039;&#039;&#039; — настроенный в разделе 1.4. Делайте, только если вы &#039;&#039;&#039;понимаете, зачем это нужно&#039;&#039;&#039; лично вам.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Когда этот раздел действительно полезен ==&lt;br /&gt;
* Вы заходите на роутер часто и каждый раз вводить пароль раздражает.&lt;br /&gt;
* На том же ПК уже есть SSH-ключ, которым вы пользуетесь для других серверов, и хочется единообразия.&lt;br /&gt;
* Вы планируете давать роутеру доступ автоматизированным инструментам (скрипты, агенты, CI), которые не умеют вводить пароль.&lt;br /&gt;
* Хочется иметь возможность &#039;&#039;&#039;выключить парольную аутентификацию&#039;&#039;&#039; и оставить только ключ — это сильно повышает безопасность, если SSH будет проброшен наружу.&lt;br /&gt;
&lt;br /&gt;
Если ни один пункт не про вас — не тратьте время.&lt;br /&gt;
&lt;br /&gt;
== Что мы получим ==&lt;br /&gt;
* Дополнительный SSH-сервер OpenSSH на порту &#039;&#039;&#039;2022&#039;&#039;&#039; (рядом с dropbear на 222, не вместо).&lt;br /&gt;
* Вход по публичному ключу без ввода пароля.&lt;br /&gt;
* Удобный алиас на ПК — подключение одной командой &amp;lt;code&amp;gt;ssh keen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Встроенный dropbear на 222 остаётся работать — это «запасной» канал на случай, если что-то сломается в настройках OpenSSH.&lt;br /&gt;
&lt;br /&gt;
== А.1. Установка OpenSSH-сервера ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install openssh-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.2. Создание пользователя для privsep ==&lt;br /&gt;
OpenSSH требует системного пользователя &amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; для изоляции привилегий. В Entware его по умолчанию нет:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
echo &#039;sshd:*:22:22:sshd:/var/empty:/bin/false&#039; &amp;gt;&amp;gt; /opt/etc/passwd&lt;br /&gt;
echo &#039;sshd:*:22:&#039; &amp;gt;&amp;gt; /opt/etc/group&lt;br /&gt;
mkdir -p /var/empty&lt;br /&gt;
chmod 755 /var/empty&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.3. Генерация host-ключей ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh-keygen -A&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.4. Конфигурация sshd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nano /opt/etc/ssh/sshd_config&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Найдите и отредактируйте следующие параметры (раскомментируйте, если стоит &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Port 2022&lt;br /&gt;
PermitRootLogin yes&lt;br /&gt;
PubkeyAuthentication yes&lt;br /&gt;
PasswordAuthentication yes&lt;br /&gt;
StrictModes no&lt;br /&gt;
AuthorizedKeysFile /opt/root/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 10px 15px; margin: 10px 0; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Внимание про StrictModes:&#039;&#039;&#039; На Keenetic корневой каталог &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; смонтирован с правами &amp;lt;code&amp;gt;777&amp;lt;/code&amp;gt;. Это нормально для прошивки, но OpenSSH &#039;&#039;&#039;по умолчанию&#039;&#039;&#039; отказывает в pubkey-аутентификации, если хоть один каталог в пути до &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt; доступен на запись группе или всем. Параметр &amp;lt;code&amp;gt;StrictModes no&amp;lt;/code&amp;gt; отключает эту проверку.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните файл (&amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== А.5. Добавление публичного ключа ==&lt;br /&gt;
На вашем компьютере получите содержимое публичного ключа (Windows, PowerShell):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если ключа нет — создайте: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На роутере:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh&lt;br /&gt;
nano ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставьте строку публичного ключа (правой кнопкой мыши в PuTTY), сохраните файл, дайте права:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.6. Запуск ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd start&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.7. Открытие порта в фаерволе (если нужен внешний доступ) ==&lt;br /&gt;
Если планируете заходить с другой сети — добавьте правило в &#039;&#039;&#039;Сетевые правила → Межсетевой экран → Ethernet-подключение → Добавить правило&#039;&#039;&#039;:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Поле&lt;br /&gt;
!Значение&lt;br /&gt;
|-&lt;br /&gt;
|Имя&lt;br /&gt;
|&amp;lt;code&amp;gt;SSH OpenSSH&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Действие&lt;br /&gt;
|Разрешить&lt;br /&gt;
|-&lt;br /&gt;
|Протокол&lt;br /&gt;
|TCP&lt;br /&gt;
|-&lt;br /&gt;
|Порт назначения&lt;br /&gt;
|Один порт → &amp;lt;code&amp;gt;2022&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== А.8. Удобный алиас на ПК ==&lt;br /&gt;
Создайте/откройте файл &amp;lt;code&amp;gt;C:\Users\ВашИмя\.ssh\config&amp;lt;/code&amp;gt; и добавьте:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
Host keen&lt;br /&gt;
    HostName 192.168.1.1&lt;br /&gt;
    User root&lt;br /&gt;
    Port 2022&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
    IdentitiesOnly yes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставьте свой LAN-адрес роутера в &amp;lt;code&amp;gt;HostName&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь подключение — одной командой:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
ssh keen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== А.9. Откат ==&lt;br /&gt;
Если что-то пошло не так — встроенный dropbear на :222 продолжает работать, через него зайдите и удалите OpenSSH:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/opt/etc/init.d/S40sshd stop&lt;br /&gt;
opkg remove openssh-server&lt;br /&gt;
rm -f /opt/etc/init.d/S39sshd-prep&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Nfqws2-g-p.png&amp;diff=993</id>
		<title>Файл:Nfqws2-g-p.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Nfqws2-g-p.png&amp;diff=993"/>
		<updated>2026-05-12T18:01:31Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nfqws2-g-p&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keen-pbr-g.png&amp;diff=992</id>
		<title>Файл:Keen-pbr-g.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keen-pbr-g.png&amp;diff=992"/>
		<updated>2026-05-12T17:53:21Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Keen-pbr-g&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Singbox-helper-con.png&amp;diff=991</id>
		<title>Файл:Singbox-helper-con.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Singbox-helper-con.png&amp;diff=991"/>
		<updated>2026-05-12T17:51:14Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Singbox-helper-con&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Singbox-helper-g.png&amp;diff=990</id>
		<title>Файл:Singbox-helper-g.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Singbox-helper-g.png&amp;diff=990"/>
		<updated>2026-05-12T17:49:33Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Singbox-helper-g&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenpbr_outbounds.png&amp;diff=989</id>
		<title>Файл:Keenpbr outbounds.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenpbr_outbounds.png&amp;diff=989"/>
		<updated>2026-05-12T17:45:28Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_log_entware_installed.png&amp;diff=988</id>
		<title>Файл:Keenetic log entware installed.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_log_entware_installed.png&amp;diff=988"/>
		<updated>2026-05-12T17:43:54Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
	<entry>
		<id>https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_usb_disks.png&amp;diff=987</id>
		<title>Файл:Keenetic usb disks.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.mywolfram.ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Keenetic_usb_disks.png&amp;diff=987"/>
		<updated>2026-05-12T17:43:03Z</updated>

		<summary type="html">&lt;p&gt;Владимир: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Владимир</name></author>
	</entry>
</feed>