Design blueprint: architecture, feature catalog, data model, ops, roadmap (docs 00-07)
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
# testbed downloads / VM images / SDKs (agent-managed, large, machine-local)
|
||||
testbed/
|
||||
*.img
|
||||
*.img.gz
|
||||
*.tar.zst
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.qcow2
|
||||
*.vmdk
|
||||
|
||||
# build outputs
|
||||
*.ipk
|
||||
*.apk
|
||||
bin/
|
||||
node_modules/
|
||||
|
||||
# editor / os
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.swp
|
||||
@@ -0,0 +1,85 @@
|
||||
# Проект: OpenWrt-плагин для XRAY — сводка и план (anchor)
|
||||
|
||||
Индекс: [01-аудит роутера](01-mini_router-audit.md) · [02-дизайн](02-xray-plugin-design.md)
|
||||
· [03-фичи и модель](03-features-and-model.md) · [04-ops/надёжность/сборка/статы](04-ops-reliability-build-stats.md)
|
||||
|
||||
## Видение (одной строкой)
|
||||
Переносимый OpenWrt-плагин управления XRAY: подписки → ноды → цепочки → правила →
|
||||
выбор egress, с прозрачным роутингом, железной надёжностью и удобным UI.
|
||||
Ориентир — passwall2, но **чище, быстрее, без лагов и спагетти**.
|
||||
|
||||
## Дифференциаторы (почему «круче passwall»)
|
||||
1. Multi-hop цепочки (L1→L2→L3) с посубскрипшн-слоями (твой xray_chain — из коробки).
|
||||
2. Subscription-driven балансер + observatory с авто-выпилом мёртвых нод.
|
||||
3. **Выбор egress first-class** (правило/цепочка → любой iface/туннель/цепочка/direct).
|
||||
4. Современные транспорты (reality/xhttp/vision) как первый класс.
|
||||
5. Прозрачность: «откуда→куда→как→почему» (explain/trace), видимые метки/таблицы.
|
||||
6. Один процесс xray + nftables fast-path → без лагов passwall.
|
||||
|
||||
## Locked-решения
|
||||
- Движок: **только xray-core** (не форкаем; xrayctl = отдельный control-plane; sing-box — потом).
|
||||
- Генератор: **Go-компаньон `xrayctl`** (парсинг share-links через libXray, валидация `xray -test`).
|
||||
- MVP: **solid single transparent-proxy сначала**; цепочки/per-client — фаза 2.
|
||||
- Тест-стенд: **Docker/QEMU на ПК** (QEMU для netfilter/tproxy, Docker/WSL2 для сборки+логики).
|
||||
- Сборка: **OpenWrt + ImmortalWrt/BananaWRT**, версии 24.10(ipk)+25.x(apk), мультиарк, CI+фид.
|
||||
- Инбаунд: **только TPROXY**, но **мульти-LAN** (несколько LAN-сетей/интерфейсов, явное указание подсети).
|
||||
- DNS: **оба режима переключаемо** (nftset-split дефолт + FakeIP опция), **полностью настраиваемый**
|
||||
(named resolvers, DNS-routing per-domain/client, per-rule DNS, detour).
|
||||
- Kill-switch: **per-policy, глобальный дефолт fail-closed**.
|
||||
- Гео-данные: **скачивать по требованию**.
|
||||
- Правила: source subnet/host/mac/iface **+ dest domain/domain-list + ip/ip-list + geosite/geoip + port/l4**.
|
||||
- Reusable **Lists/Rulesets** (domain/ip; источники inline/файл/URL-auto) — объект №8 модели.
|
||||
|
||||
## Консолидированные требования (из всего брейншторма)
|
||||
**Модель (7 объектов):** Node, Group(+balancer), Chain, Egress/Outlet, Inbound, Rule, Profile(WAN-mode).
|
||||
**Правила:** матч source по CIDR/хост (`192.168.11.14/32`)/MAC/iface/зона; dest domain/geo/ip/port/l4;
|
||||
target chain|group|node|direct|block + egress. Per-client policy. Explain/trace «откуда→куда→как→почему».
|
||||
**Egress:** выбор физ-WAN / туннеля(awg) / цепочки / direct, и комбо.
|
||||
**Надёжность (топ-приоритет):** атомарный apply, свои метки/таблицы (не трогаем fw4), идемпотентный
|
||||
реконсайл, персист через hotplug, rollback + commit-confirm, fail-safe mgmt-bypass, watchdog.
|
||||
**DNS:** DoH + hijack :53 + блок DoT/DoH + сплит + опц. FakeIP; IPv6.
|
||||
**Статистика/дашборд:** per-proxy трафик (xray Stats API), per-rule (nft counters), live-пинг
|
||||
(observatory), история (collectd/RRD), дашборд в LuCI (throughput/ноды/правила/health/trace).
|
||||
**Переносимость:** без хардкод iface/IP; детект WAN; всё через UCI.
|
||||
**Дистрибуция:** подписанный фид, self-update, one-line установка.
|
||||
|
||||
## Non-goals (чтобы не утонуть как passwall)
|
||||
- НЕ форкаем xray. НЕ поддерживаем сразу sing-box/mihomo. НЕ тащим китайские gfwlist/chnroute-специфики.
|
||||
- НЕ per-node процессы. НЕ legacy iptables. MVP НЕ включает цепочки/FakeIP/расписания (фаза 2+).
|
||||
|
||||
## Как грамотно строить план (методология)
|
||||
1. **Contract-first.** Сначала зафиксировать «контракт»: UCI-схему `/etc/config/xray` +
|
||||
интерфейс `xrayctl` (CLI+ubus). От модели зависит ВСЁ (генератор, фаервол, UI, тесты) —
|
||||
это корень зависимостей, начинаем с него.
|
||||
2. **Walking skeleton (вертикальный срез).** Тончайший сквозной путь, доказывающий
|
||||
архитектуру: 1 подписка → parse → build → 1 tproxy-outbound → трафик идёт в QEMU →
|
||||
виден в минимальном статусе. Сначала де-рискуем ТЯЖЁЛОЕ (tproxy/routing/надёжность),
|
||||
а не строим UI на шатком фундаменте (надёжность — топ-требование).
|
||||
3. **Итерации по тирам**, каждая — шипабельна и с тест-гейтом (definition of done + прогон в QEMU).
|
||||
4. **Артефакты плана:** этот бэклог (по модели+тирам), risk-register, decision-log, тест-стратегия по фазам.
|
||||
|
||||
## План по фазам (с критериями выхода)
|
||||
- **Ф0 — Контракт & каркас.** UCI-схема + `xrayctl` интерфейс (спека) + скелет репозитория
|
||||
(xrayctl/, luci-app/, CI-заготовка) + QEMU-стенд поднят.
|
||||
*Выход:* спека утверждена, QEMU-OpenWrt с тест-клиентом пингуется, CI собирает пустой пакет.
|
||||
- **Ф1a — xrayctl ядро.** fetch/parse(libXray)/build/test/apply, чтение UCI. Юнит-тесты (где угодно).
|
||||
*Выход:* из тест-подписки генерится валидный xray JSON, `xray -test` ок.
|
||||
- **Ф1b — Data-plane (надёжность).** procd + своя nft-таблица (tproxy TCP+UDP) + policy-routing
|
||||
+ hotplug-персист + атомарный apply/rollback + mgmt-bypass. DNS: DoH+hijack+базовый сплит.
|
||||
*Выход:* в QEMU LAN-клиент выходит через прокси, exit-IP меняется, reboot/`network reload`
|
||||
не ломает; rollback работает; связь не теряется.
|
||||
- **Ф1c — LuCI MVP.** Ноды(живой пинг)/подписки/настройки/статус + ubus-бэкенд.
|
||||
*Выход:* всё выше управляется из веб-UI.
|
||||
- **Ф2 — Дифференциаторы.** Egress-selection UI, per-client policy, **multi-hop цепочки**
|
||||
(перенос xray_chain), гео/домен-правила, explain/trace, дашборд статистики.
|
||||
- **Ф3 — Расширения.** WAN-mode профили/failover, awg-wrap, FakeIP, sing-box-адаптер, расписания.
|
||||
|
||||
## Risk-register (ключевое)
|
||||
DNS-leak · tproxy-петля (sockopt.mark) · flow-offload ломает tproxy · IPv6 dual-stack ·
|
||||
kmod↔ядро pin · дрейф схемы xray · boot-order (fw→dnsmasq→xray+hotplug) · lockout (→ commit-confirm).
|
||||
|
||||
## Ближайшие 3 шага
|
||||
1. Написать **спеку контракта**: UCI-схема `/etc/config/xray` + командный/ubus-интерфейс `xrayctl`.
|
||||
2. Завести **скелет репозитория** в `shater/` (git init, структура из дока 02) + CI-заготовка.
|
||||
3. Поднять **QEMU-стенд** (OpenWrt x86-64 + тест-клиент) и собрать пустой пакет через SDK (WSL2).
|
||||
</content>
|
||||
@@ -0,0 +1,77 @@
|
||||
# mini_router (BPi-R3 Mini) — полный аудит (2026-07-08)
|
||||
|
||||
SSH: `mini_router` = 192.168.11.1 (root, пароль). Hostname `BananaWRT`.
|
||||
Железо: Bananapi BPi-R3 Mini (aarch64_cortex-a53), ImmortalWrt 24.10.3, аптайм ~24 дня.
|
||||
Модем: Fibocom FM350-GL (`/dev/ttyUSB1`=AT, `ttyUSB3`=data→eth2). Оператор МегаФон, APN internet.
|
||||
|
||||
## Роль узла
|
||||
Шлюз с **двухслойным обходом** (xray-цепочка поверх AmneziaWG) + **тройной failover WAN** + доступ к домашней сети `10.10.10.0/24`.
|
||||
|
||||
## WAN failover (по metric)
|
||||
| iface | UCI | что | metric | статус |
|
||||
|-------|-----|-----|--------|--------|
|
||||
| eth1 | `ewan` (dhcp) | Ethernet-аплинк, 10.0.0.125, шлюз 10.0.0.1 (BPI-R4) | 10 | **АКТИВЕН** |
|
||||
| phy0-sta0 | `wwan` (dhcp) | Wi-Fi client | 20 | down |
|
||||
| eth2 | `wan` (atc, FM350) | SIM МегаФон, CGNAT 100.97.58.232/28 | 30 | up, резерв |
|
||||
|
||||
## Активный путь трафика (режим ETH)
|
||||
```
|
||||
LAN(br-lan) ─nft xray_tproxy (tcp+udp tproxy→:12345, mark 0x162)→
|
||||
xray_chain :12345 ─3 хопа L1→L2→L3 (подписка qomar)→
|
||||
(local out → ip rule prio10 → table160: default dev awgOut)→
|
||||
awgOut (AmneziaWG, свои пакеты mark 0x11 → prio5 → main → eth1)→
|
||||
orange_pi 146.158.118.199:25423 → VPS-exit → Internet
|
||||
```
|
||||
Параллельно **awgHome** (endpoint :58052) даёт доступ к дому 10.10.10.0/24 (там blockchain-ноды, teamspeak .160, камеры, main_router).
|
||||
|
||||
## Метки / таблицы маршрутизации
|
||||
- `0x162` — xray_chain (LAN tproxy → :12345), ip rule prio2 → table162 (`local default dev lo`)
|
||||
- `0x163` — xray-sim (awg-UDP → :10810), prio3 → table163 (`local default dev lo`) — ТОЛЬКО в SIM-режиме
|
||||
- `0x11` — свои пакеты awgOut/awgHome, prio5 → main (физический WAN), разрыв петли
|
||||
- table160 (prio10, from all): `default dev awgOut`, `10.10.10.0/24 dev awgHome`, `10.11.0.0/24 dev awgHome`, `192.168.11.0/24 dev br-lan`
|
||||
- sysctl: ip_forward=1, rp_filter=0, route_localnet=1, accept_local=1
|
||||
|
||||
## nftables (live tables: fw4, ttl_mangle, dnsmasq, xray_tproxy)
|
||||
- **xray_tproxy** (inet): prerouting mangle — bypass mark 0x162 + все RFC1918/loopback/multicast, затем `iifname br-lan tcp/udp tproxy to :12345 mark 0x162`.
|
||||
- **ttl_mangle** (ip): `oifname eth2 ip ttl set 64` (антидетект тетеринга).
|
||||
- fw4: DNS-hijack (LAN :53 → DNAT 192.168.11.1:53 / [::1]), redirect **Sunshine** (WAN 47984-48010 → 192.168.11.240, game-streaming).
|
||||
|
||||
## DNS (задействован)
|
||||
LAN → dnsmasq (`:53`, форс через nft DNAT + fw4 redirect) → **https-dns-proxy** DoH `127.0.0.1:5533` → Quad9 (9.9.9.9). dnsmasq `noresolv=1`, rebind-protection. Утечки закрыты. (unbound установлен, но `off`.)
|
||||
|
||||
## Сервисы: РАБОТАЕТ / РЕЗЕРВ / МУСОР
|
||||
|
||||
### 🟢 Работает
|
||||
- **xray_chain** (pid 2003, бинарь **26.6.22** — обновлён сегодня): цепочка, слушает :12345 + внутр. 10001/10002. Выход рабочий (exit ≠ IP роутера).
|
||||
- **awgOut / awgHome** — оба с недавним хендшейком, живы. Дом пингуется.
|
||||
- **dnsmasq + https-dns-proxy** — DNS-контур.
|
||||
- Policy routing (table160/162), метки 0x11/0x162.
|
||||
- Sunshine port-forward, SMS-tool, ttyd (:7681), statistics/collectd/vnstat, aria2, usteer.
|
||||
|
||||
### 🟡 Горячий резерв (только в SIM-режиме, сейчас простаивает — 0 соединений)
|
||||
- **xray-sim** (pid 19544): 56 VLESS-нод renawave, балансер leastPing + observatory, инбаунды 10808/10809/dokodemo 10810. Таблица `ip tproxy_awg` сейчас НЕ загружена → в него ничего не заворачивается.
|
||||
- **sim-setup.sh** + table163 + `tproxy-awg.nft` — петля «awg-UDP → VLESS» для обхода whitelist МегаФона. Поднимается при `default dev eth2`.
|
||||
- cron `*/30` крутит `update_config.sh` (renawave), рестартит xray-sim даже вхолостую.
|
||||
- ⚠️ renawave **не релеит произвольный UDP** (только DNS) → SIM-контур для awg боем не проверен, вероятно не поднимется в whitelist-часы.
|
||||
|
||||
### 🔴 Мусор / не используется
|
||||
| Компонент | Состояние | Вердикт |
|
||||
|-----------|-----------|---------|
|
||||
| **tun2socks** (pid 5721) | запущен из `/etc/rc.local`, `-device tun1 -proxy ss://…` | tun1 DOWN, нет маршрутов → **балласт**, ест ~1.2 ГБ VSZ |
|
||||
| **naiveproxy** | S99 вкл., `enable='0'`, не запущен | dead |
|
||||
| сток **xray** (S99xray) | `enabled '0'`, только config.json.example | dead, дублирует бинарь |
|
||||
| **unbound** | пакет стоит, init off | не используется; block_unbound/block_dot rules `enabled='0'` |
|
||||
| **imei-spoof** | init off, но IMEI сейчас спуфнут (358473112729568, в NVRAM) | эффект есть, при сбросе модема не переприменится |
|
||||
| Файлы-хвосты | update.sh.bak2/3/4, xray_config.json.pre_xhttp, config.json.bak.*/broken, panther_cache.txt, pyhpk6Ze0SNzvexY (207КБ), update_sub.sh.disabled, update_panther_sub.sh | наследие |
|
||||
|
||||
## Сделано сегодня
|
||||
1. `update.sh` (xray_chain): добавлена поддержка **xhttp/httpupgrade**, `raw→tcp` (был баг — xhttp молча превращался в tcp). Протестировано, залито, прогнано.
|
||||
2. Бинарь xray обновлён **26.3.27 → 26.6.22** (ручная замена, sha256 сверен). Бэкап `/usr/bin/xray.bak.26.3.27`.
|
||||
- ⚠️ opkg-пакет `xray-core` числит 25.2.21 (метаданные устарели) — НЕ делать `opkg upgrade xray-core` (откатит).
|
||||
|
||||
## Подписки в игре
|
||||
- **qomar** (`pro.qomar.pw/sub/...`) — 3 группы sub0/sub1/sub2 → цепочка xray_chain L1→L2→L3.
|
||||
- **renawave** (`sub.renawave.space/eQHzgWHz6xwZwLJP`, HWID 4378f94b..., Happ-заголовки) → xray-sim балансер.
|
||||
- `servers.txt` (локально) — курированный список RU-whitelist reality/xhttp серверов (SNI ads.x5.ru / yandex.cloud / yandex.ru).
|
||||
</content>
|
||||
</invoke>
|
||||
@@ -0,0 +1,156 @@
|
||||
# luci-app-xray (рабочее название) — дизайн-черновик
|
||||
|
||||
**Цель:** отдельный OpenWrt-плагин для управления XRAY (транспарент-прокси, подписки,
|
||||
цепочки, роутинг), переносимый на любой роутер и не привязанный к текущей задаче.
|
||||
Ориентир: passwall / passwall2, но чище, удобнее, современнее.
|
||||
|
||||
## Реально ли? — Да.
|
||||
Прецеденты: passwall/passwall2, luci-app-ssr-plus, **homeproxy** (sing-box, чистый ucode+JS),
|
||||
OpenClash / nikki (mihomo), shadowsocks-libev+luci. Технически всё это стандартный
|
||||
OpenWrt-стек: UCI + LuCI(JS) + ubus/rpcd + procd + fw4/nftables + policy routing.
|
||||
|
||||
## Чем существующие плохи (наша ниша «лучше»)
|
||||
- **passwall/passwall2**: мощно, но тяжело; смесь iptables/nftables; UI перегружен;
|
||||
конфиг-спагетти; куча хелпер-бинарей; заточка под Китай (chnroute/gfwlist); хрупкие апгрейды.
|
||||
- **homeproxy**: чисто и красиво, но только sing-box; слабое многоуровневое чейнинг/ротация.
|
||||
- **OpenClash/nikki**: mihomo-центрично.
|
||||
- Чего никто не делает хорошо (наши дифференциаторы):
|
||||
1. **Multi-hop цепочки** (L1→L2→L3) с посубскрипшн-слоями (наш xray_chain — ровно это).
|
||||
2. **Subscription-driven балансер + observatory** с авто-выпилом мёртвых нод.
|
||||
3. **Прозрачная модель роутинга** (метки/таблицы видно, не магия).
|
||||
4. **Современные транспорты** (reality/xhttp/vision) как первый класс.
|
||||
5. **RU-whitelist сценарии** (reality с whitelisted-SNI) из коробки.
|
||||
|
||||
## Ключевые архитектурные решения (обсудить)
|
||||
|
||||
### 1. Движок
|
||||
xray-core (наша экспертиза: reality/xhttp/vision). Генератор конфига делать
|
||||
**pluggable backend**, чтобы позже подключить sing-box — но НЕ абстрагировать преждевременно.
|
||||
|
||||
### 2. Генератор конфига (сердце) — самое важное
|
||||
Уход от хрупкого shell/sed JSON (текущий update.sh). Варианты:
|
||||
- **(A) ucode** — нативный OpenWrt, без зависимостей (важно на слабых роутерах), но парсинг
|
||||
vless/vmess/xhttp share-links надо писать самим.
|
||||
- **(B) Go-компаньон-бинарь** — маленький хелпер, **переиспользует парсер share-link'ов
|
||||
самого xray-core** → корректность vless/vmess/trojan/ss/**xhttp/reality** бесплатно,
|
||||
никаких regex-багов (как тот xhttp→tcp). Килл-фича.
|
||||
- Рекомендация: **B** для парсинга подписок + генерации, **ucode** для склейки UCI→JSON.
|
||||
Либо целиком Go-хелпер `xrayctl` (sub-fetch + build + validate `xray -test`).
|
||||
|
||||
### 3. UCI-схема `/etc/config/xray`
|
||||
Секции: `node` (сервер), `subscription` (url+headers+layer), `chain` (L1..Ln),
|
||||
`policy` (правила роутинга/сплит), `inbound` (tproxy/socks/http), `dns`, `global`.
|
||||
LuCI правит UCI → генератор рендерит xray JSON.
|
||||
|
||||
### 4. Роутинг/фаервол — только fw4/nftables (без legacy iptables)
|
||||
- **TPROXY** для TCP+UDP (не REDIRECT — UDP/QUIC). `/etc/nftables.d/` чанки внутри `table inet fw4`.
|
||||
- Дисциплина меток: divert-mark → `ip rule → table N → local default dev lo`;
|
||||
sockopt.mark на всех in/outbound xray = разрыв петли (у нас 0x162; в скиле 0xff).
|
||||
- sysctl: rp_filter=0, route_localnet=1, ip_forward=1. flow_offloading ОБЯЗАТЕЛЬНО off.
|
||||
- Опционально TUN-режим (tun2socks/sing-box) как escape-hatch.
|
||||
- Split по домену/гео: dnsmasq `nftset` → `@proxy4/@proxy6` (нужен dnsmasq-full).
|
||||
|
||||
### 5. DNS anti-leak (где все текут)
|
||||
Форс LAN DNS на роутер + блок DoT/DoH; DoH-резолвер; сплит-DNS; опц. **FakeIP/FakeDNS**
|
||||
(zero-leak). Зеркалить всё на IPv6 или течёт.
|
||||
|
||||
### 6. Подписки
|
||||
Fetch с кастомными заголовками (Happ HWID/User-Agent), кеш, несколько провайдеров,
|
||||
привязка sub→layer, авто-обновление (cron/timer), health-based прунинг.
|
||||
|
||||
### 7. Сервисы (procd)
|
||||
Мультиинстанс (chain + sim/failover), reload-on-config-change (`procd_set_param file`),
|
||||
корректный boot-order: network → firewall(sets+chains) → dnsmasq(@proxy) → xray + hotplug(route).
|
||||
|
||||
### 8. UI/UX (где живёт «лучше»)
|
||||
Современный client-side JS LuCI (не Lua CBI):
|
||||
- список нод с **живым пингом** (из observatory через ubus), one-click импорт подписки,
|
||||
визуальные правила роутинга, **per-client policy** (девайс X → цепочка, Y → direct),
|
||||
дашборд статуса (активная нода, throughput, health), просмотр логов.
|
||||
- backend: ucode rpcd-объект (`xray.status/nodes/reload/...`) + ACL.
|
||||
|
||||
### 9. Переносимость
|
||||
Никаких хардкод-интерфейсов/IP. Детект WAN, всё через UCI. Arch-agnostic:
|
||||
LuCI-часть `PKGARCH:=all`; xray-core как зависимость/feed; фид на несколько таргетов (CI).
|
||||
|
||||
### 10. Дистрибуция
|
||||
opkg/apk фид, CI-сборка (openwrt/gh-action-sdk), подпись (usign/APKINDEX), self-update.
|
||||
|
||||
## Риски / трудные места
|
||||
DNS-leak (вечное); UDP/FullCone NAT; смены схемы xray; fw4↔procd↔hotplug boot-order;
|
||||
IPv6; миграция конфигов при апгрейде; тесты на разных таргетах; flow offloading.
|
||||
|
||||
## MVP (чтобы реально выкатить, а не утонуть как passwall)
|
||||
1. UCI-схема + генератор (Go `xrayctl`: fetch+parse+build+`xray -test`).
|
||||
2. Один tproxy-инстанс, TCP+UDP, br-lan.
|
||||
3. Импорт подписки (vless/vmess/trojan/ss + xhttp/reality) через xray-парсер.
|
||||
4. Балансер + observatory + прунинг мёртвых.
|
||||
5. fw4-интеграция + policy routing (hotplug-персист).
|
||||
6. DNS: DoH + hijack + базовый сплит (bypass RU/LAN).
|
||||
7. Минимальный LuCI: ноды+живой пинг, подписки, on/off, статус.
|
||||
Далее: multi-hop цепочки, per-client policy, гео/домен-правила, WAN-failover-режимы.
|
||||
|
||||
## РЕШЕНИЯ (locked, 2026-07-08)
|
||||
- **Движок: только xray-core.** Генератор пишем так, чтобы sing-box можно было
|
||||
добавить позже адаптером, но сейчас не абстрагируем.
|
||||
- **Генератор: Go-компаньон `xrayctl`**, переиспользует парсинг share-link'ов
|
||||
(кандидат — **libXray** `github.com/XTLS/libXray`, ф-я `ConvertShareLinksToXrayJson`),
|
||||
валидация через `xray -test` до применения.
|
||||
- **MVP: solid single transparent-proxy сначала** (класс homeproxy). Multi-hop
|
||||
цепочки / per-client / гео — фаза 2.
|
||||
|
||||
## Компоненты MVP
|
||||
1. **`xrayctl`** (Go, отдельный пакет через golang-package.mk):
|
||||
- `sub fetch` — тянет подписку(и) с заголовками (Happ HWID/UA), кеш.
|
||||
- `sub parse` — share-links → xray-outbounds (libXray).
|
||||
- `build` — из UCI `/etc/config/xray` собирает полный JSON (inbounds+routing+dns+balancer).
|
||||
- `test` — `xray -test` перед применением.
|
||||
- `apply` — пишет конфиг, дёргает reload сервиса.
|
||||
- `status`/`nodes` — отдаёт JSON (observatory latency, active node) для UI.
|
||||
2. **UCI `/etc/config/xray`**: секции `global`, `subscription`, `node`, `inbound`,
|
||||
`dns`, `policy` (routing), позже `chain`.
|
||||
3. **procd** `/etc/init.d/xray` — xray run + respawn + reload-on-file.
|
||||
4. **fw4** `/etc/nftables.d/` — tproxy TCP+UDP, дисциплина меток, разрыв петли (sockopt.mark).
|
||||
5. **policy routing** — hotplug (ip rule + `local default dev lo` в отд. таблице) + sysctl.
|
||||
6. **DNS** — dnsmasq nftset (@proxy4/6) + DoH + hijack :53 + блок DoT/DoH.
|
||||
7. **LuCI `luci-app-<name>`** (client-side JS): views Nodes(живой пинг)/Subscriptions/
|
||||
Settings/Status; backend — ucode rpcd-объект `xray` → зовёт `xrayctl`; menu.d+acl.d+uci-defaults.
|
||||
8. **Дистрибуция** — luci.mk (app, PKGARCH=all) + golang-package.mk (xrayctl), CI
|
||||
gh-action-sdk на мультиарк, свой фид (usign/APKINDEX).
|
||||
|
||||
## Репозиторий (черновик структуры)
|
||||
```
|
||||
<name>/
|
||||
├── xrayctl/ # Go companion (go.mod, main.go, sub/ build/ ...)
|
||||
│ └── Makefile # golang-package.mk
|
||||
├── luci-app-<name>/
|
||||
│ ├── Makefile # luci.mk, LUCI_DEPENDS:=+xrayctl +xray-core +dnsmasq-full +kmod-nft-tproxy
|
||||
│ ├── htdocs/luci-static/resources/view/<name>/{nodes,subs,settings,status}.js
|
||||
│ └── root/
|
||||
│ ├── usr/share/luci/menu.d/…json
|
||||
│ ├── usr/share/rpcd/acl.d/…json
|
||||
│ ├── usr/share/rpcd/ucode/<name>.uc
|
||||
│ ├── etc/uci-defaults/xx_<name>
|
||||
│ ├── etc/init.d/<name>
|
||||
│ ├── etc/nftables.d/90-<name>-tproxy.nft
|
||||
│ └── etc/hotplug.d/iface/99-<name>
|
||||
├── .github/workflows/build.yml # multi-arch SDK build
|
||||
└── feed/ # generated Packages/APKINDEX
|
||||
```
|
||||
|
||||
## Фазы
|
||||
- **P1a** xrayctl (fetch/parse/build/test/apply) + UCI-схема + procd — CLI-рабочий.
|
||||
- **P1b** fw4 + policy routing + DNS (один tproxy-инстанс, br-lan, TCP+UDP).
|
||||
- **P1c** LuCI (nodes+пинг / subs / settings / status).
|
||||
- **P2** multi-hop цепочки (перенос xray_chain), per-client policy, гео/домен, failover, sing-box-адаптер.
|
||||
|
||||
## Тест-стратегия (важно: не сломать прод)
|
||||
mini_router в проде (xray_chain на :12345/mark 0x162). Новый стек обкатывать
|
||||
**параллельно** на других портах/метке/таблице и отдельном UCI, не трогая боевой
|
||||
xray_chain; переключать только когда готово. Либо тест-таргет (VM/контейнер/спейр-роутер).
|
||||
|
||||
## Открыто (не блокирует старт)
|
||||
- Имя проекта/бренд (варианты: xwrt / proxywrt / xgate / xkit).
|
||||
- libXray vs вендорить свой Go-парсер share-links (решим при первом коде xrayctl).
|
||||
- Публикация: свой GitHub + фид сразу, или сначала приватно.
|
||||
</content>
|
||||
@@ -0,0 +1,124 @@
|
||||
# Фиче-модель и концепция (обсуждение)
|
||||
|
||||
## Форкаем ли xray? — НЕТ.
|
||||
xray-core остаётся **неизменным** (это data-plane движок, ставим как есть).
|
||||
Наш `xrayctl` — **отдельный control-plane демон рядом**, а не форк:
|
||||
- генерит xray JSON из UCI-модели,
|
||||
- управляет подписками, роутингом, фаерволом, policy-routing, procd,
|
||||
- читает **API xray** (gRPC Stats/Observatory) для live-пинга/трафика в UI,
|
||||
- использует xray как **библиотеку** только для парсинга share-links (libXray) и валидации.
|
||||
|
||||
Форк = ад поддержки (xray на 26.6.x, релизы каждые недели — мы это уже видели).
|
||||
Аналогия: xray = двигатель, xrayctl = ЭБУ+панель. passwall/homeproxy делают ровно так —
|
||||
не форкают ядро, а оркестрируют его.
|
||||
|
||||
Два уровня «переиспользования кода xray» (оба ≠ форк):
|
||||
- (a) импорт xray/libXray как Go-модуля в xrayctl (парсинг/валидация, опц. встроить рантайм);
|
||||
- (b) shell к стоковому бинарю `xray` для run/test.
|
||||
Рекомендация: гибрид — libXray для парсинга (корректность), стоковый xray под procd для data-plane.
|
||||
Если чего-то не хватит в xray — контрибьютим апстрим или через его API, но не форк.
|
||||
|
||||
## Концепция (data-model) — как описать ВСЁ декларативно, не утонув
|
||||
Ключевой инсайт из текущего сетапа: это **конвейер слоёв + policy-routing**.
|
||||
Чистая модель из 7 объектов покрывает всё, что есть сейчас, и больше:
|
||||
|
||||
1. **Node** — сервер/эндпоинт (vless/vmess/trojan/ss/xhttp/reality; позже wg/awg-peer).
|
||||
2. **Group** — набор нод (обычно = подписка) + стратегия выбора
|
||||
(balancer: leastPing / random / roundrobin / failover-priority + observatory health).
|
||||
3. **Chain** — упорядоченные хопы L1→L2→L3; каждый хоп = Group (случайная нода) или фикс-Node.
|
||||
(Ровно твой xray_chain «inverted chain».)
|
||||
4. **Outlet / Egress** — куда в итоге выходит: **интерфейс** (eth1/wwan/eth2), **туннель**
|
||||
(awgOut/awgHome/любой wg), **Chain/Group**, **direct**, **block**. ← «выбор интерфейса выхода».
|
||||
5. **Inbound / Entry** — как трафик входит: tproxy (LAN transparent), socks/http (локально),
|
||||
dokodemo (заворот awg-UDP).
|
||||
6. **Rule** — matcher → target(+egress). Упорядочены (первый match побеждает). Явно:
|
||||
- **source**: CIDR/хост (`192.168.11.0/24`, `192.168.11.14/32`, IPv6), **MAC** (девайс
|
||||
даже при смене IP по DHCP), **interface/зона** (br-lan / гостевой SSID / VLAN), (позже) расписание.
|
||||
- **dest**: domain / geosite / geoip / ip-CIDR / port / l4proto.
|
||||
- **target**: Chain | Group | Node | direct | block, с привязкой **Egress**.
|
||||
Где применяется source-CIDR: **два слоя** — (a) в nft prerouting грубо решаем, какие
|
||||
источники вообще заходят в прокси (`ip saddr 192.168.11.14 … tproxy`), (b) в xray
|
||||
routing тонко выбираем target (`"source": ["192.168.11.14/32"] → balancerTag/outboundTag`).
|
||||
→ полный контроль «девайс X → цепочка A через awgOut, девайс Y → direct».
|
||||
|
||||
**Визуализация «откуда→куда→как→почему» (killer-фича):**
|
||||
- `xrayctl explain <src> <dst>` — прогоняет набор правил и печатает путь решения:
|
||||
matched rule → выбранная Chain/Node → Egress-iface → exit-IP (аналог `ip route get`,
|
||||
но для прокси-политики). В UI — «trace/explain» кнопка.
|
||||
- live-соединения из xray-API: src → dst → какой outbound реально используется.
|
||||
7. **Profile / WAN-mode** — условные оверрайды по активному WAN
|
||||
(«если default=eth2(SIM) → заворачивать awg в VLESS»). Обобщение твоего sim-setup.sh.
|
||||
8. **List / Ruleset** — переиспользуемые именованные списки (**domain-list** / **ip-cidr-list**),
|
||||
источник **inline / файл / URL** (авто-обновление + кеш). Ссылаются из Rule (`dest in @list`)
|
||||
и из DNS-правил. Реализация: domain→dnsmasq nftset + xray domain-rules; ip→nft set + xray ip-rules.
|
||||
|
||||
**Мульти-LAN:** инбаунд-перехват настраивается на **одну или несколько** LAN-сетей/интерфейсов/
|
||||
бриджей (br-lan / guest / VLAN'ы), LAN-подсеть указывается явно; правила матчат по inbound/source-зоне.
|
||||
|
||||
Генератор (`xrayctl build`) из этой модели рендерит:
|
||||
- xray JSON (inbounds, outbounds по нодам, balancers, observatory, routing inboundTag→balancerTag
|
||||
для слоёв цепочки — твой inverted-chain паттерн),
|
||||
- nftables-чанки (tproxy, метки, per-egress),
|
||||
- ip rule + таблицы (по каждой привязке egress),
|
||||
- dnsmasq nftset + DoH,
|
||||
- procd-инстансы.
|
||||
→ Твой текущий xray_chain + awg-wrap + failover становится **выразим декларативно** и воспроизводим на любом роутере.
|
||||
|
||||
## Выбор egress (твой явный запрос) — first-class
|
||||
Любое Rule/Chain может «прибить» выход к:
|
||||
- физическому WAN (eth1 / wwan / eth2),
|
||||
- туннелю (awgOut / awgHome / любой wg/awg),
|
||||
- прокси-Chain/Group,
|
||||
- direct,
|
||||
- и комбо (Chain → затем через awgOut → затем физический WAN).
|
||||
Реализация: policy routing (fwmark → table → dev) + xray outbound sockopt.mark + бинд на iface.
|
||||
Плагин **сам** раскладывает метки/таблицы/правила из модели — вот где passwall неуклюж, а мы чисто.
|
||||
|
||||
## Почему passwall2 лагает и как мы не лагаем
|
||||
passwall гоняет кучу хелпер-процессов (dns2socks, chinadns, ipt2socks, haproxy, процесс на ноду),
|
||||
тяжёлый shell, iptables → лаг = лишние userspace-хопы + нет offload + DNS-оверхед.
|
||||
Мы:
|
||||
- **один процесс xray** мультиплексирует всё внутри (routing rules + balancers), не процесс-на-ноду;
|
||||
- **nftables tproxy** — kernel fast-path; bypass-трафик (RU/CN/private) **не заходит в userspace** (nftset);
|
||||
- **flow-offload** оставляем для bypass-трафика;
|
||||
- live-health из **xray API**, а не curl-спам;
|
||||
- рычаги: XUDP/mux, reality 0-RTT, sniffing routeOnly, без per-conn DNS.
|
||||
|
||||
## Фичи по тирам
|
||||
- **Tier 0 (MVP):** ноды+подписки, один tproxy, balancer+health, DNS-сплит, базовые правила
|
||||
(domain/geo bypass), UI (ноды с пингом / подписки / статус).
|
||||
- **Tier 1:** **выбор egress** (per-rule бинд на iface/tunnel), **per-client policy** (девайс→таргет),
|
||||
**multi-hop цепочки** (перенос xray_chain).
|
||||
- **Tier 2:** **WAN-mode профили/failover**, **awg-wrap** (заворот UDP-туннеля в VLESS),
|
||||
FakeIP/FakeDNS, полный IPv6, дашборд статистики (throughput/health), мультиинстанс.
|
||||
- **Tier 3:** sing-box-адаптер, экспорт/импорт конфигов, **правила по расписанию**
|
||||
(whitelist-часы), внешний API, темы UI.
|
||||
|
||||
## Источники нод и их идентичность (выбор группа vs отдельная нода)
|
||||
**Node source:**
|
||||
- `subscription` — remote URL, обновляемый. Профили заголовков:
|
||||
- **HAPP-эмуляция:** x-hwid (**задать вручную ИЛИ авто-сгенерить и запомнить per-sub**),
|
||||
x-device-os / x-ver-os / x-device-model, User-Agent `Happ/x.y.z`.
|
||||
- обычная подписка (base64-список / plain / позже Clash-yaml).
|
||||
- `manual` — статические ноды, НЕ обновляются и НЕ пропадают. Способы добавления:
|
||||
вставка одной share-link-строки, **много строк через `\n`**, **загрузка текст-файла**.
|
||||
|
||||
**Идентичность ноды (fingerprint):** стабильный хеш по connection-defining полям
|
||||
(protocol+address+port+id/password+network+security+sni+path/serviceName). Правила и выбор
|
||||
ссылаются на ноду **по fingerprint** (или group+fingerprint), НЕ по индексу.
|
||||
|
||||
**Персистентность при обновлении подписки (явный вопрос пользователя):**
|
||||
- reconcile по fingerprint: новые — добавить; исчезнувшие — пометить `stale`
|
||||
(держим N обновлений / до ручного удаления), НЕ удаляем молча;
|
||||
- если «прибитая» правилом нода исчезла → **fallback по политике**: балансер группы /
|
||||
direct / block (выбор). Никогда не тихий обрыв;
|
||||
- manual-ноды стабильны по своему id, авто-удалению не подлежат.
|
||||
|
||||
**Selection:** target правила = **группа** (динамический состав + балансер) ИЛИ
|
||||
**конкретная нода** (по fingerprint; для подписочных = pin + fallback выше).
|
||||
|
||||
## Открытые вопросы для следующего шага
|
||||
- Насколько глубоко сразу закладывать egress-selection и profiles в UCI-схему (даже если UI позже)?
|
||||
- Живой пинг/статы: тянуть из gRPC-API xray (нужен `api`+`policy`+`stats` в конфиге) — ок?
|
||||
- Нужен ли TUN-режим (для приложений, которые tproxy не ловит) или tproxy достаточно?
|
||||
</content>
|
||||
@@ -0,0 +1,71 @@
|
||||
# Операционные требования: надёжность, сборка, статистика
|
||||
|
||||
## A. Надёжность («железно, таблицы не ломаются, просто работает»)
|
||||
|
||||
Главный страх (болезнь passwall) — оставить битые ip rule/route/nft после краша или
|
||||
переконфига и потерять связь. Контракт:
|
||||
|
||||
1. **Атомарное применение (всё-или-ничего).** `xrayctl apply` собирает ПОЛНОЕ новое
|
||||
состояние, валидирует, потом применяет атомарно:
|
||||
- xray: `xray -test` до подмены; старый инстанс жив, пока новый не провалидирован.
|
||||
- nftables: весь ruleset одним `nft -f` (nft транзакционен — либо весь файл, либо ничего).
|
||||
- ip rule/route: реконсайл (desired vs current), не слепой append.
|
||||
2. **Свои ресурсы, чужое не трогаем.** Резервируем **свой диапазон fwmark**, **свои
|
||||
table-ID**, и **собственную таблицу `inet xproxy`** (НЕ правим fw4-таблицу).
|
||||
fw4 reload не вайпит нашу таблицу (она отдельная) → изоляция + атомарный swap.
|
||||
dnsmasq nftset направляем в нашу таблицу (`…#inet#xproxy#set`).
|
||||
3. **Идемпотентный реконсайл.** Каждый apply сходится к desired-состоянию (добавить
|
||||
недостающее, убрать устаревшее), без накопления дублей (грех passwall). Всё «наше»
|
||||
помечено → чисто находим и удаляем только своё.
|
||||
4. **Персистентность.** ip rule/route вайпятся на `network reload`/reboot →
|
||||
переприменяем через **hotplug** (`/etc/hotplug.d/iface`) на ifup/ifdown; procd
|
||||
reload-on-config. (Твой sim-setup уже делает это — формализуем в реконсайл.)
|
||||
5. **Rollback + commit-confirm.** Держим снапшот last-known-good; `xrayctl rollback`.
|
||||
Опция «apply, и если нет подтверждения за N сек → авто-откат» (как safe-mode
|
||||
MikroTik / LuCI apply-unchecked) → **никогда не залочишь себя**.
|
||||
6. **Fail-safe.** Управляющий доступ (SSH/LuCI/LAN, DNS роутера) ВСЕГДА в обход прокси.
|
||||
Поведение при падении xray — выбор: fail-open (direct) / fail-closed (block) на политику.
|
||||
7. **Watchdog/дрейф.** procd respawn для xray; периодический реконсайл xrayctl ловит
|
||||
расхождение и переприменяет.
|
||||
8. **Гейты валидации:** `xray -test` + `nft -c -f` + connectivity-probe до commit.
|
||||
9. **Loop-guards встроены** (sockopt.mark, bypass RFC1918/loopback/IP-сервера) — из tproxy-скила.
|
||||
|
||||
## B. Сборка под OpenWrt + ImmortalWrt/BananaWRT
|
||||
|
||||
**BananaWRT** = кастомная **ImmortalWrt**-сборка (SuperKali) под BPi-R3 Mini + Fibocom
|
||||
FM350, свой CI и репозиторий. То есть это ImmortalWrt-форк → **формат пакетов тот же**,
|
||||
что у OpenWrt/ImmortalWrt (ABI-совместимо). Значит:
|
||||
|
||||
- **Дистрибутивы:** собираем против **OpenWrt SDK** и **ImmortalWrt SDK** (BananaWRT
|
||||
ставит ImmortalWrt/OpenWrt-пакеты штатно).
|
||||
- **Версии:** 24.10 (opkg/`.ipk`) + 25.x (apk/`.apk`) — **шипаем оба** (Makefile один,
|
||||
формат решает SDK).
|
||||
- **Арки:** `aarch64_cortex-a53` (этот роутер) + `x86_64` (QEMU-стенд) + ходовые
|
||||
(`mipsel_24kc`, `arm_cortex-a7_neon-vfpv4`, `aarch64_generic`…).
|
||||
- **CI:** `openwrt/gh-action-sdk` матрица {SDK: openwrt/immortalwrt × версия} × {арка}.
|
||||
- `luci-app-<name>` — `PKGARCH:=all` (собираем один раз).
|
||||
- `xrayctl` — per-arch (Go, `golang-package.mk`, dep `golang/host`, `$(GO_ARCH_DEPENDS)`).
|
||||
- ImmortalWrt-таргеты: их SDK-образы (можно указать URL SDK / отдельный action-инстанс).
|
||||
- **Фид:** подписанный (usign для ipk / EC для apk), GitHub Pages/Release, one-line
|
||||
установка ключа+фида, self-update по bump `PKG_RELEASE`.
|
||||
- ⚠️ **kmod'ы пиннятся к точному хешу ядра** — `kmod-nft-tproxy`/`kmod-nft-socket`
|
||||
либо в DEPENDS как системные (обычно уже есть), либо собирать против идентичного ядра.
|
||||
|
||||
## C. Статистика / дашборд («сколько трафика съела прокся», per-rule, дашборд)
|
||||
|
||||
Источники (всё без форка, из штатного xray + nft):
|
||||
- **Per-proxy трафик:** xray **StatsService (gRPC API)** — счётчики
|
||||
`outbound>>>TAG>>>traffic>>>uplink|downlink` (включить `api`+`stats`+`policy.system.
|
||||
statsOutboundUplink/Downlink`). → «какая нода/прокся сколько байт съела».
|
||||
- **Live-пинг/health:** **Observatory** (per-outbound alive+latency).
|
||||
- **Per-rule статистика:** xray правила сами не считаются → вешаем **nft `counter`**
|
||||
на каждое наше marking-правило (packets/bytes на firewall-слое). → hits/байты по правилу.
|
||||
- **История/графики:** на роутере УЖЕ есть **collectd + luci-statistics (RRD)** —
|
||||
можно фидить xray-статы туда → готовые исторические графики. Или свой лёгкий ring-buffer.
|
||||
- **Отдача в UI:** `xrayctl stats` (ubus-метод) агрегирует xray-API + nft-counters +
|
||||
observatory → LuCI поллит → дашборд: throughput во времени, байты по нодам,
|
||||
хиты/байты по правилам, health, активная нода, live-соединения (src→dst→outbound).
|
||||
|
||||
Дашборд-виджеты (MVP-плюс): сводка (актив-нода, вверх/вниз, аптайм туннеля),
|
||||
таблица нод (пинг/alive/трафик/выбор), таблица правил (матчи/байты), лог, «explain trace».
|
||||
</content>
|
||||
@@ -0,0 +1,120 @@
|
||||
# Полный каталог фич (WHAT) — master
|
||||
|
||||
Теги: **[MVP]** фаза 1 · **[T1]** дифференциаторы · **[T2]** расширения · **[T3]** дальний прицел.
|
||||
Принцип: **всё настраивается** (easy-режим с пресетами поверх advanced-режима).
|
||||
|
||||
## 1. Ноды / Endpoints
|
||||
- Протоколы: **[MVP]** vless, vmess, trojan, shadowsocks (вкл. 2022-blake3), socks, http;
|
||||
**[T2]** wireguard/amneziawg-outbound, dokodemo; **[T3]** hysteria2/tuic (только через sing-box-адаптер).
|
||||
- Транспорты: **[MVP]** tcp/raw, ws, grpc, **xhttp**, httpupgrade; kcp/quic — по возможности.
|
||||
- Security: **[MVP]** none, tls, **reality**; flow `xtls-rprx-vision`, fingerprint, alpn, sni, allowInsecure.
|
||||
- Мультиплексирование: **[T1]** mux/XUDP (вкл/выкл, concurrency), per-node sockopt/mark.
|
||||
- Добавление вручную: **[MVP]** одна share-link; **много строк через `\n`**; **загрузка текст-файла**;
|
||||
**[T1]** импорт из буфера; **[T2]** импорт по QR (из картинки), экспорт ноды в QR/ссылку.
|
||||
- Идентичность: **[MVP]** fingerprint (хеш connection-полей), stale-пометка, ручное удаление.
|
||||
- Тест ноды: **[MVP]** ping/alive (реальный коннект), latency; **[T1]** real-delay через узел, speedtest.
|
||||
|
||||
## 2. Группы / Подписки
|
||||
- Источник: **[MVP]** `subscription` (URL) / `manual` (статический набор).
|
||||
- Форматы подписки: **[MVP]** base64-список share-links, plain-список; **[T2]** Clash/Mihomo YAML,
|
||||
sing-box JSON, Xray JSON.
|
||||
- Заголовки: **[MVP]** **HAPP-эмуляция** (x-hwid ручной/**авто-генерируемый и запоминаемый**,
|
||||
x-device-os/x-ver-os/x-device-model, User-Agent Happ/x.y.z); **[MVP]** произвольные custom-헤aders.
|
||||
- Обновление: **[MVP]** **интервал НА КАЖДУЮ подписку** (напр. 30m / 6h / 24h / custom) +
|
||||
глобальный дефолт + **кнопка «обновить сейчас»** + при бутe; jitter + ретраи; fetch **напрямую
|
||||
ИЛИ через прокси** (когда хост подписки заблокирован); кеш + use-cache-on-fail; после апдейта →
|
||||
авто-reconcile нод (fingerprint, не рвём пины). Реализация: procd-timer / cron на подписку.
|
||||
- **[T1]** userinfo из ответа подписки (лимит/остаток трафика, срок действия) → дашборд + алерт «истекает».
|
||||
- Фильтры: **[T1]** include/exclude по regex имени/тега, по протоколу, по стране (эмодзи-флаги/гео), дедуп.
|
||||
- Стратегия группы (балансер): **[MVP]** leastPing, random, roundRobin, single(fixed);
|
||||
**[T1]** leastLoad, **failover(priority order)**. Observatory: probeURL/interval/timeout/sampling.
|
||||
- Персистентность выбора при refresh (см. док 03): reconcile по fingerprint + fallback.
|
||||
|
||||
## 3. Цепочки (multi-hop) [T1]
|
||||
- Упорядоченные хопы; каждый хоп = группа (балансер) или фикс-нода.
|
||||
- Сборка = inverted proxySettings (перенос твоего xray_chain); число слоёв произвольное.
|
||||
- End-to-end health-probe цепочки; цепочка как **target** правила и как **egress**.
|
||||
|
||||
## 4. Инбаунды / точки входа
|
||||
- **[MVP]** Transparent (**TPROXY**), **TCP+UDP**. **Мульти-LAN:** выбираешь одну или **несколько
|
||||
LAN-сетей/интерфейсов/бриджей** (br-lan/guest/VLAN'ы) — каждую перехватываем отдельно; LAN-подсеть
|
||||
указывается явно; порт(ы) настраиваются. Правила матчат по inbound/source-зоне.
|
||||
- **[T1]** Локальные SOCKS/HTTP инбаунды (для самого роутера и других приложений).
|
||||
- **[T2]** dokodemo для заворота конкретного трафика (awg-wrap).
|
||||
- **[T2]** TUN-режим (tun2socks/sing-box) как альтернатива tproxy для краевых случаев.
|
||||
|
||||
## 5. Роутинг / движок правил
|
||||
- Match: **[MVP]** source (CIDR/**host `/32`**/**MAC**/iface/зона), dest (domain / domain-suffix /
|
||||
domain-keyword / **geosite** / **geoip** / ip-CIDR / port / port-range), l4proto (tcp/udp), inbound;
|
||||
**[T3]** расписание (время суток / дни).
|
||||
- Sniffing: **[MVP]** recover SNI/Host/QUIC для domain-правил (routeOnly).
|
||||
- Target: **[MVP]** chain*/group/node/direct/block + **egress-binding**. (*chain — с T1)
|
||||
- Порядок + first-match + default-rule. **[MVP]**
|
||||
- Пресет-паки правил: **[MVP]** bypass RU+private+LAN, all-else→proxy; **[T1]** block-ads
|
||||
(geosite:category-ads), split по спискам; тумблеры пресетов.
|
||||
- **Per-client policy** (девайс → target) как отдельный удобный UI поверх правил. **[T1]**
|
||||
- Гео-данные: **[MVP]** менеджмент geoip.dat/geosite.dat (скачать/обновить), выбор источника.
|
||||
- **explain/trace**: «откуда→куда→как→почему» (`xrayctl explain src dst`). **[T1]**
|
||||
|
||||
## 5b. Списки / Rulesets (переиспользуемые)
|
||||
- **List** — именованный, типизированный: **domain-list** / **ip-cidr-list** / (port-list). **[MVP]**
|
||||
- Источник: **inline** (правка в UI) / **файл** (upload/путь) / **URL** (remote, авто-обновление
|
||||
по интервалу + кеш). **[MVP inline/файл; T1 URL-auto]** — «домен-листы и айпи-листы, в т.ч. выходные».
|
||||
- Форматы-адаптеры: plain (строку/строку), dnsmasq, **Clash rule-provider**, **geosite-category**,
|
||||
sing-box ruleset (srs), v2ray dat. (MVP: plain + clash-provider + geosite.)
|
||||
- Использование в правилах: `dest domain in @list:ru-bypass`, `dest ip in @list:ad-ips`, `source in @list`.
|
||||
- Реализация: domain-list → dnsmasq nftset + xray domain-rules; ip-list → nft set + xray ip-rules.
|
||||
- **[T1]** курируемые бандлы по требованию (RU-bypass antifilter/re:filter, ads, private) + полностью кастомные.
|
||||
|
||||
## 6. Egress / Выходы
|
||||
- **[MVP/T1]** физ-WAN (любой iface), туннель (любой wg/awg), прокси (chain/group/node), direct, block.
|
||||
- **[T1]** комбо (цепочка → затем через конкретный iface).
|
||||
- **[MVP]** автодетект доступных интерфейсов.
|
||||
|
||||
## 7. Профили / WAN-mode / Failover [T2/T3]
|
||||
- Условные оверрайды по: активному default-iface / connectivity-probe / расписанию.
|
||||
- Кейсы: SIM-режим → заворот awg-UDP в VLESS; whitelist-часы → RU-reality серверы.
|
||||
- Атомарное переключение профиля (без разрыва управляющего доступа).
|
||||
|
||||
## 8. DNS (полностью настраиваемый)
|
||||
- **Named resolvers** **[MVP]**: тип (DoH / DoT / plain / local-dnsmasq / **FakeIP**) + адрес +
|
||||
опц. **detour** (резолв через конкретный outbound / direct / цепочку).
|
||||
- **DNS-routing rules** **[MVP]**: match domain / domain-list / geosite / **client(source)** → резолвер X
|
||||
(per-domain / per-client / per-rule DNS). Default-резолвер + fallback.
|
||||
- **Split-DNS** **[MVP]**: direct-домены → ISP/local; proxied → proxy-side резолвер (без утечки; resolved==routed).
|
||||
- **FakeIP** **[T1]** (решение: оба режима переключаемо): pool 198.18.x + fakeip-domain-list / real-ip-list.
|
||||
- **[MVP]** hijack LAN :53 → роутер; блок клиентского DoT/DoH-байпаса; **IPv6-зеркало**.
|
||||
- **[MVP]** domain-list → nftset для роутинга; согласованность resolved==routed.
|
||||
|
||||
## 9. Надёжность / Ops (топ-приоритет — см. док 04)
|
||||
- **[MVP]** атомарный apply, свои таблицы/метки, идемпотентный реконсайл, hotplug-персист,
|
||||
**rollback + commit-confirm**, fail-safe **mgmt-bypass**, watchdog, boot-order.
|
||||
- **[MVP]** kill-switch: fail-closed(block)/fail-open(direct) — глобально и/или на политику.
|
||||
|
||||
## 10. Observability / Дашборд
|
||||
- **[MVP]** статус: throughput up/down, активная нода/цепочка, health туннеля, uptime.
|
||||
- **[MVP]** таблица нод: ping/alive/трафик/выбор; сортировка по latency; поиск/фильтр.
|
||||
- **[T1]** per-proxy трафик (байты, xray Stats API); per-rule hits/байты (nft counters);
|
||||
live-соединения (src→dst→outbound); история-графики (collectd/RRD).
|
||||
- **[MVP]** просмотр логов; **[T1]** explain/trace виджет.
|
||||
|
||||
## 11. UX
|
||||
- **[MVP]** one-click импорт подписки; вставка из буфера; RU/EN i18n.
|
||||
- **[T1]** bulk-actions над нодами; **[T2]** QR импорт/экспорт; dark-mode (тема LuCI).
|
||||
- **[MVP]** экспорт/импорт всего конфига (backup/restore); **[T2]** несколько профилей конфига + переключение.
|
||||
- **[MVP]** easy-режим (выбрал ноды — работает на дефолтах) поверх advanced (полные правила).
|
||||
|
||||
## 12. Дистрибуция / жизненный цикл
|
||||
- **[MVP]** сборка OpenWrt + ImmortalWrt/BananaWRT, 24.10(ipk)+25.x(apk), мультиарк, CI (gh-action-sdk).
|
||||
- **[MVP]** подписанный фид (usign/EC), one-line установка ключа+фида, self-update (bump PKG_RELEASE).
|
||||
- **[T1]** миграция UCI-схемы между версиями плагина; **[T1]** проверка совместимости версии xray.
|
||||
|
||||
## Открытые проектные развилки (нужен выбор — см. вопросы в чате)
|
||||
1. Форматы подписок в MVP (только share-links vs +Clash/sing-box).
|
||||
2. Инбаунды в MVP (tproxy-only vs +local socks/http vs +TUN).
|
||||
3. DNS по умолчанию (nftset-split vs FakeIP).
|
||||
4. Kill-switch по умолчанию (fail-closed vs fail-open).
|
||||
5. Гео-данные (бандлить geoip/geosite ~МБ vs скачивать по требованию) — важно на флеше.
|
||||
6. IPv6 в MVP (полный dual-stack сразу vs IPv4-first).
|
||||
7. Имя/бренд проекта + лицензия.
|
||||
</content>
|
||||
@@ -0,0 +1,58 @@
|
||||
# 06 — North Star («имба»): амбициозные фичи
|
||||
|
||||
**Принцип:** имба ≠ раздувание. Тяжёлая аналитика живёт в Go-демоне/оффлайн, **НЕ в пакетном
|
||||
пути** (требование «без лагов» — священно). Роутер остаётся быстрым; вау — в данных и UI, не в датаплейне.
|
||||
|
||||
## Статистика по потребителям (per-client) — как именно (реализуемо)
|
||||
Цель: «кто сколько съел» — по устройству, по проксе, по домену; live + история.
|
||||
- **Per-client байты (up/down), live+тотал:** nftables **dynamic counter set**
|
||||
(`set clients { type ipv4_addr; flags dynamic; counter; }` + правило `update @clients { ip saddr }`)
|
||||
→ на каждый source-IP авто-счётчик в ЯДРЕ; дельта раз в N сек = live-throughput.
|
||||
Имена устройств: IP→MAC→hostname из dnsmasq-leases.
|
||||
- **Per-client × per-proxy** (какой девайс через какую проксю сколько): счётчики по (saddr × mark),
|
||||
mark кодирует выбранную политику/outbound → реконструкция «клиент→прокся→байты».
|
||||
+ xray Stats API даёт per-outbound тотал (сверка).
|
||||
- **Per-client × per-domain/SNI:** парсинг xray access-log (source+domain) в Go-демоне → агрегация
|
||||
top-доменов на клиента. Тяжелее → T2, с семплингом, не в горячем пути.
|
||||
- **История:** RRD (collectd уже стоит) или встроенный ring; ретенция настраивается.
|
||||
Опц. интеграция **nlbwmon** (штатный per-host монитор) для готовых тоталов.
|
||||
- Скорость датаплейна не страдает: счётчики в ядре, агрегация в userspace-демоне.
|
||||
|
||||
## Дашборд мечты
|
||||
- **Live flow (Sankey):** client → rule → chain → egress → exit, анимация throughput.
|
||||
- **Leaderboard потребителей:** top talkers — кто сколько съел (устройство/домен/прокся).
|
||||
- **Node board:** live-пинг, alive, трафик, **SLA-uptime %**, история latency, «лучшая нода сейчас».
|
||||
- **Гео-карта** выходов и назначений; **connection inspector** (живые conn src→dst→outbound, kill).
|
||||
- **Explain/trace:** «почему этот трафик пошёл именно сюда».
|
||||
|
||||
## Контроль / автоматизация (вот это «имба»)
|
||||
- **Профили-сцены:** именованные снапшоты ВСЕГО конфига (Home / Travel / Whitelist-night / Gaming) —
|
||||
переключение в один тап (+авто по расписанию/WAN-mode).
|
||||
- **Per-device квоты** (лимиты трафика) → действие throttle/block/notify (родительский контроль).
|
||||
- **Расписания** (правила по времени: детские девайсы мимо прокси ночью; whitelist-часы авто).
|
||||
- **Авто-оптимизация маршрута:** непрерывный проб нод → авто-выбор быстрейшей per-destination.
|
||||
- **One-click «протестить всё»:** проб всех нод → ранжированная таблица → авто-сборка лучшей цепочки.
|
||||
- **Config-history + diff + rollback:** каждый apply версионируется, визуальный дифф, откат в тап.
|
||||
- **Rule-simulator:** вводишь src+dst → видишь решение/ноду/exit-IP ДО применения.
|
||||
- **Алерты (Telegram/webhook):** нода легла, квота, новый девайс, деградация цепочки, подписка истекает.
|
||||
- **Импорт из чего угодно:** подписка/буфер/файл/QR + конверт из passwall/homeproxy/clash-конфига.
|
||||
- **Fleet-режим (moonshot):** пуш конфига/подписок на НЕСКОЛЬКО роутеров из одного UI (у тебя их много).
|
||||
|
||||
## Сетевая глубина
|
||||
- Мульти-WAN + мульти-прокси балансировка; egress «быстрейший из набора» / round-robin WAN.
|
||||
- Chain-шаблоны (L1→L2→L3 из групп в один клик); полный IPv6-паритет; per-policy kill-switch;
|
||||
per-client DNS + ad-block + FakeIP.
|
||||
|
||||
## Интеграции
|
||||
- REST/gRPC API; опц. **Telegram-бот** управления; экспорт нод в QR/ссылку/подписку.
|
||||
|
||||
## Тиры (честно про реализм)
|
||||
- **Cheap-wins-that-feel-premium (T1):** per-client leaderboard · explain/trace · one-click test-all+rank ·
|
||||
профили-сцены · config-history+rollback · Telegram-алерты · node SLA. — дёшево в коде, вау-эффект огромный.
|
||||
- **T2:** per-domain stats · квоты · расписания · FakeIP · гео-карта · connection inspector · авто-оптимизация.
|
||||
- **Moonshots (T3+):** fleet-мультироутер · per-destination latency-routing · конвертеры чужих конфигов.
|
||||
|
||||
## Бюджет производительности (священно)
|
||||
Датаплейн — только ядро (nft tproxy + counters) + один xray. Аналитика/UI — Go-демон + LuCI-поллинг.
|
||||
Никаких per-connection userspace-хопов и парсеров в горячем пути. **Имба в данных, не в лаге.**
|
||||
</content>
|
||||
@@ -0,0 +1,136 @@
|
||||
# 07 — Архитектура и схемы (Mermaid)
|
||||
|
||||
Диаграммы рендерятся прямо в Gitea. Наглядная версия — `architecture.html`.
|
||||
|
||||
## 1. Системная архитектура (control-plane vs data-plane)
|
||||
Тяжёлого в пакетном пути нет: config едет вниз, телеметрия — вверх.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph PRES[Presentation]
|
||||
LUCI[LuCI app - JS]
|
||||
UBUS[ubus / rpcd]
|
||||
end
|
||||
subgraph CTRL[Control plane - xrayctl - Go]
|
||||
UCIM[UCI model /etc/config/xray]
|
||||
FETCH[sub fetch + parse - libXray]
|
||||
GEN[config generator - xray JSON + nft + ip-rules]
|
||||
REC[reconciler - atomic apply / rollback]
|
||||
TEL[telemetry - xray API / nft counters / logs]
|
||||
end
|
||||
subgraph DATA[Data plane - kernel + one xray]
|
||||
XRAY[xray-core - tproxy in / balancers / observatory]
|
||||
NFT[nftables fw4 - own table / tproxy / marks / counters]
|
||||
DNSM[dnsmasq + DoH / FakeIP]
|
||||
ROUTE[ip rule / route - policy routing / egress bind]
|
||||
PROCD[procd - supervise / respawn]
|
||||
end
|
||||
LUCI <--> UBUS
|
||||
UBUS <--> CTRL
|
||||
CTRL -->|render + atomic apply| DATA
|
||||
DATA -->|telemetry| TEL
|
||||
```
|
||||
|
||||
## 2. Модель данных — 8 объектов
|
||||
```mermaid
|
||||
flowchart LR
|
||||
SUB[Subscription - URL + HAPP headers] --> NODE[1. Node]
|
||||
MAN[manual - paste / \n / file] --> NODE
|
||||
NODE --> GRP[2. Group +balancer]
|
||||
GRP --> CH[3. Chain L1..Ln]
|
||||
NODE --> CH
|
||||
INB[5. Inbound - multi-LAN tproxy] --> RULE[6. Rule]
|
||||
RULE --> CH
|
||||
RULE --> GRP
|
||||
RULE --> NODE
|
||||
RULE --> EG[4. Egress]
|
||||
LIST[8. List / Ruleset] --> RULE
|
||||
LIST --> DNS[DNS]
|
||||
PROF[7. Profile - WAN-mode] -.override.-> RULE
|
||||
EG --> OUT[iface / tunnel / chain / direct / block]
|
||||
```
|
||||
|
||||
## 3. Путь трафика
|
||||
```mermaid
|
||||
flowchart LR
|
||||
C[LAN client] -->|nft tproxy - mark to :12345| IN[xray inbound - dokodemo :12345 - sniff SNI/Host]
|
||||
IN --> R{rule match - src / dst / list / geo}
|
||||
R --> T[target - Chain L1-L2-L3 / Group / Node]
|
||||
T -->|mark 0x11 to table| E[egress - awgOut / eth / wifi]
|
||||
E --> NET[Internet - exit IP]
|
||||
```
|
||||
Loop-guard: `sockopt.mark` на egress xray + bypass RFC1918/сервера; flow-offload остаётся ON для direct.
|
||||
|
||||
## 4. DNS (настраиваемый, без утечек)
|
||||
```mermaid
|
||||
flowchart LR
|
||||
C[client :53] -->|hijack DNAT| DM[dnsmasq - noresolv / cache]
|
||||
DM --> DR{DNS router - domain / list / geo / client}
|
||||
DR --> R1[ISP / local]
|
||||
DR --> R2[DoH - detour via outbound]
|
||||
DR --> R3[FakeIP 198.18.x]
|
||||
DR --> SET[(domain to nftset - для роутинга)]
|
||||
```
|
||||
|
||||
## 5. Жизненный цикл ноды и идентичность
|
||||
```mermaid
|
||||
flowchart LR
|
||||
S[subscription - HAPP HWID fixed/auto] --> P[parse - libXray]
|
||||
P --> FP[fingerprint - hash addr/port/id/net/sec/sni]
|
||||
FP --> RC{reconcile}
|
||||
RC -->|new| ADD[add]
|
||||
RC -->|present| KEEP[keep]
|
||||
RC -->|missing| ST[stale N refreshes then remove]
|
||||
ADD --> SEL[selection - Group dynamic / pinned Node]
|
||||
KEEP --> SEL
|
||||
SEL -->|pinned & missing| FB[fallback - group / direct / block]
|
||||
```
|
||||
Обновление подписки: **интервал на каждую подписку** + вручную + при бутe; после апдейта → этот reconcile.
|
||||
|
||||
## 6. Надёжность — state machine («железно»)
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Edit
|
||||
Edit --> Build
|
||||
Build --> Validate: xray -test / nft -c
|
||||
Validate --> KeepOld: fail
|
||||
Validate --> Stage: ok
|
||||
Stage --> Apply: atomic nft -f + swap
|
||||
Apply --> ConfirmWindow
|
||||
ConfirmWindow --> Committed: confirmed
|
||||
ConfirmWindow --> Rollback: timeout N s
|
||||
Rollback --> LastGood
|
||||
KeepOld --> [*]
|
||||
Committed --> [*]
|
||||
LastGood --> [*]
|
||||
```
|
||||
Свои метки/таблицы (fw4 не трогаем) · идемпотентный reconcile · hotplug-персист · mgmt-bypass всегда.
|
||||
|
||||
## 7. Per-consumer статистика (ядро считает, демон агрегирует)
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph K[Kernel - in-path, free]
|
||||
C1[nft dynamic counter set - per saddr]
|
||||
C2[saddr x mark counters - client x proxy]
|
||||
end
|
||||
subgraph U[Userspace - aggregate]
|
||||
X[xray Stats API - per-outbound]
|
||||
L[access-log parse - T2 per-domain]
|
||||
RRD[RRD / nlbwmon - history]
|
||||
end
|
||||
C1 --> AGG[xrayctl aggregator]
|
||||
C2 --> AGG
|
||||
X --> AGG
|
||||
L --> AGG
|
||||
RRD --> AGG
|
||||
AGG -->|ubus| DASH[Dashboard - leaderboard / per-proxy / Sankey / conn-inspector]
|
||||
```
|
||||
|
||||
## 8. Роадмап
|
||||
| Тир | Содержание |
|
||||
|-----|-----------|
|
||||
| **MVP** | подписки+manual импорт · TPROXY multi-LAN · balancer+observatory · rules(src/dst/domain-list/ip-list) · DNS(DoH+hijack+split,±FakeIP) · atomic apply+rollback+kill-switch · LuCI(nodes/subs/status) · build OpenWrt+ImmortalWrt+feed |
|
||||
| **T1** | multi-hop chains · per-client policy+egress-select · explain/trace · consumer leaderboard+SLA · profiles/scenes · config-history+rollback · one-click test-all · Telegram-алерты |
|
||||
| **T2** | per-domain stats · connection inspector · device quotas · schedules · FakeIP · geo-map · WAN-mode profiles · awg-wrap · auto route-optimization · QR |
|
||||
| **T3** | fleet (много роутеров) · per-destination latency-routing · config-конвертеры · sing-box адаптер · REST/gRPC API · TG-бот |
|
||||
</content>
|
||||
@@ -0,0 +1,36 @@
|
||||
# shater
|
||||
|
||||
**OpenWrt-плагин управления XRAY** — как passwall2, только чище, быстрее и без лагов.
|
||||
Переносимый: подписки → ноды → цепочки → правила → выбор egress, с прозрачным роутингом,
|
||||
железной надёжностью, per-consumer статистикой и удобным UI.
|
||||
|
||||
> Статус: **дизайн-фаза (до кода).** Здесь — продуманные фичи, модель и схемы.
|
||||
|
||||
## Ключевые решения
|
||||
- Движок: **только xray-core** — НЕ форкаем; `xrayctl` = отдельный control-plane демон (Go). sing-box — потом адаптером.
|
||||
- Генератор: **Go `xrayctl`** (парсинг share-links через libXray, валидация `xray -test`).
|
||||
- MVP: **надёжный одиночный transparent-proxy** (TPROXY, мульти-LAN); цепочки/per-client — фаза 2.
|
||||
- Сборка: **OpenWrt + ImmortalWrt/BananaWRT**, 24.10(ipk)+25.x(apk), мультиарк, CI + подписанный фид.
|
||||
- Тест-стенд: Docker/QEMU (QEMU для netfilter/tproxy, WSL2 для сборки).
|
||||
|
||||
## Документы
|
||||
| # | Файл | О чём |
|
||||
|---|------|-------|
|
||||
| 00 | [00-summary-and-plan.md](00-summary-and-plan.md) | Сводка, locked-решения, план по фазам, риски |
|
||||
| 01 | [01-mini_router-audit.md](01-mini_router-audit.md) | Аудит боевого роутера (эталон того, что автоматизируем) |
|
||||
| 02 | [02-xray-plugin-design.md](02-xray-plugin-design.md) | Дизайн, структура пакета, фазы |
|
||||
| 03 | [03-features-and-model.md](03-features-and-model.md) | Модель (8 объектов), правила, egress, идентичность нод |
|
||||
| 04 | [04-ops-reliability-build-stats.md](04-ops-reliability-build-stats.md) | Надёжность («железно»), сборка, статистика |
|
||||
| 05 | [05-feature-catalog.md](05-feature-catalog.md) | Полный каталог фич (12 доменов, теги MVP/T1/T2/T3) |
|
||||
| 06 | [06-north-star-imba.md](06-north-star-imba.md) | Амбициозные «мечты»: дашборд, per-client, автоматизация |
|
||||
| 07 | [07-architecture.md](07-architecture.md) | **Все схемы (Mermaid): архитектура, flow, lifecycle, SM** |
|
||||
|
||||
Визуальная версия схем (артефакт): см. `architecture.html` (тот же контент, наглядно).
|
||||
|
||||
## Модель в одну строку
|
||||
`Node → Group(+balancer) → Chain(L1..Ln) → Egress` · `Rule(src/dst/list/geo → target+egress)` ·
|
||||
`Inbound(multi-LAN tproxy)` · `Profile(WAN-mode)` · `List(domain/ip, auto-update)`.
|
||||
|
||||
## Следующий шаг
|
||||
Контракт (0c): UCI-схема `/etc/config/xray` (все 8 объектов) + интерфейс `xrayctl` (CLI + ubus).
|
||||
</content>
|
||||
@@ -0,0 +1,449 @@
|
||||
<title>shater — Architecture & Vision</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#0a0e14; --panel:#111a24; --panel2:#0d151d; --line:#1e2b3a;
|
||||
--ink:#e8eef5; --muted:#94a4b8; --faint:#5c6e83;
|
||||
--ctrl:#38bdf8; --data:#fb923c; --dns:#c084fc; --eg:#34d399;
|
||||
--ok:#34d399; --warn:#fbbf24; --crit:#f87171;
|
||||
--grid:rgba(56,189,248,.06);
|
||||
--radius:9px; --maxw:1120px;
|
||||
--mono:ui-monospace,"Cascadia Code","SF Mono",Menlo,Consolas,monospace;
|
||||
--sans:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
}
|
||||
@media (prefers-color-scheme: light){
|
||||
:root{
|
||||
--bg:#eef3f9; --panel:#ffffff; --panel2:#f3f7fc; --line:#d3deea;
|
||||
--ink:#0f1b2a; --muted:#4a5d72; --faint:#8598ac;
|
||||
--ctrl:#0284c7; --data:#c2620c; --dns:#8b2fc9; --eg:#0f9d63;
|
||||
--ok:#0f9d63; --warn:#b6820a; --crit:#d63b3b; --grid:rgba(2,132,199,.07);
|
||||
}
|
||||
}
|
||||
:root[data-theme="dark"]{
|
||||
--bg:#0a0e14; --panel:#111a24; --panel2:#0d151d; --line:#1e2b3a;
|
||||
--ink:#e8eef5; --muted:#94a4b8; --faint:#5c6e83;
|
||||
--ctrl:#38bdf8; --data:#fb923c; --dns:#c084fc; --eg:#34d399;
|
||||
--ok:#34d399; --warn:#fbbf24; --crit:#f87171; --grid:rgba(56,189,248,.06);
|
||||
}
|
||||
:root[data-theme="light"]{
|
||||
--bg:#eef3f9; --panel:#ffffff; --panel2:#f3f7fc; --line:#d3deea;
|
||||
--ink:#0f1b2a; --muted:#4a5d72; --faint:#8598ac;
|
||||
--ctrl:#0284c7; --data:#c2620c; --dns:#8b2fc9; --eg:#0f9d63;
|
||||
--ok:#0f9d63; --warn:#b6820a; --crit:#d63b3b; --grid:rgba(2,132,199,.07);
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.55;
|
||||
margin:0;-webkit-font-smoothing:antialiased;font-size:16px}
|
||||
a{color:var(--ctrl);text-decoration:none}
|
||||
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 22px}
|
||||
code,.m{font-family:var(--mono)}
|
||||
h1,h2,h3{text-wrap:balance;line-height:1.15;margin:0}
|
||||
|
||||
/* top bar */
|
||||
.top{position:sticky;top:0;z-index:20;background:color-mix(in srgb,var(--bg) 88%,transparent);
|
||||
backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
|
||||
.top .wrap{display:flex;align-items:center;gap:18px;height:54px}
|
||||
.brand{font-family:var(--mono);font-weight:700;letter-spacing:.5px;color:var(--ink)}
|
||||
.brand b{color:var(--ctrl)}
|
||||
.nav{display:flex;gap:4px;flex-wrap:wrap;margin-left:auto}
|
||||
.nav a{font-family:var(--mono);font-size:12px;color:var(--muted);padding:5px 9px;border-radius:6px}
|
||||
.nav a:hover{color:var(--ink);background:var(--panel)}
|
||||
.tbtn{font-family:var(--mono);font-size:12px;color:var(--muted);background:var(--panel);
|
||||
border:1px solid var(--line);border-radius:6px;padding:5px 9px;cursor:pointer}
|
||||
.tbtn:hover{color:var(--ink)}
|
||||
|
||||
/* hero */
|
||||
.hero{padding:64px 0 30px}
|
||||
.eyebrow{font-family:var(--mono);font-size:12px;letter-spacing:2.5px;text-transform:uppercase;color:var(--ctrl)}
|
||||
.hero h1{font-size:clamp(34px,6vw,58px);font-weight:800;letter-spacing:-1px;margin:14px 0 0}
|
||||
.hero h1 .g{color:var(--faint)}
|
||||
.lede{color:var(--muted);font-size:clamp(16px,2.4vw,20px);max-width:60ch;margin:18px 0 0}
|
||||
.kpis{display:flex;gap:26px;flex-wrap:wrap;margin-top:26px}
|
||||
.kpi{font-family:var(--mono)}
|
||||
.kpi b{display:block;font-size:22px;color:var(--ink)}
|
||||
.kpi span{font-size:12px;color:var(--faint);text-transform:uppercase;letter-spacing:1px}
|
||||
|
||||
/* section */
|
||||
section{padding:42px 0;border-top:1px solid var(--line)}
|
||||
.sec-h{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:6px}
|
||||
.sec-n{font-family:var(--mono);color:var(--faint);font-size:13px}
|
||||
section h2{font-size:clamp(22px,3.4vw,30px);font-weight:750;letter-spacing:-.4px}
|
||||
.cap{color:var(--muted);max-width:70ch;margin:8px 0 22px}
|
||||
|
||||
/* blueprint canvas */
|
||||
.canvas{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel2);
|
||||
background-image:linear-gradient(var(--grid) 1px,transparent 1px),linear-gradient(90deg,var(--grid) 1px,transparent 1px);
|
||||
background-size:26px 26px;padding:22px;overflow-x:auto}
|
||||
.scroll{overflow-x:auto}
|
||||
|
||||
/* generic node card */
|
||||
.node{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:11px 13px;min-width:0}
|
||||
.node .t{font-family:var(--mono);font-weight:600;font-size:13px;color:var(--ink)}
|
||||
.node .d{font-size:12.5px;color:var(--muted);margin-top:3px}
|
||||
.node.ctrl{border-top:3px solid var(--ctrl)} .node.data{border-top:3px solid var(--data)}
|
||||
.node.dns{border-top:3px solid var(--dns)} .node.eg{border-top:3px solid var(--eg)}
|
||||
.tag{font-family:var(--mono);font-size:11px;color:var(--muted);border:1px solid var(--line);
|
||||
border-radius:5px;padding:1px 6px;background:var(--panel2);white-space:nowrap}
|
||||
|
||||
/* flow (horizontal) */
|
||||
.flow{display:flex;align-items:stretch;gap:0;min-width:max-content}
|
||||
.flow .node{flex:0 0 auto;max-width:220px;align-self:center}
|
||||
.arw{align-self:center;color:var(--faint);font-family:var(--mono);padding:0 12px;font-size:20px;flex:0 0 auto}
|
||||
.arw.data{color:var(--data)} .arw.dns{color:var(--dns)}
|
||||
|
||||
/* lanes */
|
||||
.lanes{display:grid;gap:16px}
|
||||
.lane{border:1px dashed var(--line);border-radius:8px;padding:14px}
|
||||
.lane-h{font-family:var(--mono);font-size:11px;letter-spacing:2px;text-transform:uppercase;margin-bottom:11px}
|
||||
.lane.ctrl .lane-h{color:var(--ctrl)} .lane.data .lane-h{color:var(--data)}
|
||||
.lane.dns .lane-h{color:var(--dns)} .lane.pres .lane-h{color:var(--eg)}
|
||||
.row{display:flex;gap:12px;flex-wrap:wrap}
|
||||
.row>.node{flex:1 1 150px}
|
||||
|
||||
/* legend */
|
||||
.legend{display:flex;gap:18px;flex-wrap:wrap;margin-top:20px;font-family:var(--mono);font-size:12px}
|
||||
.legend span{display:inline-flex;align-items:center;gap:7px;color:var(--muted)}
|
||||
.dot{width:11px;height:11px;border-radius:3px;display:inline-block}
|
||||
|
||||
/* tier chips */
|
||||
.chip{font-family:var(--mono);font-size:11px;border-radius:5px;padding:1px 7px;border:1px solid;white-space:nowrap}
|
||||
.mvp{color:var(--ctrl);border-color:color-mix(in srgb,var(--ctrl) 45%,var(--line))}
|
||||
.t1{color:var(--eg);border-color:color-mix(in srgb,var(--eg) 45%,var(--line))}
|
||||
.t2{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 45%,var(--line))}
|
||||
.t3{color:var(--dns);border-color:color-mix(in srgb,var(--dns) 45%,var(--line))}
|
||||
|
||||
/* data-model grid */
|
||||
.grid3{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px}
|
||||
.obj{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:13px;border-left:3px solid var(--ctrl)}
|
||||
.obj h3{font-family:var(--mono);font-size:14px;display:flex;align-items:center;gap:8px}
|
||||
.obj .num{color:var(--faint);font-size:12px}
|
||||
.obj ul{margin:9px 0 0;padding-left:16px;color:var(--muted);font-size:12.5px}
|
||||
.obj ul li{margin:2px 0}
|
||||
.obj .rel{margin-top:9px;font-family:var(--mono);font-size:11px;color:var(--faint)}
|
||||
|
||||
/* board */
|
||||
.board{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}
|
||||
.col{background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:13px}
|
||||
.col-h{font-family:var(--mono);font-size:12px;letter-spacing:1px;display:flex;justify-content:space-between;margin-bottom:10px}
|
||||
.col ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
|
||||
.col li{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:7px 9px;font-size:12.5px;color:var(--ink)}
|
||||
.col li small{color:var(--muted)}
|
||||
|
||||
/* state machine */
|
||||
.sm{display:flex;flex-wrap:wrap;gap:0;align-items:center;min-width:max-content}
|
||||
.st{background:var(--panel);border:1px solid var(--line);border-radius:20px;padding:8px 14px;font-family:var(--mono);font-size:12.5px}
|
||||
.st.ok{border-color:color-mix(in srgb,var(--ok) 55%,var(--line));color:var(--ok)}
|
||||
.st.bad{border-color:color-mix(in srgb,var(--crit) 55%,var(--line));color:var(--crit)}
|
||||
.note{font-size:12px;color:var(--faint);font-family:var(--mono)}
|
||||
|
||||
.callout{border-left:3px solid var(--ctrl);background:var(--panel2);padding:12px 15px;border-radius:0 8px 8px 0;
|
||||
color:var(--muted);font-size:13.5px;margin-top:18px}
|
||||
.callout b{color:var(--ink)}
|
||||
.mark{color:var(--data);font-family:var(--mono)}
|
||||
|
||||
footer{border-top:1px solid var(--line);padding:34px 0 60px;color:var(--faint);font-family:var(--mono);font-size:12.5px}
|
||||
.fade{opacity:0;transform:translateY(10px);animation:rise .6s ease forwards}
|
||||
@keyframes rise{to{opacity:1;transform:none}}
|
||||
@media (prefers-reduced-motion:reduce){.fade{animation:none;opacity:1;transform:none}}
|
||||
:focus-visible{outline:2px solid var(--ctrl);outline-offset:2px}
|
||||
@media(max-width:640px){.nav{display:none}}
|
||||
</style>
|
||||
|
||||
<div class="top"><div class="wrap">
|
||||
<span class="brand"><b>shater</b> // xray control-plane</span>
|
||||
<nav class="nav">
|
||||
<a href="#arch">architecture</a><a href="#model">model</a><a href="#flow">traffic</a>
|
||||
<a href="#dns">dns</a><a href="#life">nodes</a><a href="#rely">reliability</a>
|
||||
<a href="#stats">stats</a><a href="#road">roadmap</a>
|
||||
</nav>
|
||||
<button class="tbtn" id="tt" aria-label="Toggle theme">◐ theme</button>
|
||||
</div></div>
|
||||
|
||||
<header class="hero"><div class="wrap fade">
|
||||
<div class="eyebrow">OpenWrt · xray-core · TPROXY · design blueprint</div>
|
||||
<h1>One transparent proxy engine,<br><span class="g">fully under your control.</span></h1>
|
||||
<p class="lede">A portable OpenWrt plugin that turns subscriptions, nodes, multi-hop chains and
|
||||
per-device rules into one declarative model — with rock-solid routing, per-consumer analytics,
|
||||
and a dashboard. Passwall-class capability, without the lag.</p>
|
||||
<div class="kpis">
|
||||
<div class="kpi"><b>8</b><span>model objects</span></div>
|
||||
<div class="kpi"><b>1</b><span>xray process</span></div>
|
||||
<div class="kpi"><b>0</b><span>userspace hops in path</span></div>
|
||||
<div class="kpi"><b>2</b><span>build targets (OpenWrt+ImmortalWrt)</span></div>
|
||||
</div>
|
||||
<div class="legend">
|
||||
<span><i class="dot" style="background:var(--ctrl)"></i>control plane</span>
|
||||
<span><i class="dot" style="background:var(--data)"></i>data plane</span>
|
||||
<span><i class="dot" style="background:var(--dns)"></i>dns</span>
|
||||
<span><i class="dot" style="background:var(--eg)"></i>egress / exit</span>
|
||||
<span><span class="chip mvp">MVP</span><span class="chip t1">T1</span><span class="chip t2">T2</span><span class="chip t3">T3</span></span>
|
||||
</div>
|
||||
</div></header>
|
||||
|
||||
<main class="wrap">
|
||||
|
||||
<!-- ARCHITECTURE -->
|
||||
<section id="arch">
|
||||
<div class="sec-h"><span class="sec-n">01</span><h2>System architecture</h2></div>
|
||||
<p class="cap">Two planes, cleanly split. The <b>control plane</b> (a Go daemon <code>xrayctl</code> +
|
||||
LuCI UI over ubus) renders configuration and reconciles state. The <b>data plane</b> is a single
|
||||
<code>xray</code> process plus kernel machinery — nothing heavy sits in the packet path.</p>
|
||||
<div class="canvas"><div class="lanes" style="min-width:640px">
|
||||
<div class="lane pres"><div class="lane-h">Presentation</div>
|
||||
<div class="row">
|
||||
<div class="node eg"><div class="t">LuCI app (JS)</div><div class="d">nodes · rules · dashboard · explain/trace</div></div>
|
||||
<div class="node eg"><div class="t">ubus / rpcd</div><div class="d">ucode backend object <code>xray.*</code> + ACL</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lane ctrl"><div class="lane-h">Control plane — xrayctl (Go)</div>
|
||||
<div class="row">
|
||||
<div class="node ctrl"><div class="t">UCI model</div><div class="d">/etc/config/xray → 8 objects</div></div>
|
||||
<div class="node ctrl"><div class="t">sub fetch + parse</div><div class="d">libXray share-links · HAPP headers</div></div>
|
||||
<div class="node ctrl"><div class="t">config generator</div><div class="d">build xray JSON + nft + ip-rules</div></div>
|
||||
<div class="node ctrl"><div class="t">reconciler</div><div class="d">atomic apply · validate · rollback</div></div>
|
||||
<div class="node ctrl"><div class="t">telemetry</div><div class="d">xray API · nft counters · logs</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lane data"><div class="lane-h">Data plane — kernel + one xray</div>
|
||||
<div class="row">
|
||||
<div class="node data"><div class="t">xray-core</div><div class="d">tproxy in · balancers · observatory</div></div>
|
||||
<div class="node data"><div class="t">nftables (fw4)</div><div class="d">own table · tproxy · marks · counters</div></div>
|
||||
<div class="node dns"><div class="t">dnsmasq + DoH/FakeIP</div><div class="d">hijack · split · nftset</div></div>
|
||||
<div class="node data"><div class="t">ip rule / route</div><div class="d">policy routing · egress bind</div></div>
|
||||
<div class="node data"><div class="t">procd</div><div class="d">supervise · reload · respawn</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="callout"><b>Config flows down, telemetry flows up.</b> <code>xrayctl</code> owns a dedicated
|
||||
nft table, a reserved fwmark range and its own routing tables — it never edits fw4's table, so a
|
||||
firewall reload can't break it, and every apply is a single atomic transaction.</div>
|
||||
</section>
|
||||
|
||||
<!-- DATA MODEL -->
|
||||
<section id="model">
|
||||
<div class="sec-h"><span class="sec-n">02</span><h2>Data model — 8 objects</h2></div>
|
||||
<p class="cap">Everything the plugin does is expressed by composing these. No feature sprawl —
|
||||
just relationships between eight primitives, edited in UCI, rendered to xray + nftables.</p>
|
||||
<div class="canvas"><div class="grid3">
|
||||
<div class="obj"><h3><span class="num">1</span> Node</h3>
|
||||
<ul><li>vless/vmess/trojan/ss/socks/http</li><li>reality · xhttp · vision · ws · grpc</li><li>stable <b>fingerprint</b> identity</li></ul>
|
||||
<div class="rel">from → Subscription | manual</div></div>
|
||||
<div class="obj"><h3><span class="num">2</span> Group</h3>
|
||||
<ul><li>set of Nodes (a subscription)</li><li>balancer: leastPing/random/failover</li><li>observatory health</li></ul>
|
||||
<div class="rel">contains → Node[]</div></div>
|
||||
<div class="obj"><h3><span class="num">3</span> Chain</h3>
|
||||
<ul><li>ordered hops L1→L2→L3</li><li>hop = Group or fixed Node</li><li>inverted proxySettings</li></ul>
|
||||
<div class="rel">hops → Group | Node</div></div>
|
||||
<div class="obj" style="border-left-color:var(--eg)"><h3><span class="num">4</span> Egress</h3>
|
||||
<ul><li>interface (eth/wifi/sim)</li><li>tunnel (wg/awg)</li><li>chain/group · direct · block</li></ul>
|
||||
<div class="rel">bound by → Rule | Chain</div></div>
|
||||
<div class="obj"><h3><span class="num">5</span> Inbound</h3>
|
||||
<ul><li>TPROXY per LAN (multi-LAN)</li><li>tcp + udp · explicit subnets</li><li>+ local socks/http (T1)</li></ul>
|
||||
<div class="rel">feeds → Rule engine</div></div>
|
||||
<div class="obj" style="border-left-color:var(--data)"><h3><span class="num">6</span> Rule</h3>
|
||||
<ul><li>src: cidr/host/mac/iface</li><li>dst: domain/list/ip/list/geo/port</li><li>→ target + egress + kill-mode</li></ul>
|
||||
<div class="rel">refs → List · Chain · Group · Node · Egress</div></div>
|
||||
<div class="obj" style="border-left-color:var(--dns)"><h3><span class="num">7</span> Profile</h3>
|
||||
<ul><li>conditional overrides</li><li>trigger: active WAN / probe / time</li><li>scenes: Home/Travel/Night</li></ul>
|
||||
<div class="rel">overrides → Rule · Egress · DNS</div></div>
|
||||
<div class="obj" style="border-left-color:var(--warn)"><h3><span class="num">8</span> List / Ruleset</h3>
|
||||
<ul><li>domain-list / ip-cidr-list</li><li>inline / file / URL (auto-update)</li><li>plain · clash-provider · geosite</li></ul>
|
||||
<div class="rel">used by → Rule · DNS</div></div>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- TRAFFIC FLOW -->
|
||||
<section id="flow">
|
||||
<div class="sec-h"><span class="sec-n">03</span><h2>Traffic flow</h2></div>
|
||||
<p class="cap">A LAN packet's journey, and exactly where the decisions happen. Sniffing recovers
|
||||
SNI/Host so domain rules work on transparent traffic. Every hop is annotated with its mark/table.</p>
|
||||
<div class="canvas scroll"><div class="flow">
|
||||
<div class="node"><div class="t">LAN client</div><div class="d">192.168.11.14</div></div>
|
||||
<span class="arw data">→</span>
|
||||
<div class="node data"><div class="t">nft tproxy</div><div class="d">iif br-lan · tcp+udp<br><span class="tag">mark 0xT → :12345</span></div></div>
|
||||
<span class="arw data">→</span>
|
||||
<div class="node data"><div class="t">xray inbound</div><div class="d">dokodemo :12345<br>sniff SNI/Host/QUIC</div></div>
|
||||
<span class="arw">→</span>
|
||||
<div class="node data"><div class="t">rule match</div><div class="d">src/dst/list/geo<br>first-match</div></div>
|
||||
<span class="arw">→</span>
|
||||
<div class="node ctrl"><div class="t">target</div><div class="d">Chain L1→L2→L3<br>| Group | Node</div></div>
|
||||
<span class="arw eg" style="color:var(--eg)">→</span>
|
||||
<div class="node eg"><div class="t">egress</div><div class="d">awgOut / eth / wifi<br><span class="tag">mark 0x11 → table</span></div></div>
|
||||
<span class="arw eg" style="color:var(--eg)">→</span>
|
||||
<div class="node eg"><div class="t">Internet</div><div class="d">exit IP</div></div>
|
||||
</div></div>
|
||||
<div class="callout"><b>Loop guard & fast-path.</b> xray's own egress carries <span class="mark">sockopt.mark</span>
|
||||
so the firewall lets it escape re-interception; RFC1918 / the server IP are bypassed; flow-offload stays
|
||||
<b>on</b> for direct traffic (kernel fast-path) and off only for proxied flows. That's why it doesn't lag.</div>
|
||||
</section>
|
||||
|
||||
<!-- DNS -->
|
||||
<section id="dns">
|
||||
<div class="sec-h"><span class="sec-n">04</span><h2>DNS — configurable & leak-proof</h2></div>
|
||||
<p class="cap">Clients can't bypass it; resolution matches routing. Named resolvers, per-domain and
|
||||
per-client routing, and a switchable transparent (nftset) ↔ zero-leak (FakeIP) strategy.</p>
|
||||
<div class="canvas scroll"><div class="flow">
|
||||
<div class="node"><div class="t">client :53</div></div>
|
||||
<span class="arw dns">→</span>
|
||||
<div class="node dns"><div class="t">hijack (DNAT)</div><div class="d">force to router<br>block DoT/DoH bypass</div></div>
|
||||
<span class="arw dns">→</span>
|
||||
<div class="node dns"><div class="t">dnsmasq</div><div class="d">noresolv · cache<br>→ 127.0.0.1</div></div>
|
||||
<span class="arw dns">→</span>
|
||||
<div class="node ctrl"><div class="t">DNS router</div><div class="d">match domain/list/geo/client</div></div>
|
||||
<span class="arw dns">→</span>
|
||||
<div class="node dns"><div class="t">resolvers</div><div class="d">ISP/local · DoH · FakeIP<br>detour via outbound</div></div>
|
||||
</div>
|
||||
<div class="row" style="margin-top:14px">
|
||||
<div class="node"><div class="t">split</div><div class="d">direct domains → ISP · proxied → proxy-side resolver (resolved == routed)</div></div>
|
||||
<div class="node data"><div class="t">domain-list → nftset</div><div class="d">feeds routing sets; IPv6 mirrored</div></div>
|
||||
<div class="node dns"><div class="t">FakeIP <span class="chip t1">T1</span></div><div class="d">198.18.x · fakeip-domain-list / real-ip-list</div></div>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- NODE LIFECYCLE -->
|
||||
<section id="life">
|
||||
<div class="sec-h"><span class="sec-n">05</span><h2>Node lifecycle & identity</h2></div>
|
||||
<p class="cap">A subscription refresh must never silently break a pinned rule. Nodes carry a stable
|
||||
fingerprint; reconcile diffs by it; vanished pins fall back by policy.</p>
|
||||
<div class="canvas scroll"><div class="flow">
|
||||
<div class="node ctrl"><div class="t">subscription</div><div class="d">URL + HAPP headers<br>HWID fixed/auto</div></div>
|
||||
<span class="arw">→</span>
|
||||
<div class="node ctrl"><div class="t">parse</div><div class="d">libXray share-links</div></div>
|
||||
<span class="arw">→</span>
|
||||
<div class="node ctrl"><div class="t">fingerprint</div><div class="d">hash(addr,port,id,net,sec,sni…)</div></div>
|
||||
<span class="arw">→</span>
|
||||
<div class="node ctrl"><div class="t">reconcile</div><div class="d">new → add<br>present → keep<br>missing → stale</div></div>
|
||||
<span class="arw">→</span>
|
||||
<div class="node"><div class="t">selection</div><div class="d">Group (dynamic)<br>or pinned Node</div></div>
|
||||
</div>
|
||||
<div class="row" style="margin-top:14px">
|
||||
<div class="node" style="border-top:3px solid var(--warn)"><div class="t">stale window</div><div class="d">kept N refreshes → removed; never silent delete</div></div>
|
||||
<div class="node data"><div class="t">pinned & missing → fallback</div><div class="d">group balancer / direct / block (your choice)</div></div>
|
||||
<div class="node eg"><div class="t">manual nodes</div><div class="d">stable by own id · never auto-removed · paste \n / file / link</div></div>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- RELIABILITY -->
|
||||
<section id="rely">
|
||||
<div class="sec-h"><span class="sec-n">06</span><h2>Reliability — the “железно” contract</h2></div>
|
||||
<p class="cap">The top requirement: routing tables never break, you never lock yourself out.
|
||||
Every change is validated, applied atomically, and auto-reverts if it can't be confirmed.</p>
|
||||
<div class="canvas scroll"><div class="sm">
|
||||
<span class="st">edit UCI</span><span class="arw">→</span>
|
||||
<span class="st">build state</span><span class="arw">→</span>
|
||||
<span class="st">validate<br>xray -test · nft -c</span><span class="arw">→</span>
|
||||
<span class="st">stage</span><span class="arw">→</span>
|
||||
<span class="st">atomic apply<br>nft -f · swap</span><span class="arw">→</span>
|
||||
<span class="st">commit-confirm<br>window</span><span class="arw">→</span>
|
||||
<span class="st ok">committed ✓</span>
|
||||
</div>
|
||||
<div class="sm" style="margin-top:14px">
|
||||
<span class="note">validate fails →</span><span class="st bad">abort · keep old</span>
|
||||
<span class="arw" style="padding-left:26px">•</span>
|
||||
<span class="note">no confirm in N s →</span><span class="st bad">auto-rollback → last-good</span>
|
||||
</div></div>
|
||||
<div class="row" style="margin-top:16px">
|
||||
<div class="node ctrl"><div class="t">own resources</div><div class="d">reserved marks · own table · own routing tables — fw4 untouched</div></div>
|
||||
<div class="node ctrl"><div class="t">idempotent reconcile</div><div class="d">desired vs actual · no duplicate pile-up</div></div>
|
||||
<div class="node ctrl"><div class="t">hotplug persist</div><div class="d">re-apply ip-rules on ifup/ifdown & boot</div></div>
|
||||
<div class="node eg"><div class="t">mgmt bypass</div><div class="d">SSH/LuCI/LAN always reachable · kill-switch per policy (default fail-closed)</div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- STATS -->
|
||||
<section id="stats">
|
||||
<div class="sec-h"><span class="sec-n">07</span><h2>Per-consumer analytics</h2></div>
|
||||
<p class="cap">Who ate how much — per device, per proxy, per domain, live and historical. The kernel
|
||||
counts; the daemon aggregates. Zero cost in the packet path.</p>
|
||||
<div class="canvas scroll"><div class="lanes" style="min-width:640px">
|
||||
<div class="lane data"><div class="lane-h">Kernel (in-path, free)</div>
|
||||
<div class="row">
|
||||
<div class="node data"><div class="t">nft dynamic counter set</div><div class="d">per source-IP up/down bytes</div></div>
|
||||
<div class="node data"><div class="t">(saddr × mark) counters</div><div class="d">client × proxy attribution</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lane ctrl"><div class="lane-h">Userspace (aggregate)</div>
|
||||
<div class="row">
|
||||
<div class="node ctrl"><div class="t">xray Stats API</div><div class="d">per-outbound totals (verify)</div></div>
|
||||
<div class="node ctrl"><div class="t">access-log parse <span class="chip t2">T2</span></div><div class="d">per-client × domain/SNI</div></div>
|
||||
<div class="node ctrl"><div class="t">RRD / nlbwmon</div><div class="d">history & retention</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lane pres"><div class="lane-h">Dashboard</div>
|
||||
<div class="row">
|
||||
<div class="node eg"><div class="t">consumer leaderboard</div><div class="d">top talkers · device names</div></div>
|
||||
<div class="node eg"><div class="t">per-proxy bytes</div><div class="d">which node ate what</div></div>
|
||||
<div class="node eg"><div class="t">live Sankey flow</div><div class="d">client→rule→chain→egress→exit</div></div>
|
||||
<div class="node eg"><div class="t">connection inspector <span class="chip t2">T2</span></div><div class="d">live conns · kill</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- ROADMAP -->
|
||||
<section id="road">
|
||||
<div class="sec-h"><span class="sec-n">08</span><h2>Roadmap — we build all of it</h2></div>
|
||||
<p class="cap">Ship a rock-solid core first, then layer the dream features. Each tier is shippable
|
||||
and tested in QEMU before the next.</p>
|
||||
<div class="canvas"><div class="board">
|
||||
<div class="col"><div class="col-h"><span>MVP</span><span class="chip mvp">core</span></div>
|
||||
<ul>
|
||||
<li>Subscriptions + manual import <small>paste/\n/file</small></li>
|
||||
<li>TPROXY multi-LAN · tcp+udp</li>
|
||||
<li>Balancer + observatory prune</li>
|
||||
<li>Rules: src/dst/domain-list/ip-list</li>
|
||||
<li>DNS: DoH + hijack + split (+FakeIP switch)</li>
|
||||
<li>Atomic apply · rollback · kill-switch</li>
|
||||
<li>LuCI: nodes(ping)/subs/status</li>
|
||||
<li>Build OpenWrt+ImmortalWrt · feed</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col"><div class="col-h"><span>T1</span><span class="chip t1">differentiators</span></div>
|
||||
<ul>
|
||||
<li>Multi-hop chains (L1→L2→L3)</li>
|
||||
<li>Per-client policy · egress select</li>
|
||||
<li>explain / trace</li>
|
||||
<li>Consumer leaderboard · node SLA</li>
|
||||
<li>Profiles / scenes</li>
|
||||
<li>Config history + diff + rollback</li>
|
||||
<li>One-click test-all → rank</li>
|
||||
<li>Telegram / webhook alerts</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col"><div class="col-h"><span>T2</span><span class="chip t2">power</span></div>
|
||||
<ul>
|
||||
<li>Per-domain stats · connection inspector</li>
|
||||
<li>Device quotas · schedules</li>
|
||||
<li>FakeIP · geo-map · ad-block</li>
|
||||
<li>WAN-mode profiles · awg-wrap</li>
|
||||
<li>Auto route optimization</li>
|
||||
<li>QR import/export</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col"><div class="col-h"><span>T3</span><span class="chip t3">moonshots</span></div>
|
||||
<ul>
|
||||
<li>Fleet: one UI → many routers</li>
|
||||
<li>Per-destination latency routing</li>
|
||||
<li>Config converters (passwall/clash)</li>
|
||||
<li>sing-box engine adapter</li>
|
||||
<li>REST/gRPC API · TG bot</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer><div class="wrap">
|
||||
shater · design blueprint · xray-core (not forked) + xrayctl control-plane · OpenWrt / ImmortalWrt<br>
|
||||
repo: git.qomar.pw/omar/shater · engine locked: xray-core · generator: Go + libXray · test: QEMU/WSL
|
||||
</div></footer>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
var b=document.getElementById('tt');
|
||||
b.addEventListener('click',function(){
|
||||
var cur=document.documentElement.getAttribute('data-theme');
|
||||
if(!cur){cur=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';}
|
||||
document.documentElement.setAttribute('data-theme', cur==='dark'?'light':'dark');
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
Reference in New Issue
Block a user