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>
20 KiB
shater
Управляемый интернет-шлюз для роутеров на OpenWrt. Одна коробка превращает домашнюю или офисную сеть в прозрачный VPN-шлюз, сетевой блокировщик рекламы, трекеров и вредоносных доменов, средство родительского контроля по устройствам и живую панель аналитики трафика — всё локально, всё self-hosted, всё настраивается из богатой веб-панели.
Что это
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>), совместимость с BananaWRT25.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-secretKEY_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.