Files
shater/SPECS/014-CLASH_API_TO_COMMANDCLIENT_MIGRATION/SPEC.md
T
Leadaxe c35ccd0ea7 fix(build): restore with_clash_api on desktop/CLI — drop is AAR-only
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.
2026-06-30 13:23:53 +03:00

12 KiB
Raw Blame History

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 (commit 57b5b5e5) — но изначально ошибочно срезано и с десктопа, исправлено в §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. Критерии приёмки

  1. (✅ rc.1) Сборка без with_clash_api: компилируется; конфиг с experimental.clash_api → fail-fast с понятной ошибкой; lx-конфиги стартуют.
  2. (✅ rc.3) Android стартует на сборке без with_clash_api (PlatformLogWriter не форсит Clash-сервер); десктоп не затронут; device-verified.
  3. Функциональный паритет с Clash по нужным UI возможностям — критерии в SPEC 015.
  4. Тронутые файлы переезда: 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.