github Leadaxe/singbox-launcher v2.1.0
release v2.1.0

6 hours ago

Release v2.1.0

Downloads

macOS (Universal) - Supports both Apple Silicon and Intel

Option 1: Installation Script (Recommended)

Install with a single command (version v2.1.0):

curl -fsSL https://raw.githubusercontent.com/Leadaxe/singbox-launcher/develop/scripts/install-macos.sh | bash -s -- v2.1.0

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-v2.1.0-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-v2.1.0-win64.zip
    • or singbox-launcher-v2.1.0-win64-full.zip — full bundle: sing-box.exe, wintun.dll, the config template and Mesa3D (mesa3d/, used only when no hardware OpenGL — RDP, VMs) are already inside, nothing is downloaded on first launch
  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 regular archive downloads sing-box and wintun.dll on first launch

Windows 7 (x86, legacy)

  1. Download: singbox-launcher-v2.1.0-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.

Release notes — v2.1.0 (2026-09-24)

EN

Minor release on top of v2.0.2. Core sing-box-lx 1.14.1-lx.12 (was lx.8), contract 1.1.52 (unchanged).

Two themes. The launcher no longer needs to write into its own program folder: data and logs live where each system expects them, and portable mode is now an explicit choice (issue #85). And on macOS, whatever the launcher starts as root no longer runs from a file your user account can modify — neither the daemon service nor the core with TUN in classic mode.

Before you update on macOS

  • On the first start, data found inside the app bundle is copied to ~/Library and the launcher rebuilds config.json; the old copy stays in the bundle until you remove it. Make an LX Backup (Settings → Backup) before updating, or update with build_darwin.sh -i, which replaces only the executable and restarts the launcher: dragging a new .app over the old one destroys the data before the new version can move it.

Highlights

  • The launcher no longer needs a writable program folder (issue #85). Data and logs have their own places: Linux ~/.local/share/singbox-launcher and ~/.local/state/singbox-launcher/logs, macOS ~/Library/Application Support/singbox-launcher and ~/Library/Logs/singbox-launcher, Windows %LOCALAPPDATA%\singbox-launcher. NixOS, Guix, Flatpak and snap installs start instead of failing on the first mkdir.
  • Portable mode is explicit. A portable.txt file next to the program keeps everything next to it, exactly as before; the Windows zip archives ship with it, so nothing changes for existing Windows users. Existing installs with data next to the binary keep working where they are. Settings → Storage has a checkbox that moves the data between the two layouts and restarts the launcher.
  • Settings → Storage shows every path the launcher uses (mode, program, data, logs, core with its version and source, template) with Open buttons and “Copy paths” for bug reports. The same block is the first line of the log, GET /debug/paths in the Debug API, and singbox-launcher -paths on the command line, which works without a window.
  • Remove all data: Settings → Storage → “Remove all data…” lists what will be deleted (data, logs, leftovers of earlier moves, on Windows ghost wintun adapters and orphan firewall rules) and does it; the program folder itself is left for you to delete. -purge-data prints the plan, -purge-data -yes executes it, so an installer can call it.
  • Linux: the core is now looked up in the data folder, then next to the program, then in PATH — the distribution's sing-box used to win over the downloaded lx fork. Set SINGBOX_LAUNCHER_CORE=/path/to/sing-box to force a binary. setcap on the downloaded core does not work on a home partition mounted nosuid.

Security

  • macOS daemon: the daemon service used to run as root from a file your user account can modify (the core in the bundle or in the data folder), so any program running as you could replace it and get root on the next service restart. It now runs from a copy of the core owned by root, /Library/PrivilegedHelperTools/sing-box-lxd. The launcher shows when the service is unsafe (red notice on the Local tab, plus a dialog once per launcher version) or runs a different core than the launcher (yellow); the VPN keeps working either way. One command — Install or update service — fixes every case: first install, an old service, and the update after downloading a new core. The command comes only from a launcher core lx.12 or newer: an older one would point the service back at a file you can modify, so the launcher asks you to update the core first instead (Local tab → Download/Reinstall).
  • macOS classic mode: the core with TUN now runs as root only from the same root-owned copy, never from a file your user account can modify. On the first TUN start after the update the launcher asks you to run one command (sudo … lxd --service=copy, or the service's install command when the daemon service is installed) and click Retry; after a core update it asks again before the next TUN start. Root also no longer writes into your folders: the core's log in TUN mode is /Library/Logs/sing-box-lxd/classic.log (Logs → Core shows it), and turning TUN off in the wizard no longer asks for the password. Closed on macOS; Windows (the launcher runs elevated there) comes next.
  • Windows: the native file dialogs (Add from file, opening and saving an LX Backup) no longer put their captions into the PowerShell command as quoted text. A caption with a typographic quote (’) could end the quoted text early, and the rest of it would be read as part of the command — in a launcher that runs as administrator.

Core

  • Core pinned to sing-box-lx 1.14.1-lx.12 (was lx.8). Both macOS security items need it: the core itself makes the root-owned copy — lxd --service=install together with the service, --service=copy without one (the service label stays com.leadaxe.sing-box-lxd) — and the daemon reports which file it runs and that file's sha256. Fixes from lx.9 and lx.10 come along: XHTTP connections closed by the client itself no longer push healthy connections out of the pool or fill the log with ERROR lines, and catching up with upstream sing-box 1.14.1 (34 more commits from its stable branch) removes the 5 seconds lost on the first handshake with a WireGuard/AWG peer given by a domain name. No configuration changes.

Fixed

  • Copy in command dialogs now copies only the command: the dialog after a core update, the unprotected-service warning on macOS and the Linux setcap dialog used to put the explanation on the clipboard as well, and it landed in the terminal as junk input.

Technical / Internal

  • New package internal/paths: Layout{App, Data, Logs, Mode} resolved once in main(); every path helper takes a named type (AppDir read-only, DataDir/LogDir writable), so passing the program folder into a writing helper no longer compiles. FileService.ExecDir is gone. tools/paths_guard in CI-lint catches the remaining bypasses (explicit conversions, os.* writes from App).
  • Resolution order: SINGBOX_LAUNCHER_DATA_DIR / SINGBOX_LAUNCHER_LOG_DIR → portable.txt → legacy data next to a writable binary → platform default. Rules 2–3 are skipped for a macOS .app.
  • Shipped vs downloaded: template, locales and core are read Data → App; a shipped template whose wizard_template.version equals the launcher version wins over an older downloaded one (and the stale download is removed). Core companions (wintun.dll, libcronet) are looked up next to the core that actually runs.
  • settings.json gains config_data_root: whenever the data root differs from the one config.json was built with (move, migration, env override), the config is rebuilt — its .srs and tailscale paths are absolute. The old root is stamped right after a migration, so a restart before the first core start still triggers the rebuild.
  • Migration and the Portable switch share one copier (CopyTree): temporary directory, permissions preserved, unreadable files skipped and counted, state.json never left behind; a system data folder that already holds settings is protected from the switch and from cleanup, and a portable marker over hidden system data triggers an “Existing data found” prompt instead of silently starting empty.
  • SPEC 022 and SPEC 080 are closed as absorbed by SPEC 135.
  • macOS daemon (SPEC 136): the path comparison between the plist and the launcher core is replaced by a service classifier — plist target, ownership chain of the root-owned copy (/Library → PrivilegedHelperTools → the flat copy file: root, no symlinks, no group/other write), sha256 of the copy against the launcher core (cached by dev/inode/size/mtime), and the running daemon's executable/executable_sha256 from /admin/info (with an older core, its version). Uninstall, re-pair and the purge hint run through the copy when it is safe; the kickstart row is gone from the UI (a restart would bring the old copy back). /daemon/status in the Debug API gains service_state (with core_too_old: the install/copy commands are gated on the launcher core version ≥ 1.14.1-lx.12 (its release candidates pass), parsed with -lx.N as a number and release candidates below the release), service_path, service_detail; settings.json gains daemon_unsafe_notice_version. A service that is installed correctly but not running at launchd (launchctl print, no sudo) is not_running — a yellow notice with sudo launchctl bootstrap system <plist> instead of a reinstall; the Uninstall tab keeps the root-owned copy (--keep-copy) for the classic TUN start, while Remove all data… removes it.
  • macOS classic TUN (SPEC 137): the privileged start no longer writes bin/start-singbox-privileged.sh — root runs /usr/bin/env -i PATH=… /bin/sh -c <constant body> with paths as arguments, so neither user files nor the launcher's environment (PATH, exported bash functions) reach the root shell; stop and pkill call /bin/kill, /usr/bin/pkill directly. SPEC 137.1: the core's output under root goes to /Library/Logs/sing-box-lxd/classic.log — a root-owned folder, the file owned by the launcher user with mode 0600 (the uid is validated in the body: digits, ≥ 501, an existing account); the constant body creates, checks (no symlink, owner, type) and rotates it and reports a refusal reason instead of a PID; the log window and the traffic profiler follow AppController.CoreLogPath(); the TUN-off cleanup runs with the launcher's own uid, without a privileged call. A gate before the password prompt checks the copy (ownership chain, sha256 against the launcher core — the SPEC 136 checks and hash cache); a refused gate shows a command dialog with Copy / Run in Terminal / Retry instead of a startup error. Authorization still lives for the session; privilegedAuthReuse narrows it to one action.
  • Windows file dialogs: the PowerShell script is built whole in Go and run via -EncodedCommand, with every value (caption, filter, file name) base64-encoded as UTF-8 and decoded inside the script — PowerShell also treats ‘ ’ ‚ ‛ as quotes, which single-quote escaping missed.
  • The log line about a leftover single-machine remote profile that cannot be assigned to a machine is a WARN once, then INFO on later starts (marker bin/.legacy-remote-noticed); the migration itself is unchanged.

Specifications: SPEC 135 — data directory layout · SPEC 136 — the daemon service on a root-owned copy of the core · SPEC 137 — classic TUN on the same copy


RU

Минорный релиз поверх v2.0.2. Ядро sing-box-lx 1.14.1-lx.12 (было lx.8), контракт 1.1.52 (без изменений).

Две темы. Лаунчеру больше не нужно писать в собственную папку программы: данные и логи лежат там, где их ждёт система, а portable-режим стал явным выбором (issue #85). И на macOS всё, что лаунчер запускает от root, больше не запускается из файла, который может изменить ваша учётная запись, — ни служба демона, ни ядро с TUN в classic-режиме.

Перед обновлением на macOS

  • При первом запуске данные, найденные внутри бандла, копируются в ~/Library, а config.json пересобирается; старая копия остаётся в бандле, пока вы её не удалите. Перед обновлением сделайте LX Backup (Settings → Backup) или обновляйтесь через build_darwin.sh -i, который меняет только исполняемый файл и перезапускает лаунчер: перетаскивание нового .app поверх старого уничтожает данные раньше, чем новая версия успеет их перенести.

Основное

  • Лаунчеру больше не нужна запись в папку программы (issue #85). У данных и логов свои места: Linux ~/.local/share/singbox-launcher и ~/.local/state/singbox-launcher/logs, macOS ~/Library/Application Support/singbox-launcher и ~/Library/Logs/singbox-launcher, Windows %LOCALAPPDATA%\singbox-launcher. Установки на NixOS, Guix, Flatpak и snap стартуют, а не падают на первом mkdir.
  • Portable-режим стал явным. Файл portable.txt рядом с программой оставляет всё рядом с ней, как раньше; zip-архивы для Windows поставляются с ним, поэтому для нынешних пользователей Windows ничего не меняется. Установки с данными рядом с бинарём продолжают работать там, где были. В Settings → Storage есть галка, которая переносит данные между двумя раскладками и перезапускает лаунчер.
  • Settings → Storage показывает все пути лаунчера (режим, программа, данные, логи, ядро с версией и источником, шаблон) с кнопками открытия папок и «Copy paths» для сообщений об ошибках. Тот же блок — первая строка лога, GET /debug/paths в Debug API и singbox-launcher -paths в командной строке, без окна.
  • Удаление с очисткой: Settings → Storage → «Remove all data…» перечисляет, что будет удалено (данные, логи, остатки прошлых переездов, на Windows призрачные адаптеры wintun и осиротевшие правила файрвола), и делает это; папку с программой вы удаляете сами. -purge-data печатает план, -purge-data -yes выполняет, так что установщик может его вызывать.
  • Linux: ядро теперь ищется в папке данных, затем рядом с программой, затем в PATH — раньше дистрибутивный sing-box побеждал скачанный форк lx. SINGBOX_LAUNCHER_CORE=/путь/к/sing-box задаёт бинарь явно. setcap на скачанном ядре не работает на домашнем разделе с nosuid.

Безопасность

  • Демон macOS: служба демона запускалась от root из файла, который может изменить ваша учётная запись (ядро в бандле или в папке данных), — любая программа, работающая от вашего имени, могла подменить его и получить root при ближайшем перезапуске службы. Теперь служба запускается из копии ядра, принадлежащей root, — /Library/PrivilegedHelperTools/sing-box-lxd. Лаунчер показывает, если служба небезопасна (красная плашка на вкладке «Локально» и один диалог на версию лаунчера) или запускает не то ядро, что у лаунчера (жёлтая); VPN при этом продолжает работать. Одна команда — Install or update service — чинит всё: первую установку, старую службу и обновление после скачивания нового ядра. Команду даёт только ядро лаунчера lx.12 и новее: более старое вернуло бы службу на файл, который может изменить ваша учётная запись, поэтому лаунчер сначала просит обновить ядро (вкладка «Локально» → «Скачать/Переустановить»).
  • macOS, classic-режим: ядро с TUN под root теперь запускается только из той же копии, принадлежащей root, а не из файла, который может изменить ваша учётная запись. При первом старте с TUN после обновления лаунчер попросит выполнить одну команду (sudo … lxd --service=copy, а при установленной службе демона — её команду установки) и нажать «Повторить»; после обновления ядра — снова, перед ближайшим стартом с TUN. И root больше не пишет в ваши папки: лог ядра в режиме TUN — /Library/Logs/sing-box-lxd/classic.log (его показывает Logs → Core), а снятие TUN в визарде больше не спрашивает пароль. Закрыто на macOS; Windows (там лаунчер работает с правами администратора) — следующим.
  • Windows: системные диалоги файлов (Add from file, открытие и сохранение LX Backup) больше не вставляют подписи в команду PowerShell текстом в кавычках. Подпись с типографской кавычкой (’) могла закрыть кавычки раньше времени, и остаток читался бы как часть команды — в лаунчере с правами администратора.

Ядро

  • Ядро закреплено на sing-box-lx 1.14.1-lx.12 (было lx.8). Оно нужно обоим пунктам о безопасности на macOS: копию, принадлежащую root, делает само ядро — lxd --service=install вместе со службой, --service=copy без неё (ярлык службы остаётся com.leadaxe.sing-box-lxd), — а демон сообщает, какой файл он запускает, и его sha256. Попутно приходят исправления lx.9 и lx.10: XHTTP-соединения, закрытые самим клиентом, больше не вытесняют здоровые соединения из пула и не засоряют лог строками ERROR, а подтягивание к апстриму sing-box 1.14.1 (ещё 34 коммита его стабильной ветки) убирает 5 секунд, терявшиеся на первом рукопожатии с пиром WireGuard/AWG по доменному имени. Конфигурация не меняется.

Исправления

  • Кнопка Copy в диалогах с командой теперь копирует только команду: диалог после обновления ядра, предупреждение о незащищённой службе на macOS и диалог setcap на Linux клали в буфер ещё и пояснение, и оно попадало в терминал лишним текстом.

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

  • Новый пакет internal/paths: Layout{App, Data, Logs, Mode} считается один раз в main(); каждый хелпер путей принимает именованный тип (AppDir только чтение, DataDir/LogDir запись), так что папку программы в пишущий хелпер не пропустит компилятор. FileService.ExecDir удалён. tools/paths_guard в CI-lint ловит оставшиеся обходы (явные конверсии, os.*-записи от App).
  • Порядок определения: SINGBOX_LAUNCHER_DATA_DIR / SINGBOX_LAUNCHER_LOG_DIR → portable.txt → унаследованные данные рядом с пишущимся бинарём → платформенный дефолт. Для macOS .app правила 2–3 не применяются.
  • Поставляемое и скачанное: шаблон, локали и ядро читаются Data → App; поставляемый шаблон с wizard_template.version, равным версии лаунчера, побеждает более старый скачанный (устаревшая копия удаляется). Спутники ядра (wintun.dll, libcronet) ищутся рядом с тем ядром, которое реально запускается.
  • В settings.json появился config_data_root: при любом расхождении корня данных с тем, под который собран config.json (переезд, миграция, переменная окружения), конфиг пересобирается — пути .srs и tailscale в нём абсолютные. Прежний корень штампуется сразу после миграции, так что перезапуск до первого старта ядра пересборку не теряет.
  • У миграции и переключателя Portable один копировщик (CopyTree): временный каталог, права сохраняются, нечитаемое пропускается и считается, state.json не теряется; системная папка данных с настройками защищена от переключателя и от очистки, а portable-маркер поверх скрытых системных данных показывает диалог «Existing data found», а не молча стартует пустым.
  • SPEC 022 и SPEC 080 закрыты как поглощённые SPEC 135.
  • Демон macOS (SPEC 136): сверка путей plist ↔ ядро лаунчера заменена классификатором службы — цель plist, цепочка владения root-owned копии (/Library → PrivilegedHelperTools → плоский файл копии: root, без симлинков, без записи группой/остальными), sha256 копии против ядра лаунчера (кэш по dev/inode/size/mtime) и executable/executable_sha256 работающего демона из /admin/info (у старого ядра — версия). Uninstall, пере-сопряжение и подсказка очистки идут через копию, когда она безопасна; строка kickstart из UI убрана (перезапуск поднял бы старую копию). В Debug API /daemon/status появились service_state (с core_too_old: команды install/copy гейтятся версией ядра лаунчера ≥ 1.14.1-lx.12 (его rc проходят), -lx.N сравнивается числом, rc ниже релиза), service_path, service_detail; в settings.json — daemon_unsafe_notice_version. Служба, установленная верно, но не запущенная у launchd (launchctl print, без sudo), — not_running: жёлтая плашка с sudo launchctl bootstrap system <plist> вместо переустановки; вкладка Uninstall оставляет root-owned копию (--keep-copy) для старта TUN в classic, а Remove all data… её удаляет.
  • Classic TUN на macOS (SPEC 137): привилегированный старт больше не пишет bin/start-singbox-privileged.sh — root запускает /usr/bin/env -i PATH=… /bin/sh -c <постоянное тело> с путями аргументами, так что ни файлы пользователя, ни окружение лаунчера (PATH, экспортированные функции bash) до root-шелла не доходят; остановка и pkill зовут /bin/kill, /usr/bin/pkill напрямую. SPEC 137.1: вывод ядра под root идёт в /Library/Logs/sing-box-lxd/classic.log — каталог root, файл пользователя лаунчера с правами 0600 (uid проверяется в теле: цифры, ≥ 501, учётная запись существует); постоянное тело создаёт, проверяет (не симлинк, владелец, тип) и ротирует его и вместо PID сообщает причину отказа; окно логов и профайлер трафика следуют за AppController.CoreLogPath(); чистка при снятии TUN — uid'ом лаунчера, без привилегированного вызова. Гейт до запроса пароля проверяет копию (цепочка владения, sha256 против ядра лаунчера — проверки и кэш SPEC 136); отказ — диалог с командой и кнопками Copy / Run in Terminal / Retry вместо ошибки старта. Авторизация по-прежнему на сессию; privilegedAuthReuse сужает её до одного действия.
  • Диалоги файлов на Windows: скрипт PowerShell собирается целиком в Go и запускается через -EncodedCommand, каждое значение (подпись, фильтр, имя файла) закодировано base64 от UTF-8 и раскодируется внутри скрипта — PowerShell считает кавычками и ‘ ’ ‚ ‛, которые экранирование одинарных кавычек пропускало.
  • Строка лога об оставшемся профиле удалённой машины, который не отнести ни к одной машине, — WARN один раз, на следующих стартах INFO (маркер bin/.legacy-remote-noticed); сама миграция не меняется.

Спецификации: SPEC 135 — раскладка данных · SPEC 136 — служба демона на копии ядра, принадлежащей root · SPEC 137 — classic TUN на той же копии

Don't miss a new singbox-launcher release

NewReleases is sending notifications on new releases.