Files
shater/README.md
T
omarandClaude Opus 5 a8f2b0f068 ci: derive package versions from the git tag (B4)
PKG_VERSION/PKG_RELEASE were hand-written literals nobody bumped, so
v0.2.2 … v0.2.6 all shipped as `shaterd 0.2.0-r3` with different binaries
inside (v0.2.6's ELF is 5 491 616 B against r2's 5 488 336 B). Both opkg
and apk offer an upgrade only when the feed's version string differs from
the installed one, so `apk update` saw nothing new and the routers could
not be updated through the normal path at all.

ci/version.sh is now the single source of truth. It derives the version
from `git describe`:

    tag `vX.Y.Z`   -> PKG_VERSION=X.Y.Z  PKG_RELEASE=1
    off-tag build  -> nearest tag + PKG_RELEASE=<commits since it> + 1
    no tag/no git  -> 0.0.0-r1 (below everything ever published)

Ordering verified with the real tools, not from memory — apk-tools 3.0.3
(`apk version -t`) and opkg 38eccbb1 (`opkg compare-versions`) agree that
0.2.0-r3 < 0.2.6-r2 < 0.2.6-r10 < 0.2.6-r12 < 0.2.7-r1 < 0.3.0-r1, so a
release always outranks the rolling builds that preceded it and rolling
builds grow monotonically between releases.

The value travels as SHATER_PKG_VERSION/SHATER_PKG_RELEASE in the SDK
build environment of BOTH lanes; the Makefiles keep a literal fallback so
a manual/offline build still works with no CI and no git. Because the
hand-off crosses docker, `su` and make's env import, ci/sdk-build.sh and
ci/sdk-build-apk.sh now ASSERT that the produced .ipk/.apk really carries
that version — the B4 failure mode was a stale version shipping silently,
and that can no longer happen quietly.

The binary agrees with the package: scripts/build-shaterd.sh takes
constant.Version from the same ci/version.sh (vX.Y.Z-rR[-g<sha>]) instead
of its own `git describe`, and the workflow computes it once per job.
Both build jobs now check out with fetch-depth: 0 — `git describe` needs
tags and ancestry, which the default shallow checkout has neither of.

byedpi is deliberately left alone: PKG_VERSION:=0.17.3 is upstream
ByeDPI's own version, what PKG_HASH pins and what tells an operator which
ByeDPI is installed. Stamping our tag on it would also be a downgrade —
every comparator reads 0.2.7 < 0.17.3 (component-wise, 2 < 17), verified.

Docs: INSTALL.md gains §2.1 (the scheme + the ordering evidence), and the
update sections of §5/§6 now explicitly warn against a bare `opkg upgrade`
/ `apk upgrade` and give the targeted form instead, quoting apk-tools 3:
"If list of packages is provided, only those packages are upgraded along
with needed dependencies". README.md and the release bodies match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 12:32:38 +03:00

20 KiB
Raw Blame History

shater

Управляемый интернет-шлюз для роутеров на OpenWrt. Одна коробка превращает домашнюю или офисную сеть в прозрачный VPN-шлюз, сетевой блокировщик рекламы, трекеров и вредоносных доменов, средство родительского контроля по устройствам и живую панель аналитики трафика — всё локально, всё self-hosted, всё настраивается из богатой веб-панели.

License: GPL-3.0 targets: x86_64 · aarch64_cortex-a53 feeds: opkg 24.10 · apk 25.12


Что это

shater — это сетевой прокси-стек для роутеров на OpenWrt / ImmortalWrt / BananaWRT (Banana Pi BPI-R3, BPI-R4 и совместимые). Он прозрачно (без настройки клиентов) заворачивает весь LAN-трафик через прокси с маршрутизацией по домену, гео и клиенту, фильтрует DNS, собирает статистику и управляется из встроенной веб-панели.

Ядро — форк движка sing-box через sing-box-lx — вкомпилировано в один Go-бинарь shaterd вместе с control-plane, DNS-фильтром, агрегатором статистики и самой веб-панелью. За счёт sing-box поддерживается широкий и актуальный набор протоколов: VLESS/VMess/Trojan/Shadowsocks, Reality/XTLS, WireGuard, AmneziaWG 2.0, Hysteria2, TUIC, XHTTP, MASQUE/CONNECT-IP.

Интеграция в OpenWrt — тонкий LuCI-лаунчер: мини-дашборд и кнопка «Открыть панель», которая по одноразовому токену передаёт браузер в полноценную SPA-панель, поднятую демоном на собственном порту (по умолчанию :8088).


Ключевые возможности

Прозрачный прокси и маршрутизация

  • TPROXY data-plane для нескольких LAN-интерфейсов (TCP + UDP), сниффинг SNI/Host/QUIC, без утечек DNS.
  • Правила маршрутизации по источнику (IP/CIDR/MAC/интерфейс/зона), назначению (domain/suffix/keyword/geosite), спискам, порту, протоколу → outbound / selector / chain / direct / block.
  • Группы узлов с балансировщиком/обсерваторией (least-ping / failover / round-robin), мульти-хоп цепочки и выбор egress по правилу.

Надёжность («железно»)

  • Fail-closed kill-switch: мёртвая группа → block, а не тихая утечка мимо прокси; собственная nft-таблица inet shater и свои марки/таблицы, fw4 не трогаем.
  • Атомарный apply с валидацией движком и nft -c, commit-confirm с авто-откатом к последней рабочей конфигурации.
  • Идемпотентный reconcile из hotplug/boot под flock; management-bypass (SSH/LuCI/LAN) всегда в обход.

DNS, фильтрация, блокировки

  • Перехват :53, DNS движка sing-box в процессе; резолверы DoH/DoT/plain/FakeIP, выбор резолвера по домену.
  • Блок-листы с гибкими источниками: inline / file / url (авто-обновление) / категория geosite; hosts-файл, plain-список или AdBlock-стиль ||domain^ компилируются в локальный .srs. Эффективный компилированный матчер вместо dnsmasq-мегасписков.
  • Block-DoH/DoT — не даёт устройствам обходить фильтр через свой шифрованный DNS.

Подписки и узлы

  • Подписки (VLESS/VMess/Trojan/SS/WG/AmneziaWG), форматы Clash/sing-box/Xray-JSON, интервал обновления + вручную + на загрузке; стабильная идентичность узла между обновлениями; квоты/срок из subscription-userinfo.
  • Ручные узлы: share-ссылки, импорт файла, wg-quick/AmneziaWG .conf.
  • Health board: TCP + реальная проба через прокси-путь, exit-IP, «протестировать все».

Контроль по устройствам

  • Авто-обнаружение устройств (dhcp.leases + ip neigh), имена, живой статус/трафик.
  • Тумблеры на устройство: прокси on/off, блок-листы on/off, страна/узел выхода; блок/allow домена для одного устройства или для всех; расписания.

Статистика и видимость

  • Топ доменов (запрошенные/заблокированные), allowed-vs-blocked, разбивка по устройствам, таймлайны — из DNS-событий движка в процессе (без скрейпинга логов).
  • Трафик по клиенту/узлу/правилу (байты) из nft-счётчиков; живой query-log.

Панель и профили

  • Встроенная SPA-панель (собственный порт, вшита в бинарь): overview, узлы и подписки, правила маршрутизации, DNS/блок-листы, устройства, apply/rollback.
  • Именованные профили/сцены и WAN-профили (условные оверрайды).

Полный список с тегами MVP/T1/T2 — docs-shater/FEATURES.md.


Архитектура

Один бинарь shaterd держит движок, control-plane, DNS-фильтр и веб-сервер панели в одном процессе; OpenWrt-обвязка (тонкий LuCI + procd/system glue) оборачивает его. Конфиг — UCI desired-state; демон рендерит его в конфиг движка и применяет; телеметрия течёт обратно в панель.

flowchart TB
  subgraph BIN["shaterd — один бинарь (форк sing-box-lx)"]
    ENG["движок sing-box\nпротоколы · Reality · AmneziaWG 2.0 · DNS · routing · stats"]
    CTRL["control-plane (shater/)\nUCI-модель · генерация конфига · apply/rollback · nft/routing"]
    FILT["DNS-фильтр + блок-листы + политика по устройствам (shater/)"]
    STAT["агрегатор статистики (shater/)"]
    PANEL["веб-сервер панели + вшитая SPA (свой порт, токен-auth)"]
  end
  subgraph WRT["OpenWrt-обвязка (openwrt/)"]
    LUCI["тонкий LuCI — мини-дашборд + кнопка «Открыть панель»"]
    PROCD["procd init · hotplug · uci-defaults · fw4/routing"]
  end
  LUCI -->|"ubus: mint token"| PANEL
  PROCD --> BIN
  CTRL --> ENG
  FILT --> ENG
  ENG --> STAT
  STAT --> PANEL

Путь трафика: LAN-клиент → nft tproxy (mark → tproxy-порт) → tproxy-inbound sing-box (сниффинг SNI/Host/QUIC) → маршрут по правилу → outbound/selector/chain (проксировано) · direct (flow-offload) · block. Подробные диаграммы (auth-handoff, data-plane, DNS-flow, apply-flow) — в docs-shater/ARCHITECTURE.md.


Установка

shater поставляется двумя подписанными фидами. Выберите по версии OpenWrt на роутере:

  • OpenWrt 24.10 → фид opkg (.ipk, Packages.gz, ключ usign).
  • OpenWrt / ImmortalWrt / BananaWRT 25.12+ → фид apk (.apk, packages.adb, EC-ключ).

Пакеты ставятся по зависимостям: shaterd → shater-core → luci-app-shater (+ опциональный byedpi). shaterd подтягивается автоматически как зависимость.

Путь A — фид opkg (OpenWrt 24.10)

# 1) доверяем ключу фида — ИМЯ файла обязано равняться отпечатку usign-ключа.
wget -O /etc/opkg/keys/5ac4b177689cb8e0 \
  https://git.qomar.pw/omar/shater/releases/download/latest/shater-feed.pub

# 2) добавляем фид (один URL обслуживает все арки).
echo "src/gz shater https://git.qomar.pw/omar/shater/releases/download/latest" \
  >> /etc/opkg/customfeeds.conf

# 3) обновляемся и ставим (shaterd подтянется как зависимость).
opkg update
opkg install luci-app-shater        # -> shater-core -> shaterd
opkg install byedpi                 # опционально: ByeDPI desync-egress

Обновление — только наши пакеты, никогда голый opkg upgrade (без аргументов он тянет обновления и на системные пакеты, это классический способ окирпичить роутер):

opkg update
opkg upgrade shaterd shater-core luci-app-shater byedpi

Путь B — фид apk (OpenWrt / ImmortalWrt / BananaWRT 25.12+)

/etc/apk/arch сам выбирает нужный per-arch релиз (apk-релизы раздельны по арке):

# 1) доверяем ключу apk-фида (любое имя *.pem под /etc/apk/keys подходит).
wget -O /etc/apk/keys/shater-apk.pem \
  "https://git.qomar.pw/omar/shater/releases/download/apk-latest-$(cat /etc/apk/arch)/shater-apk.pem"

# 2) добавляем репозиторий — строка указывает на сам ФАЙЛ-ИНДЕКС packages.adb.
echo "https://git.qomar.pw/omar/shater/releases/download/apk-latest-$(cat /etc/apk/arch)/packages.adb" \
  > /etc/apk/repositories.d/shater.list

# 3) обновляемся и ставим (shaterd подтянется как зависимость).
apk update
apk add luci-app-shater        # -> shater-core -> shaterd
apk add byedpi                 # опционально: ByeDPI desync-egress

Обновление — перечисляйте пакеты явно, голый apk upgrade не запускайте: без аргументов apk пересобирает состояние ВСЕХ установленных пакетов по ВСЕМ подключённым репозиториям и может задеть (в т.ч. откатить) посторонние системные пакеты.

apk update
apk upgrade shaterd shater-core luci-app-shater byedpi
# эквивалент, дополнительно закрепляющий пакеты в world:
# apk add -u shaterd shater-core luci-app-shater byedpi

Документация apk-tools 3 про apk upgrade: «If list of packages is provided, only those packages are upgraded along with needed dependencies». Проверить установленные версии: apk list -I shaterd shater-core luci-app-shater byedpi.

Версии пакетов CI берёт из git-тега (vX.Y.Z → X.Y.Z-r1, сборка вне тега → X.Y.Z-r<коммитов+1>), поэтому каждая новая сборка действительно видна менеджеру пакетов как новая. Подробности — docs-shater/INSTALL.md §2.1.

Полные инструкции — раздельная установка из .ipk/.apk вручную, закрепление версии (vX.Y.Z / apk-vX.Y.Z-<arch>), совместимость с BananaWRT 25.12-mtk-vendor — в docs-shater/INSTALL.md.

Включение

shater ставится инертным (globals выключены), чтобы установка не рвала связь. Настройте узлы/правила (через панель или uci), затем включите и примените:

uci set shater.globals.enabled=1
uci commit shater
shaterd apply        # apply + вооружить commit-confirm на живом демоне
shaterd confirm      # подтвердить (отменяет авто-откат)

/etc/init.d/shater enable && /etc/init.d/shater start поднимает демона под procd. Кнопка «Открыть панель» в LuCI чеканит одноразовый токен и передаёт браузер в панель (:8088 по умолчанию).


Сборка из исходников

Ship-артефакт — бинарь shaterd со вшитой SPA. Собирается вне дерева SDK скриптом scripts/build-shaterd.sh:

scripts/build-shaterd.sh [VERSION] [--fast]

Что он делает: (1) собирает панель — cd panel && npm ci && npm run build (Vite → panel/dist); (2) копирует panel/dist/* в shater/panel/webroot/, откуда //go:embed вшивает реальную SPA в бинарь; (3) кросс-собирает под {amd64, arm64} с musl-static набором тегов (CGO_ENABLED=0 GOOS=linux), stripped/trimmed; (4) прогоняет UPX --lzma --best (~42 МБ → ~8–11 МБ); (5) стейджит артефакт в openwrt/shaterd/files/ для пакета.

Затем OpenWrt-пакеты из openwrt/ собираются каноническим путём SDK. Детали (набор build-тегов, почему shaterd — prebuilt-пакет, порядок CI) — в docs-shater/INSTALL.md.


Структура репозитория

Репозиторий — это оверлей продукта shater поверх дерева форка движка sing-box-lx (конфликт-фри: движок в апстрим-каталогах, продукт в своих).

Путь Что это
shater/ Go: control-plane, DNS-фильтр, агрегатор статистики, хост движка
panel/ Админ-SPA (Vite + React + TS) и её Go-сервер
openwrt/ Пакеты: shaterd, shater-core, luci-app-shater, byedpi
docs-shater/ Документация продукта (см. таблицу ниже)
scripts/ build-shaterd.sh — сборка ship-артефакта
ci/ Скрипты сборки фидов и релизов (SDK, usign/EC, Gitea API)
.gitea/workflows/ release.yml — CI: сборка пакетов + подписанные фиды opkg/apk
SPECS/ Конституция форка движка и спеки (Spec Kit)
docs-lx/ Справочник конфигурации фич движка (lx-config.md, .ru.md)
lx-test/, submodules/ Примеры конфигов движка и submodule AmneziaWG-рантайма
docs/, mkdocs.yml Апстрим документация sing-box (mkdocs) — как есть
adapter/ cmd/ dns/ route/ option/ protocol/ transport/ … Дерево движка sing-box-lx

CI и релизы

CI на Gitea Actions (.gitea/workflows/release.yml) собирает все 4 пакета и публикует подписанные фиды:

  • opkg (24.10): один комбинированный релиз, подписан usign-ключом (публичный dist/shater-feed.pub, отпечаток 5ac4b177689cb8e0; секрет — в Gitea-secret KEY_BUILD).
  • apk (25.12+): параллельная линия, по релизу на арку, подписан EC-ключом (dist/shater-apk.pem; секрет — KEY_APK).

Триггеры: push тега vX.Y.Z → версионный релиз; workflow_dispatch → плавающий latest/apk-latest-<arch> (всегда свежий фид). Публикация — через Gitea API (ci/gitea-release.sh). Ключи никогда не перегенерируются — это инвалидировало бы доверие на всех развёрнутых роутерах.


Связь с upstream и движок

shater вкомпилирует форк движка sing-box-lx — тонкий downstream апстрима SagerNet/sing-box, добавляющий набор клиентских фич (XHTTP, AmneziaWG 2.0, MASQUE, расширения наблюдаемости) за build-тегами и живущий ребейзом на каждый upstream-тег, а не merge. Форк разрабатывается по Spec Kit; неизменяемые принципы — в SPECS/CONSTITUTION.md, справочник фич движка — в docs-lx/lx-config.ru.md.

История: v0.1 (движок на xray-core, полностью рабочая и VM-проверенная версия) сохранена на ветке v0.1. v0.2 схлопнула runtime в один форкнутый бинарь.


Документация

Документ О чём
docs-shater/CONTEXT.md Начните здесь — контекст проекта, история v0.1→v0.2, testbed/инфра
docs-shater/INSTALL.md Сборка ship-артефакта и установка обоих фидов (opkg/apk)
docs-shater/ARCHITECTURE.md One-binary дизайн, auth-handoff, data/DNS/apply-потоки (диаграммы)
docs-shater/FEATURES.md Полный список фич с тегами MVP/T1/T2
docs-shater/ROADMAP.md Фазовый план
docs-shater/DECISIONS.md Почему sing-box, почему форк, split панели, лицензия
docs-shater/DESIGN.md Визуальная система панели — «Faceplate», токены, компоненты
docs-shater/PORTING.md Порт проверенных кусков из v0.1

Индекс папки — docs-shater/README.md.


Оборудование

Арка aarch64_cortex-a53 покрывает Banana Pi BPI-R3 (MT7986/Filogic 830) и BPI-R4 (MT7988/Filogic 880) — оба таргет OpenWrt mediatek/filogic. x86_64 — QEMU-стенд для тестов.


Лицензия

GPL-3.0 — как у upstream sing-box. Подробности — в docs-shater/DECISIONS.md (D6). Неофициальный форк, не аффилирован с SagerNet.