24 KiB
Карта проекта sing-box-rr-edition
Назначение
Репозиторий упаковывает RR Edition — урезанный для OpenWrt форк
sing-box-extended. Основной сценарий — network-only/tproxy-ориентированный
sing-box для роутеров с пакетами IPK (OpenWrt 24.10) и APK (OpenWrt 25.12).
Это не маленькая самостоятельная реализация: каталог sing-box-rr/ содержит
почти полный upstream-код github.com/sagernet/sing-box, а профиль RR Edition
задаётся прежде всего registry, build tags и упаковкой. Наличие исходника в
дереве не означает, что функция доступна в пакетном бинарнике.
Текущий снимок при составлении карты:
- ветка:
extended; - версия пакета:
1.14.1-rr-edition-2.7.2-r3; - Go-модуль:
github.com/sagernet/sing-box; - требование
go.mod: Go 1.26.4; - пакетные build tags:
with_quic,with_utls,with_clash_api.
Верхний уровень
VERSION— единственный источник версии и OpenWrtr-релиза.SOURCE_DATE_EPOCH— timestamp для воспроизводимой упаковки.build-ipk.sh— локальная сборка arm64 IPK для OpenWrt 24.10.build-apk.sh— локальная сборка arm64/x86_64 APK для OpenWrt 25.12.packages/— закоммиченные готовые пакеты по версии OpenWrt и архитектуре.sing-box-rr/— Go-код, OpenWrt feedMakefile, release-конфигурация и унаследованные upstream workflows.README.md— руководство по урезанию и упаковке; отдельные детали в нём устарели, см. раздел «Оговорки» ниже.
Главные точки изменения
| Задача | Источник истины |
|---|---|
Изменить версию/rN |
VERSION |
| Изменить возможности OpenWrt-пакета | sing-box-rr/release/DEFAULT_BUILD_TAGS_OTHERS |
| Добавить/убрать runtime-тип | sing-box-rr/include/registry.go и соответствующий include/*_stub.go |
| Изменить JSON surface и валидацию | sing-box-rr/option/ |
| Изменить сборку runtime-графа/порядок запуска | sing-box-rr/box.go |
| Изменить CLI | sing-box-rr/cmd/sing-box/ |
| Изменить маршрутизацию | sing-box-rr/route/ |
| Изменить DNS | sing-box-rr/dns/ |
| Изменить протокол | sing-box-rr/protocol/ и регистрация в include/ |
| Изменить OpenWrt service/config | sing-box-rr/release/config/ |
| Изменить feed package metadata | sing-box-rr/Makefile |
| Изменить локальную упаковку | build-ipk.sh и build-apk.sh |
Держать поведение в одном месте: сначала менять build tag/registry, затем config surface и только после стабилизации удалять физические каталоги.
Карта Go-кода
cmd/sing-box/— Cobra CLI:run,check,format,merge,generate,rule-set,tools,api,versionи вспомогательные команды.box.go— composition root. Создаёт и связывает все менеджеры, а также владеет фазамиPreStart,StartиClose.include/— связывает интерфейсы с конкретными реализациями. Здесь registry и build-tag пары «реализация/stub».adapter/— интерфейсы и менеджеры endpoint/inbound/outbound/provider/ service/certificate; граница между ядром и реализациями.option/— строгий JSON-конфиг, типы опций и первичная валидация. Неизвестные JSON-поля запрещены.route/— router, connection/network managers, route rules и rule sets.dns/— DNS router, правила, кэш и transports.protocol/— inbound/outbound реализации протоколов.transport/— сетевые транспорты, включая V2Ray transports.common/— общие сетевые, TLS, HTTP, netns, database и platform helpers.experimental/— Clash/V2Ray API, cache file и прочие расширения.service/— отдельные runtime services.test/— интеграционные протокольные тесты, поднимающие настоящийBox.
Production-путь запуска
OpenWrt procd
-> /usr/bin/sing-box run -c <config> -D <workdir>
-> cmd/sing-box/main.go: mainCommand.Execute
-> cmd/sing-box/cmd.go: preRun
рабочий каталог + include.Context(registries)
-> cmd/sing-box/cmd_run.go: run
readConfig -> сортировка путей -> mergeOptionsList
-> create
box.New -> Box.Start
-> box.go
managers -> Initialize -> Start -> PostStart -> Started
-> ожидание сигнала
SIGHUP: check -> Close -> перечитать конфиг -> создать новый Box
SIGINT/SIGTERM: Close -> выход
box.New строит граф в следующем центре: endpoint, inbound, outbound,
provider, DNS transport/router, network, connection, HTTP client, router,
service и certificate-provider managers. Box.Start запускает их фазами;
переставлять порядок без трассировки зависимостей нельзя. Box.Close закрывает
компоненты явно заданным порядком и агрегирует ошибки.
Конфиг может приходить из нескольких -c и каталогов -C. Все найденные
JSON-файлы сортируются по полному пути, затем сливаются. Без флагов CLI ищет
config.json в рабочем каталоге.
Фактический пакетный профиль
OpenWrt package builds читают только
sing-box-rr/release/DEFAULT_BUILD_TAGS_OTHERS:
with_quic,with_utls,with_clash_api
Из include/registry.go пакетный runtime получает:
- inbounds: TUN, redirect, TProxy, direct, SOCKS, HTTP, mixed, Shadowsocks, VMess, Trojan, ShadowTLS, VLESS, AnyTLS и QUIC-семейство Hysteria/TUIC/Hysteria2;
- outbounds: direct, block, selector/urltest/fallback, SOCKS, HTTP, Shadowsocks, VMess, Trojan, ShadowTLS, VLESS, Mieru, AnyTLS и то же QUIC-семейство;
- DNS transports: TCP, UDP, TLS, HTTPS, DNSCrypt/SDNS, hosts, local, fake-IP, fallback, QUIC и HTTP/3;
- services: OOM killer; profiler остаётся распознаваемым stub, потому что в
package profile нет
with_profiler; - Clash API и uTLS включены build tags.
Некоторые config types намеренно остаются распознаваемыми, но ведут в stub с
явной ошибкой: Naive outbound без with_naive_outbound, удалённые
failover/SSR/старый WireGuard, VPN endpoints, resolved/SSMAPI и функции без
QUIC при выключенном with_quic. Provider manager и поле providers ещё есть
в общем графе, но package registry провайдеров пуст.
Полные DEFAULT_BUILD_TAGS, DEFAULT_BUILD_TAGS_WINDOWS и Docker profile —
другие, значительно более широкие профили. Не использовать их как описание
возможностей опубликованного OpenWrt-пакета.
URLTest и NetworkQuality
URLTest реализован в sing-box-rr/protocol/group/urltest.go, а измерение и
политика выбора по пропускной способности — в соседнем
protocol/group/networkquality.go. Сам измеритель не дублируется: группа
использует существующий common/networkquality.Run через dialer конкретного
outbound.
Конфигурация URLTest поддерживает:
strategy: "latency"— поведение по умолчанию и прежний выбор по задержке;strategy: "networkquality"— среди доступных по URLTest участников выбирается максимальный комбинированный scoredownload capacity (bit/s) / URLTest delay (ms); при равном score побеждает меньшая задержка, а нулевая считается равной1ms;networkquality_ttl— минимальный интервал до повторного сетевого замера, период автоматического теста и TTL кэша; по умолчанию и минимум30m;networkquality_target—appleпо умолчанию, встроенныйcloudflareилиcustom;networkquality_config_url— Config v1 URL дляcustom; если target не указан, наличие URL автоматически выбираетcustom;networkquality_download_urlиnetworkquality_upload_url— прямые endpoint-ы дляcustomвместо Config v1;networkquality_probe_url— необязательный маленький download endpoint для latency/responsiveness probes; по умолчанию берётсяnetworkquality_download_url.
Apple использует прежний публичный Mensura Config v1. Cloudflare задан
встроенным Config v1 с официальными endpoint-ами
h3.speed.cloudflare.com/__down и h3.speed.cloudflare.com/__up, поэтому
отдельный внешний config-файл ему не нужен. Собственный
network-quality/goserver подключается как custom: голый host или корневой
URL автоматически дополняется /.well-known/nq, полный URL с путём остаётся
без изменений. Config URL и прямые URL взаимоисключаются; в прямом режиме
download, upload и probe должны использовать один host.
Успешные NetworkQuality-замеры дедуплицируются между всеми URLTest-группами
одного Box. Идентичность участника намеренно упрощена до нормализованного
server:port; ключ также включает target, max runtime и режим HTTP/3. Поэтому
разные credentials/transports на одном server:port используют общий замер.
Общий common/urltest.HistoryStorage хранит последний успешный результат и
объединяет одновременно запущенные проверки, а локальная карта группы по-прежнему
публикует результат под tag каждого своего участника. Если владелец общей
проверки отменён, независимый ожидающий запрос с живым контекстом повторно
захватывает ключ и запускает измерение сам, а не наследует чужой context.Canceled.
Один общий слот в HistoryStorage разрешает не более одного реального
NetworkQuality-теста на весь Box; разные ключи из разных групп ждут в общей
очереди, а ожидание прерывается контекстом запроса. Standalone daemon RPC
StartNetworkQualityTest использует тот же слот, поэтому тоже не пересекается с
групповыми замерами.
При strategy: "networkquality" первый автоматический замер запускается после
начального latency URLTest, затем повторяется через TTL после завершения
предыдущего автоматического прохода. Переключение с latency на
networkquality также запускает замер сразу и заново отсчитывает TTL после его
завершения; в режиме latency автоматический таймер остановлен. До истечения TTL
ручной API-вызов переиспользует кэш. Сам TTL не исключает старый download из
сортировки. Обычная ошибка повторного замера публикуется в API, но не удаляет
последние успешные capacity и исходный timestamp: они остаются в выборе до нового
успешного результата.
HTTP 429 распознаётся как отдельный rate-limit: API возвращает участнику
rateLimited: true и текст ошибки, но сохраняет capacity и исходный timestamp
последнего успешного замера. Такой download продолжает участвовать в выборе,
независимо от его возраста; без сохранённого результата
алгоритм возвращается к latency. Сам ответ 429 запускает новый TTL-интервал:
повторные API-вызовы внутри него возвращают сохранённый rate-limit без нового
сетевого теста.
Upload capacity измеряется, хранится и отдаётся API, но в сортировке не
участвует. Если успешных download-результатов нет, выбранный режим
networkquality автоматически возвращается к latency-алгоритму. Участники
с известной URLTest-задержкой тестируются строго последовательно; участник без
delay не занимает общий слот и получает в API ошибку
URLTest delay unavailable. Предыдущие успешные capacity и timestamp такого
участника сохраняются, но без delay он не участвует в выборе; после появления
нового delay сохранённый download снова становится доступен сортировке.
Результат каждого участника, включая пропущенного, публикуется API сразу после
обработки. Неполная первая карта в выборе не участвует: до полного
опроса всей текущей группы URLTest выбирает по задержке. NetworkQuality только публикует полный
snapshot и сам не меняет outbound; комбинированный download/latency-выбор применяет следующий
полный URLTest-проход. Отдельные delay-ответы Clash API обновляют history, но не переключают
NetworkQuality-группу по частично обновлённой карте задержек. При повторных замерах предыдущий полный snapshot
действует до готовности нового; отмена
оставляет частичные API-результаты видимыми, но не заменяет завершённый snapshot.
Живые delay-замеры всех участников сохраняются, но в history самой NetworkQuality-группы
публикуется committed delay выбранного now, обновляемый только после полного URLTest-прохода.
Публикуемая API-история живёт в памяти конкретной URLTest-группы, общий кэш —
в памяти Box; ни то ни другое не сохраняется между перезапусками.
Runtime-управление находится в Clash meta API
experimental/clashapi/api_meta_group.go:
GET|PUT /group/{name}/strategy— прочитать или изменить стратегию;GET /group/{name}/networkquality— получить последний замер;POST /group/{name}/networkquality— последовательно обработать всех участников и измерить только имеющих URLTest delay; тело принимаетmaxRuntimeSecondsиhttp3.
Ответ NetworkQuality содержит progress.current и progress.total: во время
прохода это 1-based номер обрабатываемого участника и фиксированный размер
снимка группы на старте; вне активного прохода оба значения равны нулю.
Контракт между API и группой находится в adapter/experimental.go: прежний
URLTestGroup сохранён для source compatibility, а новые методы собраны в
расширяющем NetworkQualityURLTestGroup. JSON-поля конфига находятся в
option/group.go, а регрессионные тесты выбора и конкурентности — в
protocol/group/networkquality_test.go. Встроенный Cloudflare Config и
передача inline-конфига в общий измеритель проверяются в
common/networkquality/networkquality_test.go.
OpenWrt-интеграция и упаковка
UCI-конфиг release/config/openwrt.conf по умолчанию включает сервис и задаёт:
- конфиг:
/etc/sing-box/config.json; - workdir:
/usr/share/sing-box; - stderr logging: включён.
Init script release/config/openwrt.init использует procd, следит за файлом
конфига, включает respawn и reload trigger, выставляет высокий nofile.
Оба локальных скрипта:
- читают версию и
SOURCE_DATE_EPOCHиз корня; - читают package tags из
DEFAULT_BUILD_TAGS_OTHERS; - собирают
./cmd/sing-boxвнешним Go с-trimpath, version ldflag и опциональным CGO/musl toolchain; - укладывают бинарник, JSON/UCI/init/keep-файлы;
- формируют package metadata и сохраняют артефакт в
packages/.
Пакет называется sing-box-rr, предоставляет sing-box и sing-box-tiny,
конфликтует с sing-box, зависит от ca-bundle, kmod-inet-diag, kmod-tun
и создаёт пользователя sing-box с UID/GID 5566. Совместимость с
sing-box-tiny нужна downstream-пакетам вроде ZeroBlock.
Локальные пути SDK и /usr/local/go/bin/go жёстко заданы в скриптах. Их можно
переопределять только предусмотренными переменными; переносимость не
предполагать. IPK helper поддерживает arm64, APK helper — arm64 и x86_64.
Feed sing-box-rr/Makefile поддерживает OpenWrt aarch64 и x86_64 и также
использует внешний Go вместо SDK golang/host.
Базовые команды из корня:
./build-ipk.sh arm64
./build-apk.sh arm64
./build-apk.sh x86_64
--force удаляет выделенные Go build/module caches в /tmp перед сборкой.
Тесты и проверки
В дереве 110 файлов *_test.go. Основные группы:
common/— низкоуровневые helpers;test/— интеграционные тесты протоколов, создающие настоящийbox.Box;route/— запуск соединений, rule-set lifecycle и семантика правил;dns/— race/fallback/order и transport multiplexer;option/— строгий JSON и валидация;transport/,service/,experimental/,adapter/— точечные тесты.
Интеграционный harness в test/box_test.go вызывает production-путь
include.Context -> box.New -> Box.Start -> Box.Close. Часть тестов требует
сетевых возможностей/root; upstream workflow на Unix запускает весь suite
через sudo.
Рекомендуемая локальная проверка из sing-box-rr/:
BUILD_TAGS="$(cat release/DEFAULT_BUILD_TAGS_OTHERS)"
/usr/local/go/bin/go test -tags "$BUILD_TAGS" -ldflags "$(cat release/LDFLAGS)" ./...
Для полного соответствия CI на Unix может понадобиться -exec sudo. Go
toolchain должен удовлетворять go.mod; build scripts выставляют
GOTOOLCHAIN=go<version>+auto, если переменная не задана явно.
Workflow-файлы находятся в sing-box-rr/.github/workflows/, а не в корневом
.github/workflows/ этого wrapper-репозитория. Поэтому не считать их
автоматически действующим CI корневого репозитория без отдельной проверки
хостинга; использовать их как upstream-рецепты test/lint/build.
Оговорки и известные несоответствия
go.modтребует Go 1.26.4, хотяREADME.mdещё упоминает 1.26.1. Источник истины —go.mod.README.mdпоказывает./build-ipk.sh x86_64, но текущий IPK helper принимает толькоarm64|all; x86_64 реализован только в APK helper/feed.README.mdговорит, что Naive outbound исключён из default builds. Это верно для package profileDEFAULT_BUILD_TAGS_OTHERS, но полныйDEFAULT_BUILD_TAGSвсё ещё содержитwith_naive_outbound.- Дефолтный
release/config/config.jsonслушает:::8080 и содержит опубликованный статический Shadowsocks-пароль. Это пример, а не безопасная production-конфигурация; пароль и bind policy нужно менять до установки на доступный извне роутер. - В репозитории закоммичены бинарные IPK/APK. После изменения версии, tags, registry или release config нужно пересобрать нужные архитектуры и проверить, что metadata и содержимое пакетов синхронны с исходниками.
Правило работы с проектом
Перед изменением сначала проследить существующий production call chain и найти владельца поведения. Для урезания использовать один путь:
- package build tag;
- registry или build-tag stub;
- проверка валидных конфигов и production-тестов;
- только затем чистка option/constant/source surface.
Не дублировать feature policy одновременно в новых скриптах и Go-коде.
Версия живёт в VERSION, package tags — в DEFAULT_BUILD_TAGS_OTHERS,
runtime registration — в include/registry.go.