Files
omarandClaude Opus 5 daaa0fda41 fix(wireguard): stop holding AmneziaWG down behind a WireGuard hop
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>
2026-07-26 04:53:32 +03:00
..

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

  1. Папка SPECS/NNN-NAME/ (следующий номер). В шапке SPEC.md — Тип + Статус: N.
  2. SPEC.md → PLAN.md → TASKS.md.
  3. Реализация по TASKS с учётом IMPLEMENTATION_PROMPT и CONSTITUTION.
  4. 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. Это отдельные задачи в репозитории лаунчера.