github Leadaxe/singbox-launcher v1.3.1-52-gb97f13e-prerelease
prerelease v1.3.1-52-gb97f13e-prerelease

pre-releaseone hour ago

Release v1.3.1-52-gb97f13e-prerelease

Downloads

macOS (Universal) - Supports both Apple Silicon and Intel

Option 1: Installation Script (Recommended)

Install with a single command (version v1.3.1-52-gb97f13e-prerelease):

curl -fsSL https://raw.githubusercontent.com/Leadaxe/singbox-launcher/develop/scripts/install-macos.sh | bash -s -- v1.3.1-52-gb97f13e-prerelease

The script will:

  • Download the release archive
  • Extract and install to /Applications/
  • Fix macOS quarantine attributes and permissions
  • Launch the application automatically

Option 2: Manual Installation

  1. Download: singbox-launcher-v1.3.1-52-gb97f13e-prerelease-macos.zip
  2. Extract the ZIP file
  3. Remove quarantine attribute (required):
    xattr -cr "singbox-launcher.app" && chmod +x "singbox-launcher.app/Contents/MacOS/singbox-launcher"
  4. Double-click singbox-launcher.app to run
    • If macOS blocks the app, go to System Settings → Privacy & Security and click "Open Anyway"
    • Alternatively, right-click the app and select "Open" (first time only)

Windows (amd64)

  1. Download: singbox-launcher-v1.3.1-52-gb97f13e-prerelease-win64.zip
  2. Extract the ZIP file to a folder, for example: C:\Program Files\singbox-launcher\
  3. Run singbox-launcher.exe from that folder
    • You may need administrator rights to install to Program Files
    • The launcher will automatically download sing-box and wintun.dll on first launch

Windows 7 (x86, legacy)

  1. Download: singbox-launcher-v1.3.1-52-gb97f13e-prerelease-win7-32.zip
  2. Extract the ZIP file to a folder and run singbox-launcher-win7-32.exe
    • For Windows 7 / 32-bit or legacy compatibility only

Linux Support

⚠️ Linux build temporarily unavailable - мы ищем тестировщика для ручного тестирования перед включением автоматической сборки.

Checksums

See checksums.txt for SHA256 checksums of all files.

⚠️ Pre-release build off v1.3.1-52-gb97f13e-prerelease — for testing only, not for production. Use the latest stable release on the Releases page if you are not sure.


Upcoming release — черновик

Сюда складываем пункты, которые войдут в следующий релиз. Перед релизом переносим в X-Y-Z.md и очищаем этот файл.

Не добавлять сюда мелкие правки только UI (порядок виджетов, выравнивание, стиль кнопок без смены действия и т.п.). Писать новое поведение: данные, форматы, сохранение, заметные для пользователя возможности.

EN

Highlights

  • Every remote machine now has its own config. Until now a single profile was shared by all of them: one wizard state, one built config. Configuring a second machine silently overwrote the first, and Deploy sent whichever config happened to be written last — a router could receive a config built for a VPS. Each machine now owns a directory holding its wizard state, snapshots, built config.json, rule sets and subscription bodies, so machines can't overwrite or delete each other's files. Existing setups migrate automatically when exactly one machine is paired; with several paired the old files are left untouched and a warning is logged, because there is no way to tell whose they were.

  • Machine management moved into one place: the new Local and Remote tabs. They replace Core and Servers and share one layout — proxy list on the left, management on the right. Local pairs the proxy list with your own core's controls; Remote pairs it with the list of machines, each row showing name, platform, address and core status, with Configure / Start-Stop / Deploy / edit / remove right there. Previously this was scattered across three screens (the Servers header, the connection window, the wizard), and none of them showed a machine as a whole.

  • Picking a machine and choosing what to build for are no longer separate switches. Configure on a machine's row opens the wizard rooted on that machine's profile, and Deploy on the same row sends that machine's own config — so "built for one, deployed to another" is impossible by construction rather than by validation. A machine's platform and architecture now belong to its registry entry (edit them in its row), which is also what the config is built for.

  • The main window now opens at 1000×700 and won't shrink below it. Both tabs are two-column, and below that size the columns stop fitting and labels get cut off. It can still be stretched.

  • Daemon mode for the core (macOS). The launcher can now run the VPN core inside a long-lived system service (sing-box lxd) instead of spawning sing-box run itself — the same in-process, reload-surviving model the Android app uses. Configure it in the new connection settings window: Servers → ⚙ → Local (the Remote tab keeps the SPEC 064 remote Clash override). What it buys you:

    • Sudo once, in your own Terminal. Installing the service is a single command the launcher prepares for you (copy or open in Terminal — full launchctl output, your own sudo; the launcher itself never runs anything privileged). After that, starting/stopping the VPN and applying config changes need no password at all.
    • Quitting the launcher can keep the VPN up. By default, closing the launcher leaves the core running in the daemon; a "Stop VPN when quitting" toggle restores the classic behavior.
    • In-process config swaps. Applying a new config no longer kills and restarts a process: the daemon swaps the core in-place, validates the config in a subprocess before touching the running instance, and auto-rolls-back to the last working config if the new one fails to start.
    • Richer observability over gRPC. Proxy groups, node selection, latency tests, live status/traffic, connections, and core logs all flow over the daemon's gRPC channel (the CommandClient protocol shared with the Android line) — including a new balancer pool view on the Servers tab showing each urltest slot and its delay.
    • Pairing. The launcher pairs with the daemon over mTLS using a one-time invite (address#fingerprint#code). The install command prints one at the end — paste it into the pairing field; mint a fresh one any time with sudo sing-box lxd client add (works for remote daemons too). The daemon fully owns its home directory and credentials (daemon.json with the listen address and admin secret lives in its state dir, reported over GET /admin/info); the launcher keeps only its own client keypair — a trusted client certificate is the whole credential.

    The classic engine remains the default and is unchanged; daemon mode is opt-in and requires a core build with the lxd subcommand (sing-box-lx 1.14.0-lx.23 or newer).

Fixed

  • Quit from the tray actually terminates the process. Quitting via the tray menu (or the Exit button) with the main window hidden or unfocused left the process alive: on Windows the tray icon stayed behind as a ghost, and relaunching the .exe reported "already running" because the dead-looking process still held the single-instance lock. Root cause is in Fyne's glfw driver: Quit() runs its tray teardown only when one of our windows currently holds focus — exactly the opposite of the quit-from-tray situation. The launcher now tears the tray icon down explicitly (systray.Quit(), the same call the driver skips: on Windows it deletes the notification-area icon immediately and stops the systray message pump), and a shutdown watchdog force-exits the process within 3 seconds if the Fyne event loop still refuses to unwind — by that point sing-box is already stopped and log files are closed, so nothing is lost.

Technical / Internal

  • GracefulExit is now idempotent (sync.Once): it is reachable both from the tray/Exit button and from main() after app.Run() returns, and used to run the whole teardown twice.
  • fyne.io/systray promoted from indirect to direct dependency in go.win7.mod (v1.12.0, version unchanged); go.mod already had it direct (v1.12.2).
  • Core engine abstraction (CoreBackend). The UI, tray, shortcuts, and debug-API no longer call the process manager or Clash API directly; everything routes through the active backend (LegacyBackend = classic spawn, DaemonBackend = lxd). Proxy-group operations go through a ProxyTransport seam (Clash HTTP for classic, gRPC for daemon), so the Servers tab is engine-agnostic. Daemon mode adds google.golang.org/grpc + protobuf (darwin-only build tags — the win7 build is untouched); the daemon protobuf stubs are vendored from the fork via scripts/sync_daemonpb.sh.
  • Daemon mode requires a core built with with_lx_command (the lxd subcommand). RequiredCoreVersion must be bumped to a fork release that ships it (1.14.0-lx.23+) before the feature is usable by end users; until then it is developed against a locally built core.
  • Cleanup pass over the SPEC 094–099 code. Dead code removed (pickMainXrayVLESS, xrayBuildJumpFromOutbound, containsStringValue, activateDaemonEngineIfPossible — all orphaned by their own refactors). Copy-to-clipboard unified: one NewCopyButton replaces three drifting copies of the "icon turns into a checkmark" feedback, and fynewidget.SetClipboard replaces the deprecated per-window Window.Clipboard() across both UI packages. Deploy's resource collection moved out of the machine-list panel into services.CollectDeployResources (no widgets in it — it now has unit tests, which the GUI-excluded ui package could not have). ProcessBase is shared instead of duplicated in the machine profiler.
  • ui/components no longer drags in core. ClickRedirect — the only widget there that needed app state — took the whole *core.AppController just to focus the wizard window, and through the shared widget package that dependency reached every file importing the gutter helpers from it (20+, across ui, ui/configurator/* and ui/traffic). It now takes *uiservice.UIService, a leaf package. ui/traffic, documented as isolated from AppController, went from 20 transitive internal dependencies (core, core/services, core/build, core/config/subscription) down to 8 — the isolation its package doc claims is now real rather than aspirational.
  • Fixed while cleaning: lxdOverrideTransportForID took the override lock twice (check id, then fetch transport), so a machine switch between the two reads could hand back the wrong machine's transport — now one snapshot under a single lock. CommandRow's copy button no longer flashes the success checkmark when the command failed to build.

RU

Основное

  • У каждой удалённой машины теперь свой конфиг. До этого профиль был один на всех: одно состояние визарда, один собранный конфиг. Настройка второй машины молча затирала первую, а Deploy отправлял то, что оказалось записано последним, — роутер мог получить конфиг, собранный для VPS. Теперь у каждой машины своя директория: её состояние визарда, снапшоты, собранный config.json, rule-set'ы и тела подписок. Машины не могут перезаписать или удалить файлы друг друга. Существующие настройки переезжают автоматически, если сопряжена ровно одна машина; если их несколько — старые файлы остаются нетронутыми с предупреждением в логе, потому что определить владельца невозможно.

  • Управление машинами собрано в одном месте — вкладки Local и Remote. Они заменяют Core и Servers и устроены одинаково: слева список прокси, справа управление. На Local список соседствует с управлением своим ядром, на Remote — со списком машин: в строке видно имя, платформу, адрес и состояние ядра, там же кнопки «Настроить», Start/Stop, Deploy, правка и удаление. Раньше это было размазано по трём экранам (шапка Servers, окно подключения, визард), и ни на одном не было видно машину целиком.

  • Выбор машины и выбор «для кого собираем» перестали быть разными переключателями. Кнопка «Настроить» в строке машины открывает визард, корневой на её профиле, а Deploy в той же строке отправляет её собственный конфиг — промах «собрал для одной, задеплоил на другую» стал невозможен по конструкции, а не по проверке. Платформа и архитектура машины теперь принадлежат её записи (правятся в той же строке) — под них и собирается конфиг.

  • Главное окно открывается в 1000×700 и не сжимается меньше. Обе вкладки двухколоночные, и ниже этого размера колонки перестают помещаться, а подписи обрезаются. Растягивать по-прежнему можно.

  • Daemon-режим ядра (macOS). Лаунчер теперь умеет запускать ядро VPN внутри долгоживущей системной службы (sing-box lxd), а не спавнить sing-box run сам — та же модель «ядро внутри процесса, канал управления переживает перезагрузку конфига», что и в Android-приложении. Настраивается в новом окне подключения: Servers → ⚙ → Local (вкладка Remote — прежний удалённый Clash-override SPEC 064). Что это даёт:

    • Sudo один раз, в вашем терминале. Установка службы — одна команда, которую лаунчер готовит за вас (скопировать или открыть в терминале — полный вывод launchctl, ваш собственный sudo; сам лаунчер ничего привилегированного не запускает). Дальше запуск/остановка VPN и смена конфига идут вообще без пароля.
    • Выход из лаунчера может оставлять VPN работать. По умолчанию закрытие лаунчера не выключает ядро в демоне; галочка «Останавливать VPN при выходе» возвращает классическое поведение.
    • Смена конфига без убийства процесса. Применение нового конфига больше не перезапускает процесс: демон подменяет ядро на месте, валидирует конфиг сабпроцессом до того, как тронуть работающий инстанс, и автоматически откатывается на последний рабочий конфиг, если новый не стартовал.
    • Богатая наблюдаемость по gRPC. Группы прокси, выбор узла, тесты задержки, живой статус/трафик, соединения и логи ядра — всё идёт по gRPC-каналу демона (протокол CommandClient, общий с Android-линией), включая новый экран пула балансировщика на вкладке Servers: каждый слот urltest-группы и его задержка.
    • Сопряжение. Лаунчер сопрягается с демоном по mTLS через одноразовое приглашение (адрес#отпечаток#код). Команда установки печатает его в конце — вставьте в поле сопряжения; свежее можно выпустить в любой момент командой sudo sing-box lxd client add (работает и для удалённых демонов). Демон полностью владеет своим каталогом и учётными данными (daemon.json с адресом и админ-секретом живёт в его state-каталоге и виден через GET /admin/info); у лаунчера остаётся только собственная клиентская пара — доверенный сертификат и есть весь мандат.

    Классический движок остаётся по умолчанию и не меняется; daemon-режим включается вручную и требует сборки ядра с сабкомандой lxd (sing-box-lx 1.14.0-lx.23 или новее).

Исправлено

  • Quit из трея действительно завершает процесс. Выход через меню трея (или кнопку Exit) при скрытом или расфокусированном главном окне оставлял процесс живым: на Windows иконка в трее висела «призраком», а повторный запуск .exe отвечал «процесс уже запущен» — внешне закрытое приложение продолжало держать single-instance lock. Корень — в glfw-драйвере Fyne: Quit() выполняет снятие трея только когда одно из окон приложения в фокусе, то есть ровно наоборот к ситуации «выход из трея». Теперь лаунчер снимает иконку явно (systray.Quit() — тот самый вызов, который драйвер пропускает: на Windows он немедленно удаляет иконку из области уведомлений и останавливает message pump систрея), а сторожевой таймер завершает процесс принудительно в пределах 3 секунд, если event loop Fyne так и не размотался — к этому моменту sing-box уже остановлен и лог-файлы закрыты, потерь нет.

Техническое / Внутреннее

  • GracefulExit теперь идемпотентен (sync.Once): он достижим и из трея/кнопки Exit, и из main() после возврата app.Run(), и раньше прогонял весь teardown дважды.
  • fyne.io/systray переведён из indirect в прямую зависимость в go.win7.mod (v1.12.0, версия не менялась); в go.mod он уже был прямым (v1.12.2).
  • Абстракция движка ядра (CoreBackend). UI, трей, горячие клавиши и debug-API больше не зовут процесс-менеджер или Clash API напрямую — всё идёт через активный движок (LegacyBackend = классический spawn, DaemonBackend = lxd). Операции с группами прокси проходят через шов ProxyTransport (Clash HTTP для classic, gRPC для daemon), поэтому вкладка Servers не зависит от движка. Daemon-режим добавляет google.golang.org/grpc + protobuf (build-tag только darwin — win7-сборка не затронута); protobuf-стабы демона вендорятся из форка через scripts/sync_daemonpb.sh.
  • Daemon-режим требует ядра, собранного с with_lx_command (сабкоманда lxd). Перед тем как фича станет доступна конечным пользователям, RequiredCoreVersion нужно поднять до релиза форка с этим тегом (1.14.0-lx.23+); до этого разработка идёт против локально собранного ядра.
  • Чистка кода SPEC 094–099. Удалён мёртвый код (pickMainXrayVLESS, xrayBuildJumpFromOutbound, containsStringValue, activateDaemonEngineIfPossible — все осиротели при собственных рефакторингах). Копирование в буфер сведено воедино: один NewCopyButton вместо трёх разъезжавшихся копий фидбека «иконка на секунду становится галочкой», а fynewidget.SetClipboard заменил устаревший пооконный Window.Clipboard() в обоих UI-пакетах. Сбор ресурсов для Deploy вынесен из панели списка машин в services.CollectDeployResources (виджетов в нём нет — и на него появились юнит-тесты, невозможные в GUI-пакете ui, исключённом из go test). ProcessBase переиспользуется вместо копии в профайлере машины.
  • ui/components больше не тянет core. ClickRedirect — единственный виджет там, которому нужно состояние приложения, — принимал целый *core.AppController ради того, чтобы сфокусировать окно визарда, и через общий пакет виджетов эта зависимость доставалась каждому файлу, который импортирует оттуда gutter-хелперы (20+, в ui, ui/configurator/* и ui/traffic). Теперь он принимает *uiservice.UIService — листовой пакет. У ui/traffic, задокументированного как изолированный от AppController, транзитивных внутренних зависимостей стало 8 вместо 20 (ушли core, core/services, core/build, core/config/subscription): изоляция, заявленная в доккомменте пакета, стала фактической, а не декларативной.
  • Попутно исправлено: lxdOverrideTransportForID брал блокировку override дважды (сначала проверка id, потом получение транспорта) — смена машины между двумя чтениями могла вернуть транспорт не той машины; теперь один снимок под одной блокировкой. Кнопка копирования в CommandRow больше не показывает галочку успеха, когда команду собрать не удалось.

Don't miss a new singbox-launcher release

NewReleases is sending notifications on new releases.