Чекер нод для панели Remnawave. Если нода, привязанная к хосту, падает и не поднимается заданное время, менеджер отключает хост. Включённый он был или скрытый — неважно. Когда нода восстанавливается, хост включается обратно.
- Совместимость с панелью 2.7.x – 3.x (проверено на контрактах API 2.7.0 и 3.4.5).
- Опционально вычищает отключаемый хост из шаблонов Mihomo / Stash / Clash и возвращает его на место после включения.
- Настраиваемые уведомления в Telegram: события, чаты, топики, свои шаблоны сообщений, прокси.
- Веб-интерфейс в стиле shadcn/ui со входом администратора, языками RU/EN и темами «Системная / Светлая / Тёмная» (системная следует за настройкой ОС на лету).
- Шрифты Ferrum Trio и Ferrum Mono (SIL OFL 1.1) встроены в бинарник, без внешних CDN.
- Build info как в OpenWrt-exodus: версия, ветка, коммит и время сборки в шапке; при выходе новой версии чип подсвечивается.
- Один статический бинарник с SQLite, Docker-образ для
linux/amd64иlinux/arm64.
- Каждые N секунд (по умолчанию 30) менеджер читает из панели список нод и хостов.
- Нода считается упавшей, если
isConnected = false. Ноды, выключенные в панели вручную, по умолчанию не считаются упавшими, это настраивается. - Хост учитывается, если к нему привязаны ноды (поле «Ноды» в настройках хоста). Если все его ноды недоступны дольше заданного времени, хост отключается. Политику можно сменить на «любая нода».
- Отключённый менеджером хост включается обратно, когда нода проработает заданное время после восстановления.
Что ещё учтено:
- Хосты, которые администратор отключил сам, менеджер не трогает и никогда не включает.
- Если администратор включил хост вручную, пока нода ещё лежит, менеджер это уважает. Хост не будет отключён повторно, пока ноды не восстановятся.
- Тестовый режим: только журнал и уведомления, без изменений в панели.
- Защита от массового отключения: если одновременно легла заданная доля нод (например, проблема у самой панели), хосты не отключаются.
- Ноды и хосты можно исключить из обработки на страницах «Ноды» и «Хосты».
- Состояние хранится в SQLite, поэтому перезапуск контейнера не сбрасывает таймеры и не теряет хосты, которые надо включить обратно.
Панель сама убирает отключённые хосты из подписок. Но если шаблон Mihomo ссылается на хост по имени, клиент не сможет загрузить такой конфиг. Это бывает в группах с remnawave: { include-proxies: false } и ручным списком proxies, а также в правилах вида DOMAIN-SUFFIX,netflix.com,🇩🇪 Germany.
При включённой опции менеджер перед отключением хоста:
- убирает его имя из
proxy-groups[].proxies; - убирает правила, которые ведут на этот хост (опционально);
- подставляет
DIRECT(настраивается), если группа сinclude-proxies: falseосталась пустой.
После включения хоста всё возвращается на прежние места. Шаблон правится точечно, по строкам: форматирование, комментарии и эмодзи сохраняются. Каждая правка сверяется с результатом разбора YAML, так что сломанный шаблон в панель не уйдёт.
mkdir -p /opt/remnanode-manager && cd /opt/remnanode-manager
curl -o docker-compose.yml https://raw.githubusercontent.com/prettyleaf/remnanode-manager/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/prettyleaf/remnanode-manager/main/.env.example
docker compose up -dCompose подключает контейнер к сети remnawave-network, поэтому адрес панели — http://remnawave:3000. Если панель на другом сервере, удалите блок networks и укажите её публичный адрес.
Веб-интерфейс слушает 127.0.0.1:3100. Опубликуйте его через reverse proxy с HTTPS и откройте в браузере. При первом входе будет предложено создать администратора. Можно сделать это заранее через ADMIN_USERNAME / ADMIN_PASSWORD в .env.
Затем в разделе Настройки → Панель укажите адрес панели и API-токен и нажмите «Проверить».
Создаётся в панели: Remnawave Settings → API Tokens. В 3.x токену можно ограничить права. Нужны:
| Права | Зачем |
|---|---|
nodes:read, hosts:read, system:read |
мониторинг |
hosts:write |
отключение и включение хостов |
subscription-template:read, subscription-template:write |
только для очистки шаблонов Mihomo |
Caddy:
manager.example.com {
reverse_proxy 127.0.0.1:3100
}nginx:
location / {
proxy_pass http://127.0.0.1:3100;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}При работе за прокси задайте TRUST_PROXY=true: тогда IP клиента для журнала и ограничения попыток входа берётся из X-Forwarded-For.
| Переменная | По умолчанию | Описание |
|---|---|---|
APP_PORT |
3100 |
порт веб-интерфейса |
APP_HOST |
0.0.0.0 |
адрес, на котором слушает сервер |
DATA_DIR |
/app/data |
каталог с базой SQLite |
TRUST_PROXY |
false |
доверять заголовкам X-Forwarded-* |
COOKIE_SECURE |
auto |
флаг Secure для cookie сессии: auto / true / false |
ADMIN_USERNAME, ADMIN_PASSWORD |
— | создать администратора при старте; пароль сбрасывается на это значение при каждом перезапуске |
REMNAWAVE_PANEL_URL, REMNAWAVE_API_TOKEN |
— | начальные настройки подключения; дальше они меняются в интерфейсе |
UPDATE_CHECK |
true |
раз в 6 часов спрашивать GitHub о новой версии (только запрос к api.github.com) |
LOG_LEVEL |
info |
debug / info / warn / error |
Остальные настройки задаются в веб-интерфейсе и хранятся в базе.
docker exec -it remnanode-manager remnanode-manager reset-adminКоманда удаляет учётные записи администраторов. При следующем открытии интерфейса будет предложено создать новую.
В разделе Настройки → Telegram укажите токен бота и ID чатов (через запятую), при необходимости ID топика. Для каждого события можно включить или выключить отправку и изменить шаблон. Поддерживается HTML-разметка Telegram и переменные вида {node}, {hosts}, {downtime}. Если api.telegram.org недоступен, задайте прокси (socks5://…, http://…) или свой сервер Bot API.
Уведомления о событиях отправляются, только когда включён переключатель «Отправлять уведомления» и настройки сохранены. Кнопка «Отправить тест» работает независимо от него и берёт ещё не сохранённые значения формы. Если Telegram отклонил уведомление о событии (бот удалён из чата, нет доступа к API и т. п.), в журнале событий появится запись «Ошибка Telegram» с причиной.
События: нода недоступна или восстановилась, хосты отключены или включены, изменён шаблон Mihomo, панель недоступна или восстановилась, сработала защита, ошибки.
Чип с версией в шапке открывает «Информацию о сборке»: версия, ветка, коммит (ссылка на GitHub), время сборки, Go и платформа, версия подключённой панели. Кнопка копирования собирает всё это в текст для issue.
Метаданные вшиваются при сборке через -ldflags (internal/buildinfo); CI и Dockerfile передают их аргументами VERSION, COMMIT, BRANCH, BUILD_TIME, REPOSITORY. Проверка обновлений:
- релизная сборка (
X.Y.Z) сравнивается с последним GitHub Release; - dev-сборка сравнивает свой коммит с головой своей ветки и даёт ссылку на diff.
Схема публикации повторяет Remnawave. Образы лежат в GitHub Packages (ghcr.io).
| Событие | Образ |
|---|---|
push в ветку dev |
ghcr.io/prettyleaf/remnanode-manager:dev |
тег X.Y.Z (например, 1.2.3, без v) |
:latest, :X.Y.Z, :X + GitHub Release vX.Y.Z |
Перед сборкой прогоняются go vet, go test и сборка фронтенда. Образы мультиархитектурные (amd64 + arm64) и собираются кросс-компиляцией, без QEMU. Отдельные секреты не нужны: используется GITHUB_TOKEN с правом packages: write.
Выпустить релиз:
git tag 0.1.0
git push origin 0.1.0Нужны Go 1.26+, Node.js 24+ и pnpm 11.
make web # собрать фронтенд в internal/webui/dist
make run # запустить сервер на :3100 с данными в ./data
make dev-web # Vite dev server на :5173 с проксированием /api на :3100
make test # go vet + go testСтруктура:
cmd/remnanode-manager точка входа
internal/checker цикл проверки и логика отключения/включения
internal/mihomo точечная правка YAML-шаблонов
internal/remnawave клиент API панели (2.7 – 3.x)
internal/notify Telegram
internal/server HTTP API, авторизация, раздача SPA
internal/store SQLite
web/ React + Vite + Tailwind + shadcn/ui