lx(specs): bootstrap Spec Kit — constitution, prompt, roadmap, specs 001-004

Establish sing-box-lx as a thin downstream of SagerNet/sing-box:
upstream + exactly two client-side features (XHTTP transport, AmneziaWG 2.0
endpoint), gated behind build tags, kept rebaseable onto upstream tags.

- SPECS/CONSTITUTION.md, IMPLEMENTATION_PROMPT.md, README.md
- SPECS/001 FORK_BOOTSTRAP, 002 XHTTP_CLIENT_TRANSPORT,
  003 AWG2_CLIENT_ENDPOINT, 004 BUILD_CI_RELEASE
This commit is contained in:
Leadaxe
2026-06-09 14:31:11 +03:00
parent 78b2e12fbd
commit 60a94bf0d7
16 changed files with 710 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
# Руководство для AI-агентов — sing-box-lx
`sing-box-lx` — **тонкий downstream** апстрима [SagerNet/sing-box](https://github.com/SagerNet/sing-box): upstream **плюс ровно две фичи** и ничего больше:
1. **XHTTP** — клиентский v2ray-транспорт (совместимость с Xray XHTTP).
2. **AmneziaWG 2.0 (AWG2)** — клиентский endpoint поверх WireGuard.
Главная ценность проекта — **согласованность с upstream**. Любое изменение оценивается по тому, насколько легко оно переживёт ребейз на следующий тег upstream.
---
## Что читать в первую очередь
| Документ | Назначение |
|----------|------------|
| **SPECS/CONSTITUTION.md** | Неизменяемые принципы: приоритеты, build-tag изоляция, минимальный дифф, запреты |
| **SPECS/IMPLEMENTATION_PROMPT.md** | DoD, git/ребейз-ритуал, команды сборки и тестов, контракт выхода |
| **SPECS/README.md** | Формат задач `NNN-T-S-NAME` (Spec Kit), workflow |
Перед реализацией задачи из `SPECS/` обязательно прочитай её **SPEC.md → PLAN.md → TASKS.md** и применяй **IMPLEMENTATION_PROMPT.md**.
---
## Жёсткие границы (детали — в CONSTITUTION)
- **Только две фичи.** Любая фича вне XHTTP/AWG2 — вне скоупа, спросить пользователя.
- **Go module path остаётся `github.com/sagernet/sing-box`** (для чистых ребейзов).
- **Всё новое — за build-tag** (`with_xhttp`, `with_awg`) и **в новых файлах/пакетах**.
- **Правки upstream-файлов** — только помеченными `// lx:` блоками, атомарными коммитами.
- **Никаких merge с upstream — только rebase.** `origin/lx` всегда ребейзится на тег.
- **Имя бинаря — `sing-box`** (drop-in для лаунчера); идентичность `-lx` — в версии.
- **Scope — client-only**: outbound/endpoint. Server/inbound отложены.
---
## Язык
Спеки, отчёты и ответы в чате — **русский**. Код, комментарии, коммиты — английский, в стиле upstream sing-box.
+39
View File
@@ -0,0 +1,39 @@
# PLAN: 001 — FORK_BOOTSTRAP
## 1. Канонический набор build-тегов lx
Единый источник истины (использовать в Makefile, CI, DoD):
```
with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_xhttp,with_awg
```
Хранить в `Makefile` (переменная `LX_TAGS`) и продублировать в `SPECS/CONSTITUTION.md` при изменениях.
## 2. Изменяемые / новые файлы
| Файл | Тип | Изменения |
|------|-----|-----------|
| `Makefile` | new (или дополнение) | Цель `lx-build`: `go build -tags "$(LX_TAGS)" -ldflags "$(LX_LDFLAGS)" -o sing-box ./cmd/sing-box`; переменные `LX_TAGS`, `VERSION=…-lx.$(LX_BUILD)` |
| `.github/workflows/lx-ci.yml` | new | Скелет: checkout → setup-go → `make lx-build` → `go vet` → `./sing-box check -c test/config/xhttp_smoke.json` (заглушка появится в 002) |
| `test/config/*.json` | new | Sample-конфиги для `sing-box check` (минимальный валидный, без фич — для 001) |
| `SPECS/001-.../IMPLEMENTATION_REPORT.md` | new | Отчёт |
> Версия: upstream хранит строку версии в `constant/version.go` (или собирается через ldflags в `cmd/sing-box`). Проверить фактический механизм и **задавать `-lx` суффикс через `-ldflags -X`**, не правя `constant/version.go` напрямую (иначе лишний `// lx:` дифф на каждый ребейз). Если upstream не поддерживает ldflags-override — тогда минимальная `// lx:` правка в `constant/version.go`.
## 3. Зона касания upstream
- В идеале **ноль** правок upstream-файлов (всё через новые файлы + ldflags).
- Допустимый минимум: одна `// lx:` строка в `constant/version.go`, если ldflags-override невозможен.
## 4. Порядок работ
1. Проверить механизм версии upstream (`constant/version.go`, `cmd/sing-box`).
2. `Makefile` с `LX_TAGS`/`LX_LDFLAGS`/`lx-build`.
3. Sample-конфиг + CI-скелет.
4. Прогнать DoD, заполнить отчёт.
## 5. Риски
- Версионный механизм upstream может не принимать ldflags-override — fallback на `// lx:` правку.
- `with_xhttp`/`with_awg` как несуществующие теги не ломают сборку (Go игнорирует неизвестные build-теги) — но файлов с этими тегами пока нет, это нормально.
+44
View File
@@ -0,0 +1,44 @@
# SPEC: 001 — FORK_BOOTSTRAP
Заложить скелет downstream'а `sing-box-lx`: remotes, рабочая ветка, build-теги, версия с `-lx`, конвенция маркеров `// lx:` и шаблон гейтинга. После задачи репозиторий — корректный «upstream + ноль фич», готовый принимать XHTTP (002) и AWG2 (003).
---
## 1. Проблема / контекст
`Leadaxe/sing-box-lx` — форк-зеркало upstream (родословная `SagerNet/sing-box` сохранена). Нужна повторяемая инфраструктура downstream'а, при которой каждое будущее изменение изолировано и ребейзопригодно (см. CONSTITUTION § 3).
## 2. Требования
### 2.1 Git
- `origin = Leadaxe/sing-box-lx`, `upstream = SagerNet/sing-box`. **(сделано)**
- Ветка `lx` базируется на стабильном теге `v1.13.13`. **(сделано)**
- Default branch на GitHub = `lx`; шумные зеркальные ветки (`dependabot/*`, `dev-*`, `copilot/*`) — вне внимания (можно удалить с origin, не обязательно).
### 2.2 Build-теги
- Ввести **`with_xhttp`** и **`with_awg`** как опознаваемые теги проекта (фактический код — в 002/003). Зафиксировать **канонический набор тегов сборки lx** в одном месте (см. PLAN), переиспользуемый в DoD и CI.
- Инвариант: без `with_xhttp`/`with_awg` бинарь ведёт себя как upstream.
### 2.3 Версия
- `sing-box version` должен печатать суффикс **`-lx.N`** (напр. `1.13.13-lx.1`).
- Суффикс задаётся при сборке (ldflags), не хардкодом в исходниках upstream (минимальный дифф).
### 2.4 Конвенции
- Документировать и применять маркеры правок upstream-файлов: `// lx:begin <feat>` … `// lx:end <feat>`.
- Принять шаблон гейтинга `include/<feat>.go` (+ `<feat>_stub.go`), как у upstream `include/wireguard.go`.
### 2.5 CI-скелет
- Минимальный workflow: сборка `lx` с каноническим набором тегов под linux/amd64 + `go vet` + `sing-box check` на sample-конфиге. (Полная матрица и авто-ребейз — в 004.)
## 3. Критерии приёмки
- `go build ./...` (без тегов) — ок.
- `go build -tags "<канон lx>" ./cmd/sing-box` — ок (теги пока no-op).
- Собранный бинарь: `./sing-box version` содержит `-lx.`.
- Имя выходного файла — `sing-box`.
- CI-скелет зелёный на push в `lx`.
## 4. Вне скоупа
- Любой код XHTTP/AWG (это 002/003).
- Полная CI-матрица, релизы, авто-ребейз (это 004).
+25
View File
@@ -0,0 +1,25 @@
# TASKS — 001-F-N-FORK_BOOTSTRAP
## Git / GitHub
- [x] `origin` = Leadaxe/sing-box-lx, `upstream` = SagerNet/sing-box
- [x] Ветка `lx` от тега `v1.13.13`
- [ ] Default branch на GitHub → `lx`, push `lx`
- [ ] (опц.) Удалить шумные зеркальные ветки с origin (`dependabot/*`, `copilot/*`, `dev-*`)
## Build / Version
- [ ] Изучить механизм версии upstream (`constant/version.go`, `cmd/sing-box`, ldflags)
- [ ] `Makefile`: `LX_TAGS`, `LX_LDFLAGS`, цель `lx-build` (output `sing-box`)
- [ ] Версия печатает `-lx.N` (через ldflags; иначе минимальный `// lx:` дифф)
## Конвенции
- [ ] Зафиксировать маркеры `// lx:begin/end` (в CONSTITUTION уже описаны — проверить применимость)
- [ ] Подтвердить шаблон `include/<feat>.go` + `<feat>_stub.go` на примере upstream `wireguard.go`
## CI-скелет
- [ ] `.github/workflows/lx-ci.yml`: build(lx tags) + vet + `sing-box check`
- [ ] `test/config/` sample-конфиг(и)
## Закрытие
- [ ] DoD-чеклист (CONSTITUTION § 3 / IMPLEMENTATION_PROMPT § 2)
- [ ] IMPLEMENTATION_REPORT.md
- [ ] Папка → статус `C`
@@ -0,0 +1,49 @@
# PLAN: 002 — XHTTP_CLIENT_TRANSPORT
## 1. Архитектура
**Было (upstream):** `transport/v2ray/transport.go::NewClientTransport` — `switch options.Type { case … }`.
**Станет:** реестр конструкторов.
```go
// transport/v2ray/registry.go (new)
var clientRegistry = map[string]ClientConstructor{}
func RegisterClient(typ string, ctor ClientConstructor) { clientRegistry[typ] = ctor }
```
- Встроенные типы регистрируются в `init()` (в `transport.go` или соседнем файле) — поведение для http/ws/quic/grpc/httpupgrade без изменений.
- `NewClientTransport` → `ctor, ok := clientRegistry[options.Type]`; нет — прежняя ошибка.
- XHTTP-конструктор регистрируется из пакета `v2rayxhttp` через `init()` **только** под `//go:build with_xhttp` (через проводящий файл, чтобы импорт пакета подтягивался лишь с тегом).
Конструктор XHTTP должен соответствовать сигнатуре `ClientConstructor` (см. upstream `transport.go`): `(ctx, dialer, serverAddr, options, tlsConfig) → (adapter.V2RayClientTransport, error)`. Опции достаются из `options.XHTTPOptions`.
## 2. Изменяемые / новые файлы
| Файл | Тип | Изменения |
|------|-----|-----------|
| `transport/v2ray/registry.go` | **new** | `clientRegistry`, `RegisterClient`, `init()` встроенных типов |
| `transport/v2ray/transport.go` | `// lx:` | `NewClientTransport`: `switch` → lookup в реестре (минимальная правка) |
| `transport/v2rayxhttp/*.go` | **new** | Клиент XHTTP: `client.go`, `conn.go`, `dialer.go`, `http.go`, `mux.go`, `upload_queue.go`, `writer.go` |
| `transport/v2rayxhttp/register.go` | **new** | `//go:build with_xhttp` — `init(){ v2ray.RegisterClient(C.V2RayTransportTypeXHTTP, New) }` |
| `constant/v2ray.go` | `// lx:` | `V2RayTransportTypeXHTTP = "xhttp"` |
| `option/v2ray_transport.go` | `// lx:` | одна строка: поле `XHTTPOptions` в `_V2RayTransportOptions` |
| `option/v2ray_xhttp.go` | **new** | тип `XHTTPOptions` (mode, path, host, headers, padding…) |
| `include/v2rayxhttp_stub.go` | **new** | `//go:build !with_xhttp` — понятная ошибка/нет регистрации |
| `test/config/xhttp_*.json` | **new** | Конфиги для `sing-box check` |
## 3. Зона касания upstream (для ребейза)
Только: `transport/v2ray/transport.go`, `constant/v2ray.go`, `option/v2ray_transport.go`. Все — с `// lx:` маркерами, атомарными коммитами. Реестр (`registry.go`) — новый файл, конфликтов не даёт.
## 4. Порядок работ
1. `registry.go` + рефактор `NewClientTransport` (поведение идентично — прогнать существующие тесты транспорта).
2. Константа + опции (`v2ray_xhttp.go` + `// lx:` поле).
3. Пакет `v2rayxhttp` (портировать клиент, сверить с Xray).
4. `register.go` под тегом + `_stub.go` без тега.
5. Конфиги, `sing-box check`, ручной коннект.
## 5. Риски
- **XHTTP — движущаяся цель** в Xray; нужна периодическая сверка параметров (`mode`, padding).
- `mode=auto` в sing-box-портах исторически падает в `packet-up`, что ломало, напр., аплоад в Telegram ([hiddify#2082](https://github.com/hiddify/hiddify-app/issues/2082)) — задокументировать фактический выбор режима.
- Рефактор `switch`→registry должен **точно** сохранить семантику ошибок и nil-обработку (`options.Type == ""` → `nil, nil`).
@@ -0,0 +1,54 @@
# SPEC: 002 — XHTTP_CLIENT_TRANSPORT
Добавить **клиентский XHTTP-транспорт** (совместимость с Xray XHTTP) для VLESS/VMess/Trojan, встроив его через **registry-рефактор** диспетчера v2ray-транспортов, за build-тегом `with_xhttp`.
---
## 1. Проблема / контекст
- Upstream sing-box XHTTP не поддерживает и не планирует ([#3550](https://github.com/SagerNet/sing-box/issues/3550)). Сервера на Xray всё чаще только XHTTP (после депрекации части транспортов в Xray).
- В sing-box диспетчер v2ray-транспортов — **хардкод-`switch`** по `options.Type` в `transport/v2ray/transport.go`. Добавлять `case` на каждый ребейз — точка постоянных конфликтов.
## 2. Цель
VLESS/VMess/Trojan outbound с `transport.type = "xhttp"` поднимают рабочее соединение к XHTTP-серверу Xray, в т.ч. поверх **TLS/Reality**. Без тега `with_xhttp` тип `xhttp` отвергается с понятной ошибкой.
## 3. Требования
### 3.1 Registry-рефактор диспетчера (точка касания upstream)
- Превратить выбор клиентского транспорта в **реестр**: `transport.RegisterClient(type, ClientConstructor)` + `map[string]ClientConstructor`, заполняемый при `init()`.
- Встроенные транспорты (`http`, `ws`, `quic`, `grpc`, `httpupgrade`) регистрируются как раньше (поведение идентично upstream).
- `NewClientTransport` ищет конструктор в реестре вместо `switch` (поведение для известных типов — без изменений; для неизвестных — та же ошибка `unknown transport type`).
- **Серверный** диспетчер (`NewServerTransport`) — **не трогаем** (scope client-only); xhttp-сервер отложен.
### 3.2 Пакет `transport/v2rayxhttp` (новый код)
- Клиентская реализация XHTTP (референс — [`hiddify/hiddify-sing-box`](https://github.com/hiddify/hiddify-sing-box) `transport/v2rayxhttp`, сверка параметров с Xray-core).
- Поддержать режимы Xray: `auto`, `packet-up`, `stream-up`, `stream-one`; параметры `path`, `host`, `headers`, и padding-расширения (`x_padding_bytes` и т.п.) — в объёме, нужном для совместимости.
- Регистрация конструктора через `init()` в файле за `//go:build with_xhttp`.
### 3.3 Опции и константа
- `constant/v2ray.go`: `V2RayTransportTypeXHTTP = "xhttp"` (внутри `// lx:` маркера).
- `option/v2ray_transport.go`: поле `XHTTPOptions XHTTPOptions` в `_V2RayTransportOptions` + тип `XHTTPOptions` (в новом файле `option/v2ray_xhttp.go`, чтобы минимизировать дифф основного файла; в `_V2RayTransportOptions` — одна `// lx:` строка).
### 3.4 TLS/Reality
- `tlsConfig` прокидывается в конструктор как у прочих транспортов → связка **XHTTP + Reality** работает без доп. кода. (XHTTP + XTLS-Vision несовместимы — ограничение протокола, не наше.)
## 4. Критерии приёмки
- `sing-box check -c` принимает VLESS + `transport.type=xhttp` + `tls.reality`.
- Реальный коннект к XHTTP-серверу Xray (ручная проверка), хотя бы `mode=stream-one` и `packet-up`.
- Сборка **без** `with_xhttp`: конфиг с `xhttp` → ошибка `unknown transport type: xhttp` (или эквивалент реестра).
- `go test ./transport/...`, `go vet ./...` зелёные.
- Ребейз-проверка: при следующем upstream-теге конфликты возможны **только** в `transport/v2ray/transport.go`, `constant/v2ray.go`, `option/v2ray_transport.go`.
## 5. Вне скоупа
- **XHTTP server/inbound** (отдельная будущая задача).
- Маппинг `vless://…type=xhttp` в самом лаунчере (репозиторий `singbox-launcher`, follow-up к его задаче 023).
- 100% паритет всех Xray-расширений XHTTP — только то, что нужно для рабочего коннекта.
## 6. Ссылки
- [V2Ray Transport — sing-box](https://sing-box.sagernet.org/configuration/shared/v2ray-transport/)
- [hiddify-sing-box (референс XHTTP)](https://github.com/hiddify/hiddify-sing-box)
- [XHTTP overview (Habr)](https://habr.com/en/articles/990208/)
@@ -0,0 +1,28 @@
# TASKS — 002-F-N-XHTTP_CLIENT_TRANSPORT
## Registry-рефактор (касание upstream)
- [ ] `transport/v2ray/registry.go`: `clientRegistry`, `RegisterClient`, `init()` встроенных типов (http/ws/quic/grpc/httpupgrade)
- [ ] `// lx:` правка `NewClientTransport` — lookup вместо `switch`; сохранить `Type==""` → `nil,nil` и текст ошибки
- [ ] Прогнать существующие тесты транспорта — поведение без изменений
## Опции / константа
- [ ] `// lx:` `V2RayTransportTypeXHTTP="xhttp"` в `constant/v2ray.go`
- [ ] `option/v2ray_xhttp.go`: тип `XHTTPOptions` (mode/path/host/headers/padding)
- [ ] `// lx:` поле `XHTTPOptions` в `_V2RayTransportOptions`
## Пакет v2rayxhttp (client)
- [ ] Портировать клиент (референс hiddify, сверка с Xray): `client.go`, `conn.go`, `dialer.go`, `http.go`, `mux.go`, `upload_queue.go`, `writer.go`
- [ ] Режимы `auto`/`packet-up`/`stream-up`/`stream-one`; padding-параметры
- [ ] `transport/v2rayxhttp/register.go` (`//go:build with_xhttp`) — регистрация конструктора
- [ ] `include/v2rayxhttp_stub.go` (`//go:build !with_xhttp`)
## Проверки
- [ ] `test/config/xhttp_reality.json` + `sing-box check`
- [ ] Ручной коннект к Xray XHTTP-серверу (`stream-one`, `packet-up`)
- [ ] Сборка без тега: `xhttp` → `unknown transport type`
- [ ] `go vet ./...`, `go test ./transport/...`
## Закрытие
- [ ] DoD-чеклист
- [ ] IMPLEMENTATION_REPORT.md (+ зафиксировать выбор `mode=auto`)
- [ ] Папка → `C`
@@ -0,0 +1,47 @@
# PLAN: 003 — AWG2_CLIENT_ENDPOINT
## 1. Архитектура
AmneziaWG = WireGuard-девайс с расширенным конфигом. В sing-box девайс создаётся в `transport/wireguard` поверх `github.com/sagernet/wireguard-go`. Стратегия: **подменить модуль на `amneziawg-go`** (API-совместим с wireguard-go) и **под `with_awg`** прокидывать AWG-поля в строку конфигурации девайса; endpoint остаётся типом `wireguard`.
## 2. Зависимость
- Submodule: `submodules/amneziawg-go` → `amnezia-vpn/amneziawg-go` (pin commit).
- `patches/` — для локальных фиксов поверх amneziawg-go (применяются скриптом сборки; пусто, если не нужны).
- `go.mod` `// lx:` replace `github.com/sagernet/wireguard-go => ./submodules/amneziawg-go`.
> Проверить: совпадает ли публичный API amneziawg-go (пакеты `device`, `conn`, `tun`) с тем, что импортирует `transport/wireguard`. Если расходится — минимальные `patches/` или адаптерный слой в новом файле.
## 3. Изменяемые / новые файлы
| Файл | Тип | Изменения |
|------|-----|-----------|
| `.gitmodules`, `submodules/amneziawg-go` | **new** | Submodule, pinned commit |
| `patches/*.patch` | **new** | (опц.) патчи поверх amneziawg-go |
| `go.mod` / `go.sum` | `// lx:` | `replace` wireguard-go → submodule |
| `option/wireguard_awg.go` | **new** | Под-структура/поля `Jc,Jmin,Jmax,S1,S2,H1..H4,I1..I5` + парс/валидация |
| `option/…wireguard endpoint options` | `// lx:` | встроить AWG-поля в `WireGuardEndpointOptions` (минимум строк) |
| `transport/wireguard/device_awg.go` | **new** (`//go:build with_awg`) | Формирование AWG-строки конфига девайса |
| `transport/wireguard/device_stub_awg.go` | **new** (`//go:build !with_awg`) | Ошибка «awg not built», если AWG-поля заданы |
| `protocol/wireguard/endpoint.go` | `// lx:` | Прокинуть AWG-опции в создание девайса (1 ветка под флагом) |
| `include/awg.go` / правка `include/wireguard.go` | new/`// lx:` | Проводка под тегом (если нужно) |
| `test/config/awg2_*.json` | **new** | Конфиги для `sing-box check` |
## 4. Зона касания upstream (для ребейза)
`go.mod`/`go.sum`, файл опций wireguard-endpoint, `protocol/wireguard/endpoint.go`, `transport/wireguard/*` (минимально). Девайс-логика и опции AWG — в **новых** файлах под тегом → основной конфликт только в `go.mod` и одной ветке endpoint.
## 5. Порядок работ
1. Submodule + `go.mod` replace; собрать обычный WG (без `with_awg`) — поведение upstream.
2. Сверить API amneziawg-go vs `transport/wireguard`; при необходимости `patches/`.
3. Опции AWG (`wireguard_awg.go` + `// lx:` поля).
4. `device_awg.go` (формат `jc=/h1=/i1=…`) под `with_awg`; stub без тега.
5. Прокидка в endpoint; конфиги; `check`; ручной коннект к AWG2-серверу.
## 6. Риски
- **API-дрейф** amneziawg-go относительно версии wireguard-go, на которую завязан upstream (`v0.0.2-beta.1.0.20260224…`). Возможен лаг — фиксировать совместимый коммит сабмодуля, не «latest».
- **Регистр I1–I5** (uppercase) — silent ignore при ошибке; валидировать.
- Взаимодействие junk/CPS с `persistent_keepalive` и MTU — проверять на реальном сервере.
- Доменный `server` + FakeIP: может потребоваться override резолва (референс hoaxisr) — добавлять только при подтверждённой необходимости.
@@ -0,0 +1,51 @@
# SPEC: 003 — AWG2_CLIENT_ENDPOINT
Добавить **клиентский AmneziaWG 2.0 endpoint** поверх существующего WireGuard-endpoint sing-box, используя `amneziawg-go` (как submodule + patches), за build-тегом `with_awg`.
---
## 1. Проблема / контекст
- AmneziaWG обходит DPI обфускацией: junk-пакеты (`Jc`/`Jmin`/`Jmax`), магические значения размеров/типов (`S1`/`S2`, `H1`–`H4`), а в **2.0** — CPS-пакеты `I1`–`I5` (первый — снимок реального протокола, напр. QUIC Initial) и диапазонные заголовки. См. [AmneziaWG 2.0](https://docs.amnezia.org/documentation/instructions/new-amneziawg-selfhosted/).
- Upstream sing-box AmneziaWG не принимает ([#4045](https://github.com/SagerNet/sing-box/issues/4045), closed not-planned).
- В sing-box WireGuard — это **endpoint** (`protocol/wireguard/endpoint.go`, девайс через `transport/wireguard`, зависимость `github.com/sagernet/wireguard-go`).
## 2. Цель
WireGuard-endpoint с заданными AWG-параметрами (`jc`, `jmin`, `jmax`, `s1`, `s2`, `h1`–`h4`, `i1`–`i5`) поднимает рабочее соединение к серверу **AmneziaWG 2.0**. Без тега `with_awg` бинарь = обычный WireGuard upstream.
## 3. Требования
### 3.1 Зависимость amneziawg-go (касание `go.mod`)
- Подключить [`amnezia-vpn/amneziawg-go`](https://github.com/amnezia-vpn/amneziawg-go) как **git submodule** (`submodules/amneziawg-go`) + каталог `patches/` (паттерн [`hoaxisr/amnezia-box`](https://github.com/hoaxisr/amnezia-box)).
- `go.mod`: `// lx:` `replace github.com/sagernet/wireguard-go => ./submodules/amneziawg-go` (или pin на форк-модуль). Зафиксировать **конкретный коммит** сабмодуля.
- Замена активна всегда на уровне модуля, но **AWG-поведение** включается только кодом под `with_awg` (см. 3.3); без тега девайс конфигурируется как обычный WG.
### 3.2 Опции (касание option)
- Расширить `option.WireGuardEndpointOptions` полями AWG: `Jc, Jmin, Jmax, S1, S2, H1, H2, H3, H4` (числа) и `I1, I2, I3, I4, I5` (строки, **регистр важен** — uppercase в .conf, во внутренней модели — как есть).
- Поля — в новом файле `option/wireguard_awg.go` со встраиванием/хелпером; в основной struct — минимальные `// lx:` строки (или встроенная под-структура `AmneziaWG`).
- Без `with_awg`: поля либо игнорируются, либо дают понятную ошибку «awg support not built» (выбрать и задокументировать; предпочтительно — явная ошибка, чтобы не было тихой деградации обфускации).
### 3.3 Девайс (касание transport/wireguard + protocol/wireguard)
- При `with_awg` конфигурация девайса передаёт AWG-параметры в `amneziawg-go` (формат строки конфига: `jc=`, `jmin=`, `jmax=`, `s1=`, `s2=`, `h1=`…`h4=`, `i1=`…`i5=`).
- Регистрация endpoint остаётся `C.TypeWireGuard` (AWG — это WG + доп. поля, отдельный тип не вводим — минимальный дифф). Проводка — вариант `include/wireguard.go` под тегом или новый `include/awg.go`.
- Сохранить фиксы резолва (DialContext/ListenPacket для доменов/FakeIP), если они нужны (референс hoaxisr) — но **только** если без них AWG-endpoint не работает с доменными `server`.
## 4. Критерии приёмки
- `sing-box check -c` принимает wireguard-endpoint c полями `jc/h1/i1…` под `with_awg`.
- Реальный коннект к серверу AmneziaWG 2.0 (ручная проверка), с непустыми `Jc` и хотя бы одним `I1`.
- Сборка **без** `with_awg`: обычный WG работает как upstream; AWG-поля → явная ошибка «не собрано».
- `go vet ./...`, тесты затронутых пакетов — зелёные.
- Ребейз-проверка: конфликты возможны только в `go.mod`/`go.sum`, `option/*wireguard*`, `protocol/wireguard/endpoint.go`, `transport/wireguard/*`.
## 5. Вне скоупа
- **AWG inbound/server** (отдельная задача).
- Парсинг `awg-quick`/`.conf` — это забота лаунчера/UI.
- AmneziaWG 1.x как отдельный режим — 2.0 обратно совместима по базовым полям; спец-режим 1.x не вводим.
## 6. Ссылки
- [AmneziaWG 2.0 — Amnezia Docs](https://docs.amnezia.org/documentation/instructions/new-amneziawg-selfhosted/)
- [amneziawg-go](https://github.com/amnezia-vpn/amneziawg-go) · [hoaxisr/amnezia-box (референс интеграции)](https://github.com/hoaxisr/amnezia-box)
@@ -0,0 +1,28 @@
# TASKS — 003-F-N-AWG2_CLIENT_ENDPOINT
## Зависимость
- [ ] Submodule `submodules/amneziawg-go` (pin коммит, совместимый с wireguard-go upstream)
- [ ] `// lx:` `replace` в `go.mod`; `go mod tidy`; сборка обычного WG без `with_awg` = upstream
- [ ] Сверить API amneziawg-go vs `transport/wireguard`; при расхождении — `patches/`
## Опции
- [ ] `option/wireguard_awg.go`: `Jc,Jmin,Jmax,S1,S2,H1..H4` (int), `I1..I5` (string, регистр)
- [ ] `// lx:` встроить AWG-поля в `WireGuardEndpointOptions`
- [ ] Без `with_awg` + заданы AWG-поля → явная ошибка «awg not built»
## Девайс
- [ ] `transport/wireguard/device_awg.go` (`//go:build with_awg`): строка конфига `jc=/jmin=/jmax=/s1=/s2=/h1..h4=/i1..i5=`
- [ ] `device_stub_awg.go` (`//go:build !with_awg`)
- [ ] `// lx:` прокидка опций в `protocol/wireguard/endpoint.go`
- [ ] Проводка под тегом (`include/awg.go` или правка `include/wireguard.go`)
## Проверки
- [ ] `test/config/awg2_basic.json` + `sing-box check`
- [ ] Ручной коннект к серверу AmneziaWG 2.0 (непустой `Jc`, хотя бы `I1`)
- [ ] Сборка без тега: обычный WG ок; AWG-поля → ошибка
- [ ] `go vet ./...`, тесты затронутых пакетов
## Закрытие
- [ ] DoD-чеклист
- [ ] IMPLEMENTATION_REPORT.md (зафиксировать pin-коммит сабмодуля, формат конфиг-строки)
- [ ] Папка → `C`
+41
View File
@@ -0,0 +1,41 @@
# PLAN: 004 — BUILD_CI_RELEASE
## 1. Файлы
| Файл | Тип | Изменения |
|------|-----|-----------|
| `Makefile` | дополнение (из 001) | `lx-build`, `lx-release` (cross-compile матрица), `LX_TAGS`, `LX_LDFLAGS` |
| `.github/workflows/lx-ci.yml` | расширение (из 001) | Матрица OS×ARCH, submodule init, build+vet+test+`check` |
| `.github/workflows/lx-rebase.yml` | **new** | schedule/dispatch: fetch upstream tags → rebase → build → PR/issue |
| `.github/workflows/lx-release.yml` | **new** | on tag `v*-lx.*`: cross-build, zip, checksums, GitHub Release |
| `test/config/xhttp_reality.json`, `awg2_basic.json` | из 002/003 | Используются в CI `check` |
| `scripts/lx/cross_build.sh` | **new** | Матрица `GOOS/GOARCH` → `dist/<os>-<arch>/sing-box` |
## 2. Версия / ldflags
`LX_LDFLAGS = -X github.com/sagernet/sing-box/constant.Version=<upstream>-lx.<N>` (точный путь/символ — по факту из 001). `<N>` — счётчик lx-релизов поверх данного upstream-тега.
## 3. Авто-ребейз (lx-rebase.yml) — логика
```
fetch upstream --tags
latest = max(stable tags)
if base(lx) == latest: exit "up to date"
git checkout -b lx-rebase/$latest lx
git rebase $latest # конфликты → собрать дифф // lx: зон, открыть issue, stop
make lx-build && go vet && sing-box check ... # упало → issue
push origin lx-rebase/$latest ; gh pr create
```
## 4. Порядок работ
1. Расширить `Makefile` (cross-build) и `lx-ci.yml` до полной матрицы.
2. `lx-rebase.yml` (сначала `workflow_dispatch`, потом cron).
3. `lx-release.yml` + `cross_build.sh`.
4. Демо-прогон ребейз-workflow на текущем теге.
## 5. Риски
- Кросс-сборка с `with_gvisor`/cgo-зависимостями (wireguard/amneziawg) — проверить, что AWG-submodule собирается под все `GOOS/GOARCH` (особенно windows/arm64).
- Авто-ребейз на **alpha/beta** теги нежелателен — фильтровать только стабильные (`vX.Y.Z` без суффиксов).
- `git submodule` в CI — не забыть `--init --recursive` и pin.
+46
View File
@@ -0,0 +1,46 @@
# SPEC: 004 — BUILD_CI_RELEASE
Собрать воспроизводимый конвейер сборки/CI/релизов `sing-box-lx`: кросс-платформенные бинари `sing-box` с lx-тегами, версия `-lx.N`, и **авто-ребейз на новый upstream-тег**.
---
## 1. Проблема / контекст
Реальная стоимость downstream'а — не первичная разработка, а N ребейзов в год и регулярные сборки на 3 платформы. Нужен конвейер, который ловит «фичи поломались об новый upstream» раньше пользователя и выпускает drop-in бинарь для лаунчера.
## 2. Требования
### 2.1 Сборка
- Единый набор `LX_TAGS` (из 001) + цель `make lx-build`, output **`sing-box`** (drop-in для лаунчера).
- Версия `vX.Y.Z-lx.N` через ldflags (из 001).
### 2.2 CI-матрица
- Платформы: `{linux, darwin, windows} × {amd64, arm64}`.
- Шаги: `make lx-build` → `go vet` → `go test ./...` (или затронутые) → `sing-box check` на sample-конфигах XHTTP и AWG2.
- Сборка с submodule (`git submodule update --init`).
### 2.3 Авто-ребейз на upstream-тег
- Workflow по расписанию/`workflow_dispatch`:
1. `git fetch upstream --tags`, определить новейший стабильный тег `> текущей базы`.
2. Попытка `git rebase <tag>` ветки `lx` в CI.
3. Успех + сборка/`check` зелёные → пуш ветки `lx-rebase/<tag>` и **PR**; конфликт → **issue** с диффом `// lx:` зон.
- Никогда не пушить силой в `lx` автоматически — только через PR с ревью.
### 2.4 Релизы
- Тег `vX.Y.Z-lx.N` → артефакты по платформам (zip с бинарём `sing-box`), checksums.
- Release notes: какой upstream-тег в основе + состояние фич (`with_xhttp`/`with_awg`).
## 3. Критерии приёмки
- CI зелёный на push в `lx` по всей матрице.
- Артефакты собираются, бинарь называется `sing-box`, `version` → `-lx.N`.
- Авто-ребейз workflow отрабатывает на `workflow_dispatch` (демо на текущем теге → «уже актуально» или PR).
## 4. Вне скоупа
- Подпись кода/нотаризация, публикация в пакетные менеджеры.
- Полностью автоматический мёрж ребейза (всегда ревью).
## 5. Ссылки
- [Build from source — sing-box](https://sing-box.sagernet.org/installation/build-from-source/)
+25
View File
@@ -0,0 +1,25 @@
# TASKS — 004-F-N-BUILD_CI_RELEASE
## Сборка
- [ ] `Makefile`: `lx-build` (output `sing-box`), `lx-release` (cross), `LX_TAGS`, `LX_LDFLAGS`
- [ ] `scripts/lx/cross_build.sh`: матрица `GOOS/GOARCH` → `dist/<os>-<arch>/sing-box`
- [ ] Версия `vX.Y.Z-lx.N` через ldflags
## CI
- [ ] `lx-ci.yml`: матрица `{linux,darwin,windows}×{amd64,arm64}`, submodule init, build+vet+test
- [ ] `sing-box check` на `xhttp_reality.json` и `awg2_basic.json`
## Авто-ребейз
- [ ] `lx-rebase.yml`: fetch upstream tags → выбрать новейший **стабильный** → rebase в CI
- [ ] Успех → ветка `lx-rebase/<tag>` + PR; конфликт/падение → issue с диффом `// lx:` зон
- [ ] Запрет авто-force-push в `lx`; только PR
- [ ] Демо-прогон `workflow_dispatch`
## Релизы
- [ ] `lx-release.yml`: on tag `v*-lx.*` → cross-build, zip, checksums, GitHub Release
- [ ] Release notes: upstream-база + состояние `with_xhttp`/`with_awg`
## Закрытие
- [ ] DoD-чеклист; проверить кросс-сборку AWG-submodule под windows/arm64
- [ ] IMPLEMENTATION_REPORT.md
- [ ] Папка → `C`
+78
View File
@@ -0,0 +1,78 @@
# CONSTITUTION — sing-box-lx
Неизменяемые принципы проекта. При конфликте с любым SPEC/PLAN — приоритет у этого документа.
---
## 1. Миссия
`sing-box-lx` — **тонкий downstream** [SagerNet/sing-box](https://github.com/SagerNet/sing-box). Это **upstream + ровно две фичи**:
1. **XHTTP** — клиентский v2ray-транспорт, совместимый с Xray XHTTP.
2. **AmneziaWG 2.0 (AWG2)** — клиентский endpoint поверх WireGuard, с обфускацией (Jc/Jmin/Jmax, S1/S2, H1–H4, I1–I5).
Обе фичи **отклонены upstream** ([XHTTP — not planned](https://github.com/SagerNet/sing-box/issues/3550), [AmneziaWG — closed not-planned](https://github.com/SagerNet/sing-box/issues/4045)), поэтому форк постоянный, а «согласованность с upstream» достигается не вливанием в него, а **дешёвым ребейзом на каждый новый тег**.
---
## 2. Приоритеты (в порядке убывания)
1. **Согласованность с upstream / минимальный дифф.** Любое решение выбирается так, чтобы ребейз на следующий тег был максимально дешёвым.
2. **Корректность и совместимость** с реальными серверами Xray-XHTTP и AmneziaWG 2.0.
3. **Ребейзопригодность** изменений (изоляция, атомарность, маркеры).
4. **Сами фичи** (функциональность XHTTP/AWG2).
Если фича требует жертвовать пунктом 1 — она переосмысливается, а не пункт 1.
---
## 3. Жёсткие правила (запреты и инварианты)
### 3.1 Объём
- **Только две фичи.** Любой код вне XHTTP и AWG2 — вне скоупа. Багфиксы upstream не патчим у себя — ждём апстрим-тег.
- **Scope — client-only.** Реализуем outbound/endpoint и клиентскую сторону транспорта. Server/inbound для XHTTP и AWG — **отложены** (отдельные будущие задачи), в текущих спеках не реализуются.
### 3.2 Изоляция изменений
- **Go module path остаётся `github.com/sagernet/sing-box`.** Не переименовывать — это ломает все внутренние импорты и каждый ребейз.
- **Новый код — в новых файлах/пакетах.** XHTTP-транспорт — пакет `transport/v2rayxhttp`. AWG — в выделенных файлах рядом с `protocol/wireguard` / `transport/wireguard`.
- **Каждая фича — за build-tag:** `with_xhttp`, `with_awg`. Без тега сборка обязана быть **байт-в-байт эквивалентна upstream по поведению** (фича отсутствует).
- **Шаблон гейтинга — `include/*_stub.go`** (как у upstream `include/wireguard.go` + `wireguard_stub.go`): реальная регистрация под тегом, заглушка с понятной ошибкой без тега.
### 3.3 Правки upstream-файлов
- Допускаются **только** там, где иначе нельзя (диспетчеры, struct опций, списки констант, `go.mod`).
- Каждая такая правка **обёрнута маркером**:
```go
// lx:begin xhttp
...
// lx:end xhttp
```
- Правки upstream-файлов выносятся в **отдельные атомарные коммиты** (см. IMPLEMENTATION_PROMPT). Один коммит = одна логическая правка одной зоны.
### 3.4 Синхронизация
- **Только rebase, никогда merge.** Ветка `lx` всегда ребейзится поверх тега upstream (`v1.13.13`, затем следующий стабильный).
- `origin` = `Leadaxe/sing-box-lx`, `upstream` = `SagerNet/sing-box`. Теги тянем из `upstream`.
### 3.5 Дистрибуция
- **Имя бинаря — `sing-box`** (drop-in для лаунчера `singbox-launcher`, который ищет `LookPath("sing-box")` → `bin/sing-box`).
- Идентичность сборки — **в версии**: `sing-box version` → `1.13.13-lx.N` (см. задачу BUILD_CI_RELEASE).
---
## 4. Архитектурные ориентиры (факты upstream v1.13.13)
- **v2ray-транспорты** диспатчатся `switch` по `options.Type` в `transport/v2ray/transport.go` (`NewClientTransport`/`NewServerTransport`). Константы — `constant/v2ray.go`. Опции — `option/v2ray_transport.go` (`_V2RayTransportOptions`). VLESS/VMess/Trojan ходят через общий транспорт — пер-протокольных правок не требуется.
- **WireGuard** — это **endpoint**: `protocol/wireguard/endpoint.go`, регистрация `endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)`, проводка в `include/wireguard.go` (+ `wireguard_stub.go`). Девайс — через `transport/wireguard`, зависимость `github.com/sagernet/wireguard-go` в `go.mod`.
---
## 5. Референсы (только как образец, код не тянуть «как есть»)
- **AWG2** — [`hoaxisr/amnezia-box`](https://github.com/hoaxisr/amnezia-box) (submodule + `patches/amneziawg-go`, тег `with_awg`).
- **XHTTP** — [`hiddify/hiddify-sing-box`](https://github.com/hiddify/hiddify-sing-box), пакет `transport/v2rayxhttp`.
- **Спецификация XHTTP** — Xray-core (актуальная версия параметров `mode`/`path`/`host`/`extra`).
---
## 6. Лицензия
Upstream — GPLv3. Портируемый код из сторонних проектов держать в отдельных файлах с сохранением исходных лицензионных заголовков и указанием происхождения.
+71
View File
@@ -0,0 +1,71 @@
# IMPLEMENTATION_PROMPT — sing-box-lx
Правила реализации задач. Применять вместе с **CONSTITUTION.md** и SPEC/PLAN/TASKS конкретной задачи.
---
## 1. Философия
Ты строишь не «ещё один sing-box», а **дельту над upstream**. Хорошая дельта — та, которую через месяц можно за 10 минут переставить на новый тег. Поэтому: меньше тронутых upstream-строк > красивая абстракция; новый файл > правка существующего; build-tag > безусловный код.
---
## 2. Definition of Done
Задача закрыта, когда:
1. **Сборка без тегов** = поведение upstream:
`go build ./...` — ок, фичи отсутствуют.
2. **Сборка с тегами** включает фичи:
`go build -tags "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,with_xhttp,with_awg" ./cmd/sing-box`
3. `go vet ./...` и `go test ./...` зелёные (для затронутых пакетов как минимум).
4. `./sing-box check -c <тест-конфиг>` принимает конфиг с новой фичей (xhttp-транспорт / awg-endpoint).
5. Все правки upstream-файлов обёрнуты `// lx:begin <feat>` / `// lx:end <feat>` и лежат в отдельных коммитах.
6. **TASKS.md** отражает факт (`[x]`), заполнен **IMPLEMENTATION_REPORT.md**.
7. Папка задачи переименована в статус **C** при приёмке.
---
## 3. Git-дисциплина
### 3.1 Ветки
- `lx` — рабочая ветка, всегда ребейзится поверх тега upstream.
- Фичевые ветки от `lx`: `lx/xhttp`, `lx/awg` — вливаются в `lx` через rebase (linear history).
### 3.2 Коммиты
- Формат: `lx(<feat>): <что>` — напр. `lx(xhttp): add v2rayxhttp client package`, `lx(awg): extend wireguard endpoint with junk/header opts`.
- **Атомарность по зонам:** правки upstream-файлов (диспетчер, опции, константы, go.mod) — **отдельными** коммитами от нового кода. Так при ребейз-конфликте видно ровно, что и где.
- Порядок коммитов в дельте (от «нулевого конфликта» к «точкам касания»):
1. новые пакеты/файлы;
2. `// lx:` правки upstream-файлов;
3. `go.mod`/`go.sum`/submodule;
4. build-tag проводка (`include/*`), CI.
### 3.3 Ритуал ребейза на новый тег upstream
```sh
git fetch upstream --tags
git checkout lx
git rebase <new-tag> # напр. v1.13.14
# конфликты ожидаемы ТОЛЬКО в // lx: зонах upstream-файлов и go.mod
# разрешить, сверяясь с маркерами; пересобрать с тегами; прогнать DoD
git push --force-with-lease origin lx
git tag <new-tag>-lx.1 && git push origin <new-tag>-lx.1
```
Если ребейз дал конфликт вне `// lx:` зоны — значит изоляция нарушена, это дефект дельты, а не upstream.
---
## 4. Консоль и осторожность
- Не запускать интерактивные git-флаги (`-i`).
- `--force-with-lease`, не `--force`.
- Сабмодуль AWG обновлять осознанно (фиксировать конкретный коммит amneziawg-go).
---
## 5. Контракт выхода (что вернуть после реализации)
1. Список изменённых/новых файлов с разбивкой **new pkg / `// lx:` upstream / deps / wiring**.
2. Команды сборки (с тегами) и результат DoD-чеклиста.
3. Точная зона возможных конфликтов при следующем ребейзе (какие upstream-файлы тронуты).
4. Что осталось вне скоупа (server-сторона и т.п.).
+46
View File
@@ -0,0 +1,46 @@
# SPECS — sing-box-lx (Spec Kit)
Все задачи — папки `NNN-T-S-NAME`. Внутри: SPEC.md → PLAN.md → TASKS.md → IMPLEMENTATION_REPORT.md.
## Имя папки: `NNN-T-S-NAME`
| Часть | Значение | Расшифровка |
|-------|----------|-------------|
| **NNN** | 001, 002, … | Сквозной номер |
| **T** (тип) | F / B / Q | Feature / Bug / Question (исследование) |
| **S** (статус) | N / O / W / C | New / Open (в работе) / Wait / Complete |
| **NAME** | UPPER_SNAKE | Название |
## Файлы внутри папки
| Файл | Назначение |
|------|------------|
| **SPEC.md** | Что и зачем — проблема, требования, критерии приёмки |
| **PLAN.md** | Как строить — архитектура, изменяемые файлы, зона касания upstream |
| **TASKS.md** | Чеклист по этапам |
| **IMPLEMENTATION_REPORT.md** | Отчёт после реализации |
## Корень SPECS
| Файл | Назначение |
|------|------------|
| **CONSTITUTION.md** | Неизменяемые принципы, приоритеты, запреты |
| **IMPLEMENTATION_PROMPT.md** | DoD, git/ребейз-ритуал, контракт выхода |
## Workflow
1. Папка `SPECS/NNN-T-S-NAME/` (следующий номер, статус `N`).
2. SPEC.md → PLAN.md → TASKS.md.
3. Реализация по TASKS с учётом IMPLEMENTATION_PROMPT и CONSTITUTION.
4. IMPLEMENTATION_REPORT.md, DoD-чеклист, переименование папки в `…-C-…`.
## Roadmap (план задач)
| # | Задача | Статус | Суть |
|---|--------|--------|------|
| **001** | FORK_BOOTSTRAP | N | Remotes, ветка `lx`, build-теги `with_xhttp`/`with_awg`, версия `-lx`, маркеры `// lx:`, CI-скелет |
| **002** | XHTTP_CLIENT_TRANSPORT | N | Registry-рефактор диспетчера v2ray + пакет `transport/v2rayxhttp` (client) за `with_xhttp` |
| **003** | AWG2_CLIENT_ENDPOINT | N | amneziawg-go (submodule+patches) + расширение wireguard-endpoint за `with_awg` |
| **004** | BUILD_CI_RELEASE | N | Списки build-тегов, CI-матрица платформ, авто-ребейз на upstream-тег, релизные артефакты |
> **Вне этого репозитория:** потребление ядра лаунчером (`singbox-launcher`) — парсинг `type=xhttp` в реальный XHTTP-транспорт (сейчас `023` маппит его в `httpupgrade`), AWG-поля в визарде, замена `bin/sing-box`. Это отдельные задачи в репозитории лаунчера.