SPEC 014 dropped with_clash_api because LxBox (Android) drives the core over the native libbox CommandClient, making the Clash REST server dead weight in the AAR. But the drop landed in the shared Makefile.lx LX_TAGS, which also feeds every desktop/CLI release build (mac/windows/linux-musl via `make -s lx-print-tags`). A CLI binary has no CommandClient channel — it is managed by external dashboards (yacd/MetaCubeXD) over the Clash REST API — so every desktop release since rc.1 shipped with no way to manage the core; a config with experimental.clash_api failed fast. CI stayed green (lx-ci BASE_TAGS kept the tag), so it was invisible in CI. Restore with_clash_api to the desktop LX_TAGS; leave build_libbox (AAR) unchanged. The two tag sets now diverge by design: desktop = with Clash API, AAR = without. Verified: desktop binary builds with with_clash_api in Tags; `check` accepts an experimental.clash_api config; the Clash REST server comes up live (endpoints answer 401 security-middleware, not the stub's fail-fast). Docs: Makefile.lx comment, SPEC 014 (§2/§3.1 scoped to AAR + new §3.4), lx-release.yml tag comment + notes line, changelog rc.17.
12 KiB
SPEC: 014 — CLASH_API_TO_COMMANDCLIENT_MIGRATION
| Поле | Значение |
|---|---|
| Тип | F (feature) — смена канала управления ядром (client-side) |
| Статус | A (accepted) — with_clash_api drop из AAR в v1.14.0-lx.1-rc.1; box.go-фикс в rc.3; десктоп-регрессия исправлена в rc.17 (§3.4) |
Переезд управления ядром с Clash API на нативный libbox CommandClient — на Android. LxBox перестаёт использовать Clash REST API и переходит на нативный gRPC-канал StartedService (поверх unix-сокета). Из AAR-сборки убирается with_clash_api — отпадает HTTP-сервер Clash и связанный attack surface. Десктоп/CLI сохраняют with_clash_api (внешние дашборды ходят по Clash REST API; нативного CommandClient-канала у CLI нет) — см. §3.4.
Этот SPEC фиксирует сам переезд и его последствия. Доработки command-протокола, понадобившиеся, чтобы CommandClient заменил Clash API по функциональности (per-node delay, таблица правил, pull-снапшоты групп, фикс потери групп), вынесены в отдельный SPEC 015 — COMMAND_PROTOCOL_RPC_EXTENSIONS.
Scope: client/command-сторона only (§3.1 client-only).
1. Проблема / контекст
Графические клиенты sing-box исторически управляли ядром через два канала: Clash REST API (experimental.clash_api, под build-tag with_clash_api) и нативный libbox CommandClient. Clash API — это слой совместимости со сторонними дашбордами; для своего клиента на устройстве разработчики предполагают именно CommandClient (gRPC поверх локального unix-сокета).
LxBox переходит на CommandClient как единственный канал управления, потому что:
- это нативный, более богатый интерфейс (closed-connections история, per-event дельты соединений, ProcessInfo раздельными полями, NQ/STUN/Tailscale-инструменты) — то, что Clash REST не покрывает;
- Clash API — это лишний HTTP-сервер в процессе и открытый локальный порт (attack surface), не нужный, когда клиент ходит по нативному каналу;
- убрав
with_clash_api, мы уменьшаем дифф и размер AAR.
2. Цель
LxBox (Android) управляет ядром только через CommandClient; with_clash_api не входит в AAR-сборку. Конфиг, ссылающийся на experimental.clash_api, fail-fast с понятной ошибкой (а не молчаливо деградирует). Функциональный паритет с Clash API по нужным UI возможностям достигается доработками CommandClient — см. SPEC 015.
Важно (исправлено): дроп
with_clash_apiотносится только к Android AAR. Десктоп/CLI-бинари (mac/windows/linux) управляются внешними дашбордами (yacd/MetaCubeXD) именно через Clash REST API — нативного CommandClient-канала вне gomobile/libbox у них нет. Поэтомуwith_clash_apiостаётся в десктопLX_TAGS. Изначально (rc.1) тег был ошибочно убран и из десктоп-набора тоже — см. §3.4.
3. Требования
3.1 Дроп with_clash_api — только Android AAR
- Убрать
with_clash_apiизsharedTags(cmd/internal/build_libbox/main.go,// lx:-блок). ДесктопLX_TAGS(Makefile.lx) тег сохраняет — см. §3.4. - Без тега (в AAR) подключается
include/clashapi_stub.go— конфиг сexperimental.clash_apiполучаетclash api is not included in this build, rebuild with -tags with_clash_api(fail-fast, не молчаливый отказ). - lx-конфиги на Android
clash_apiне используют — управление идёт через CommandClient. - Сделано в
v1.14.0-lx.1-rc.1(commit57b5b5e5) — но изначально ошибочно срезано и с десктопа, исправлено в §3.4.
3.2 Доработки CommandClient → SPEC 015
Нативный CommandClient беднее Clash API по ряду возможностей, нужных UI (per-node delay-тест, таблица правил, pull-снапшоты групп/узлов, баг потери одно-узловых групп). Все эти доработки — в SPEC 015 (класс §3.6, build-tag with_lx_command). URLTestOutbound и GetRules уже зашиплены (rc.2); GetGroups/GetOutbounds + фикс len<2 — target rc.4. Здесь они только упоминаются как часть полного перехода; тех-спека — в 015.
3.3 Follow-on фикс — Android start fatal от with_clash_api-дропа (rc.3)
Удаление with_clash_api (§3.1) сделало любой старт на Android фатальным:
create clash-server: clash api is not included in this build — даже без
clash_api в конфиге. Корень в апстрим-box.go: PlatformLogWriter != nil
(всегда на Android/libbox) форсил needClashAPI = true (Clash-сервер исторически
был единственным наблюдателем логов/трафика), а needClashAPI ведёт к
NewClashServer(). Десктоп не затронут (PlatformLogWriter == nil).
Фикс (box.go, // lx: шов, commit 029acd11, пререлиз v1.14.0-lx.1-rc.3):
расщепить заботы — PlatformLogWriter взводит новый needObservable
(= needClashAPI || needAPIService || PlatformLogWriter != nil) для Observable
log factory + traffic/connection-tracker; только явный experimental.clash_api
по-прежнему взводит needClashAPI → NewClashServer. daemon уже nil-safe к
отсутствию clashServer → Clash-mode деградирует мягко. Проверено: стартует без
clash_api; всё ещё fail-fast с ним. Device-verified на реальном устройстве
(Android-старт работает на настоящем rc.3).
WATCH — апстрим issue SagerNet/sing-box#4240
(подан как чистый upstream-репро, форк НЕ упомянут). Когда апстрим починит —
снять наш // lx: шов в box.go на следующем ребейзе.
⚠️ #4240 УДАЛЁН (по состоянию на 2026-06-26 GitHub API отдаёт
HTTP 410 "This issue was deleted"). Удаление ≠ fix и ≠ closed/not-planned — это НЕ сигнал «можно снимать шов». Issue-ссылка как критерий снятия больше непроверяема. Новый критерий снятия шва: сверять не статус issue, а сам upstream-код — починили лиPlatformLogWriter-путь, который форсил Clash-сервер (Android-старт безwith_clash_api). До подтверждения в коде — живём на своём// lx:фиксе. (Аккаунт НЕ забанен: #3858/#3806 closed какcompleted, #4093 + PR #4094 живут; удаление #4240 — отдельная история, не бан.)
3.4 Фикс — with_clash_api ошибочно срезан и с десктопа (rc.17)
§3.1 в rc.1 убрал with_clash_api из обоих наборов тегов: и из sharedTags
AAR, и из десктоп LX_TAGS (Makefile.lx). Для AAR это верно (LxBox ходит по
CommandClient). Для десктопа — ошибка: mac/windows/linux-бинарь запускается как
CLI и управляется внешними дашбордами (yacd, MetaCubeXD, clash-dashboard)
исключительно через Clash REST API — нативного CommandClient-канала вне
gomobile/libbox у CLI нет. Без with_clash_api десктоп-юзер остался без способа
управлять ядром: конфиг с experimental.clash_api падает fail-fast.
Все релизные desktop/linux-musl сборки берут теги из
make -f Makefile.lx -s lx-print-tags (lx-release.yml),
поэтому баг ушёл во все desktop-артефакты rc.1…rc.16 молча (CI-проверка
lx-ci.yml BASE_TAGS clash_api держала, так что компиляция была зелёной — баг
не виден в CI, только в релизном артефакте).
Фикс: вернуть with_clash_api в десктоп LX_TAGS (Makefile.lx). AAR-набор
(build_libbox) не трогаем — там дроп остаётся в силе. Так два набора тегов
расходятся по дизайну: desktop = с Clash API, AAR = без. Десктоп-сборки снова
управляются через Clash REST API из коробки.
4. Критерии приёмки
- (✅ rc.1) Сборка без
with_clash_api: компилируется; конфиг сexperimental.clash_api→ fail-fast с понятной ошибкой; lx-конфиги стартуют. - (✅ rc.3) Android стартует на сборке без
with_clash_api(PlatformLogWriterне форсит Clash-сервер); десктоп не затронут; device-verified. - Функциональный паритет с Clash по нужным UI возможностям — критерии в SPEC 015.
- Тронутые файлы переезда:
cmd/internal/build_libbox/main.go(// lx:, дроп тега),Makefile.lx(LX_TAGS),box.go(// lx:шов §3.3).
5. Вне скоупа
- Тех-спека RPC-доработок CommandClient — в SPEC 015.
- Возврат
with_clash_apiради delay-тестов (гибрид) — отклонён: паритет достигается нативно (SPEC 015).
6. Ссылки
- SPEC 015 — COMMAND_PROTOCOL_RPC_EXTENSIONS — доработки CommandClient до уровня Clash-паритета.
- CONSTITUTION §3.5 (дистрибуция,
with_lx_commandв AAR), §3.6 (класс command-расширений). include/clashapi_stub.go(fail-fast при отсутствии тега);box.go(needClashAPI/needObservable).- Память:
lx-commandclient-extensions-spec014.