The guard refused to start an AmneziaWG endpoint whose detour chain reached a WireGuard one, and refused silently: not an error, just started=false, after which every dial failed with "WireGuard is not ready yet". A selector hook went further and suspended an already-working node the moment its group switched to a WireGuard member. It existed because AmneziaWG inside WireGuard hung the kernel on Android. We do not ship Android, upstream dropped the guard once the cause was gone, and the cure landed here yesterday — the ClientBind reserved-gate plus the submodule pin that carries its twin. So the tree held both the cure and the prohibition on using it, and the configuration simply did not come up while looking like a node that "just does not work". Also takes the two fixes that belong with it. ClientBind.conn was read on a lock-free fast path and written under a mutex; upstream found that race with the same end-to-end test we wrote yesterday, so we had taken one half of a pair again. And the outer WireGuard UDP socket forced DF, unlike direct, hysteria and tuic — with encapsulation the datagram regularly exceeds the path MTU and the kernel drops it instead of fragmenting, a symptom indistinguishable from the bug we spent yesterday on. The race needed its own test: the existing e2e run did not flag it under -race even at -count=15. Eight goroutines over both connect branches reproduce it deterministically, naming the lock-free read and the guarded write. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SPECS — sing-box-lx (Spec Kit)
Все задачи — папки NNN-NAME. Внутри: SPEC.md → PLAN.md → TASKS.md → IMPLEMENTATION_REPORT.md.
Имя папки: NNN-NAME
| Часть | Значение | Расшифровка |
|---|---|---|
| NNN | 001, 002, … | Сквозной номер — стабильный якорь, не меняется никогда |
| NAME | UPPER_SNAKE | Название |
Имя папки не несёт тип/статус: они меняются по ходу задачи, а имя должно
оставаться стабильным (на него ссылаются из кода, доков и сабмодуля). Ссылки
давай короткой формой SPECS/NNN-NAME — она переживёт смену статуса.
Тип и статус — в шапке SPEC.md + Roadmap
Источник правды по типу/статусу — таблица-шапка в начале SPEC.md каждой
задачи; Roadmap ниже её агрегирует.
| Поле | Значение |
|------|----------|
| Тип | F (feature) \| B (bug) \| Q (question/исследование) |
| Статус | N (new) \| O (open) \| W (wait) \| C (complete) |
Файлы внутри папки
| Файл | Назначение |
|---|---|
| SPEC.md | Что и зачем — проблема, требования, критерии приёмки |
| PLAN.md | Как строить — архитектура, изменяемые файлы, зона касания upstream |
| TASKS.md | Чеклист по этапам |
| IMPLEMENTATION_REPORT.md | Отчёт после реализации |
| HISTORY.md | Хронология: как делали раньше, почему переделали. Только при смене архитектуры фичи (см. правило структуры ниже) |
Структура SPEC.md — актуальное состояние сверху, НЕ хронология
Правило: SPEC.md описывает ТЕКУЩУЮ (актуальную) архитектуру фичи в первую очередь. Порядок разделов — от актуального состояния к деталям, НЕ по хронологии разработки.
- Верх SPEC.md = как фича устроена СЕЙЧАС (актуальная архитектура), затем детали/контракт/критерии.
- НЕ вести SPEC.md как дневник («сначала сделали так, потом в rc.9 исправили…»). Пометки вида «ИСПРАВЛЕНО в rc.N», «прежнее утверждение неверно», отвергнутые подходы — НЕ в SPEC.md.
- Когда архитектура фичи МЕНЯЕТСЯ (переделали механизм): SPEC.md переписывается под новое состояние, а старое состояние + обоснование смены (как делали, почему это оказалось неверно, почему выбрали новое) выносятся в HISTORY.md этой же папки.
- Читатель SPEC.md должен понять, как всё работает СЕЙЧАС, не продираясь через историю решений. История — по запросу, в HISTORY.md.
Конфигурация фич
Пользовательский конфиг XHTTP и AmneziaWG 2.0 (поля + примеры) — ../docs-lx/lx-config.md.
Корень SPECS
| Файл | Назначение |
|---|---|
| CONSTITUTION.md | Неизменяемые принципы, приоритеты, запреты |
| IMPLEMENTATION_PROMPT.md | DoD, git/ребейз-ритуал, контракт выхода |
Workflow
- Папка
SPECS/NNN-NAME/(следующий номер). В шапке SPEC.md —Тип+Статус: N. - SPEC.md → PLAN.md → TASKS.md.
- Реализация по TASKS с учётом IMPLEMENTATION_PROMPT и CONSTITUTION.
- IMPLEMENTATION_REPORT.md, DoD-чеклист, статус в шапке SPEC.md и Roadmap →
C.
Roadmap (план задач)
| # | Задача | Статус | Суть |
|---|---|---|---|
| 001 | FORK_BOOTSTRAP | C | Remotes, ветка lx, Makefile.lx, версия -lx (ldflags), CI-скелет, lx-test/config — ✅ собрано/проверено |
| 002 | XHTTP_CLIENT_TRANSPORT | C | ✅ live-validated против Xray (3x-ui): packet-up/auto работают (handshake+DNS+HTTPS+download); stream-one — был баг, исправлен в 011 |
| 003 | AWG2_CLIENT_ENDPOINT | C | ✅ Функционален, проверен живым AWG2-сервером (handshake+keepalive+трафик). merged-форк Leadaxe/wireguard-go (sagernet+обфускация) через submodule; S1–S4/H1–H4/I1–I5 |
| 004 | BUILD_CI_RELEASE | C | ✅ Makefile.lx/libbox-теги, дешёвый CI (lint+build-check на push; cross×6+AAR на dispatch), lx-release.yml (релиз v1.13.13-lx.3 опубликован — 6 desktop + 2 AAR), lx-rebase.yml (авто-ребейз → PR/issue, демо зелёное) |
| 005 | AWG2_RANGED_MAGIC_HEADERS | C | ✅ Проверено живым awg2-сервером с ranged-конфигом (handshake+трафик). Диапазонные H1–H4 ("N-M") из awg2-экспортов: option.MagicHeader (number|string) → spec-строка в IpcSet; vendored wireguard-go уже умел |
| 006 | LINUX_MUSL_STATIC_ROUTER_BUILDS | C | ✅ CI-приёмка 4/4 арки статикой (amd64/arm64/armv7/mipsle-softfloat, statically linked, libdl=0, naive сохранён). musl-сборки под роутеры по подобию upstream build.yml (cronet-go + Chromium musl-toolchain, with_musl). Чинит #1 (libdl.so.2 на AsusWRT + armv7). CI-only, без Go-кода |
| 007 | AWG_OVER_WIREGUARD_DETOUR_GUARD | C | ✅ Код+тесты+DoD, Start-guard field-verified (Android lx.9). Bug в 003: AWG-нода с detour на WireGuard-туннель (плоский WG или AWG) вешает ядро на Android (AWG внутри WG). Два дополняющих guard'а: Start-guard (Endpoint.Start, статическая транзитивная detour-цепь — device не поднимается) + selector-guard (SelectOutbound — при переключении селектора на WG гасит AWG-потребителей до переключения, через ConsumersOf). Оба — вариант B (ядро живёт, узел не встаёт, ошибка в лог). Ленивый dialer-guard (lx.8) откачен. detour на VLESS и WG→AWG — разрешены. Чинит #2 |
| 008 | AWG_JUNK_PARAM_VALIDATION | C | ✅ Код+тесты+DoD. Bug в 003 (найден при 007): jmin > jmax паникует rand.Int в amneziawg-go (краш в timer-горутине). validateJunk в awgIpcLines отвергает на уровне конфига (check/старт), без паники. Узко — только краш-кейс; jc-несогласованность осознанно не трогаем (минимальный дифф, совместимость). Чинит #3 |
| 009 | WIRESOCK_MASQUERADE_PROFILES | C | ✅ Код+тесты+DoD; механизм проверен вживую (туннель + трафик на 009), релиз v1.13.13-lx.11. WireSock-стиль id/ip/ib (домен/протокол/браузер) — декларативный сахар над I1 CPS. Профили quic (1-RTT short header) / dns (EDNS OPT response) / stun (Binding Success Response) / sip (200 OK response), структуры портированы из open-source WireSock amneziawg-proxy/src/transform.rs (MIT). Механизм — I1 only (S1–S4 невозможен против WARP, сабмодуль не трогаем). id обязателен только для dns/sip (там идёт на провод), для quic/stun опционален. Строгая LDH-валидация домена (security-граница: инъекция в SIP/DNS). ib — без JA3-fingerprint (честно задокументировано). Все профили приняты реальным newObfChain; sing-box check зелёный; адверсариальный ревью (6 агентов) — 0 находок |
| 010 | WG_ENDPOINT_GRO_SPLIT_BRAIN | C | ✅ Корень подтверждён на железе, фикс верифицирован (download 0.44→20.7 Mbps), вмержен в lx (lx.14). Bug: WG-endpoint без detour на Android режет download (GRO split-brain — UDP_GRO включён, а receive-путь linux-only). Фикс — гейт UDP_GRO за !android в сабмодуле wireguard-go (conn/). UDP/WG-only |
| 011 | XHTTP_STREAM_ONE_DOWNLINK | C | ⚠️ Принято на синтетике; лайв НЕ прогонялся (нет reality+xhttp ноды). Bug в 002 (жалоба): vless+reality+xhttp+mode:auto не работал. Корень (сверено с Xray + issue #5635 + hiddify): stream-one слал <path>/<sessionId>, а Xray-сервер роутит stream-one только при пустом sessionId → downlink не-VLESS → unknown version. Фикс: голый путь без sessionId; mode:auto+reality → stream-one (детект reality по имени типа, без with_utls-зависимости). Юнит-тесты (URL-layout + reality-детект), check, сборки зелёные. Ветка lx-xhttp-streamone, в lx не влито; лайв — открытый TODO в REPORT |
| 012 | TCP_DOWNLINK_STALL_ZOMBIE_CONNS | C | ⚠️ НЕ воспроизводится на lx.14 (статус закрытия — «not reproducible»). Симптом (↑517 ↓0, WhatsApp/Telegram «висят») наблюдался на РАЗНЫХ нодах, включая WG → зонтик над «↓0»-сталлом, не один баг. WG-долю закрыл 010 (UDP/WG-only). Для не-WG (VLESS/reality) отдельного код-фикса нет — симптом сейчас не воспроизводится без подтверждённого объяснения. Артефакты: зонд LX_CONN_TRACE (не прогнан в бою), PROBE.md |
| 013 | PACKAGE_NAME_REGEX_RULE_ITEM | C | ✅ Код+тесты, сборки/vet/gofmt зелёные. Бэкпорт апстрим-фичи 1.14 (941ce58b) на базу 1.13.13 без полной миграции: rule-item package_name_regex (regex-матчинг Android-пакета) для route/DNS/headless. Новый route/rule/rule_item_package_name_regex.go + поля/регистрация/cond в 6 файлах роутинга. Хунк RuleSetVersion5 из коммита НЕ переносился (это rule-set v5, не фича). Полная миграция на 1.14 отложена до v1.14.0 stable (feasibility: ~1,5–2 дня, риск — ребейз AWG-подмодуля; lx-rebase.yml сам исключает alpha) |
| 014–021 | (см. шапки в SPECS/NNN-*/SPEC.md) |
C | Command-протокол RPC (014/015), connections-мьютекс (016), Connection.Detour (017), DNS-query-стрим (018), URLTest пул/sticky (019), multi-WG idle-suspend (020), MASQUE CONNECT-IP outbound (021). Источник статуса — шапка каждого SPEC.md |
| 023 | MUSL_TOOLCHAIN_MIRROR | C | 🛠 Durable-зеркало Chromium musl-тулчейна. snapshot.debian.org периодически 503-ит и блокирует релиз (v1.14.0-lx.2-rc.1 падал дважды на нём; actions/cache промахивается из-за ref-scoping тегов). Producer-workflow (lx-musl-toolchain-mirror.yml) заливает собранный тулчейн 4 арок в release-ассет musl-toolchain-cache; lx-release.yml восстанавливает его на cache-miss ДО фолбэка на snapshot. Источники: actions/cache → lx-mirror → snapshot. Обе workflow lx-owned, upstream-дифф нулевой. Зеркало заполнено, restore проверен |
| 022 | LX_DEEP_AUDIT | C | 🔍 Аудит-исследование + ремедиация. Многоагентный аудит всей LX-дельты по 10 осям + адверсариальная верификация каждой находки. 32→27 подтверждено (0 critical, 1 high, 1 medium, 6 low, 19 nit), 5 опровергнуто. Исправлено 24/27 (ветка lx-spec022-audit-fixes): P0 #1 masque h2 CONNECT висел навечно (ctx игнорировался); P1 #2 idle-suspend воскрешал guard-suspended AWG (hang ядра Android); + все P2/P3. Осознанно пропущены #12/#17/#18 (санкционированные компромиссы / ребейз-цена). Реестр + ремедиация в SPEC.md |
| 024 | RUNTIME_LOOP_GUARD | DEFERRED | 🔁 Guard от runtime-колец в detour/selector. Статическое кольцо ядро отклоняет на старте (circular outbound dependency); собранное в рантайме через SelectOutbound — нет защиты → fatal stack overflow, падение процесса. Проработана событийная модель E1–E5 + topologyMu-линеаризация гонки; адверсарская проверка (7 агентов): deadlock/false-positives HOLDS, но TOCTOU BREAKS — даже полный ядерный guard негерметичен без покрытия endpoint-mgr/Remove/history-side-channels и без устранения pointer↔tag расхождения после рантайм-Create. Решение (2026-07-06): защита на уровне UI (LxBox), ядро не трогаем. Design record: SPEC.md |
Вне этого репозитория: потребление ядра лаунчером (
singbox-launcher) — парсингtype=xhttpв реальный XHTTP-транспорт (сейчас023маппит его вhttpupgrade), AWG-поля в визарде, заменаbin/sing-box. Это отдельные задачи в репозитории лаунчера.