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.
120 lines
12 KiB
Markdown
120 lines
12 KiB
Markdown
# 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](../015-COMMAND_PROTOCOL_RPC_EXTENSIONS/SPEC.md)**.
|
||
|
||
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](../015-COMMAND_PROTOCOL_RPC_EXTENSIONS/SPEC.md).
|
||
|
||
> **Важно (исправлено):** дроп `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](../../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](../015-COMMAND_PROTOCOL_RPC_EXTENSIONS/SPEC.md)** (класс §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](https://github.com/SagerNet/sing-box/issues/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](../../.github/workflows/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](../015-COMMAND_PROTOCOL_RPC_EXTENSIONS/SPEC.md).
|
||
4. Тронутые файлы переезда: `cmd/internal/build_libbox/main.go` (`// lx:`, дроп тега), `Makefile.lx` (LX_TAGS), `box.go` (`// lx:` шов §3.3).
|
||
|
||
---
|
||
|
||
## 5. Вне скоупа
|
||
- Тех-спека RPC-доработок CommandClient — в [SPEC 015](../015-COMMAND_PROTOCOL_RPC_EXTENSIONS/SPEC.md).
|
||
- Возврат `with_clash_api` ради delay-тестов (гибрид) — отклонён: паритет достигается нативно (SPEC 015).
|
||
|
||
---
|
||
|
||
## 6. Ссылки
|
||
- [SPEC 015 — COMMAND_PROTOCOL_RPC_EXTENSIONS](../015-COMMAND_PROTOCOL_RPC_EXTENSIONS/SPEC.md) — доработки 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`.
|