github Leadaxe/singbox-launcher v1.2.2
release v1.2.2

latest releases: v1.2.5, v1.2.4, v1.2.3...
8 hours ago

Release v1.2.2

Downloads

macOS (Universal) - Supports both Apple Silicon and Intel

Option 1: Installation Script (Recommended)

Install with a single command (version v1.2.2):

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

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.2.2-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.2.2-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.2.2-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.

v1.2.2 — core lx.5 (energy revision), subscription-credential fixes, deep code review

Core pinned: sing-box-lx 1.14.0-lx.5 (from 1.14.0-lx.4) — update via Core Dashboard → Download/Reinstall. No config migration needed.

This is a correctness release: the launcher-side fixes come from a deep review of the subscription parsers, the sing-box process lifecycle, and the Debug API, with every applied finding verified against the real code.

EN

Highlights

  • Core bumped to sing-box-lx 1.14.0-lx.5. Same build tags (with_clash_api + with_xhttp + with_awg + with_quic/with_gvisor + with_naive_outbound; Windows archives still ship libcronet.dll). The fork's energy revision lands: suspended WireGuard/AmneziaWG tunnels are no longer resurrected by a screen-off/on or a network change; idle-suspend no longer cuts a live transfer (before suspending, the tick now consults the established-TCP gauge and a transfer delta); an abandoned urltest group stops probing — and waking — its members. Two new opt-in knobs: route.lx_idle_suspend_reachable (a second, longer idle window after which even reachable endpoints suspend, waking lazily on the next dial) and urltest.passive_check (a recent successful dial counts as proof of liveness and saves whole probe cycles). (SPEC 072)
  • Subscription credentials are no longer corrupted. The parser double-decoded userinfo — url.Parse already percent-decodes it, and the extra pass turned a + in a trojan / hysteria2 / tuic / ssh / socks / naive password into a space and decoded literal %XX sequences twice. Affected nodes reached the server and silently failed authentication. SSH was hit hardest: a PEM private key body is full of +, so every inline key was mangled.
  • One bad node no longer breaks the whole config. A port outside 1..65535 now degrades just that node instead of reaching config.json and failing sing-box check for everything. The same guard now covers junk REALITY public_key values arriving from Xray-JSON subscriptions — the URI path has had it since v1.1.7, the JSON path did not; such a node falls back to plain TLS with a warning.
  • The auto-update opt-out is respected at startup. With auto-update disabled, the 30-second startup sweep still fetched every subscription once per launch.
  • A failed core update no longer destroys the working core. If the copy breaks mid-way, the previous binary is restored from .old instead of leaving a truncated file in place; a partially extracted wintun.dll is no longer installed as valid. After a successful core install the Core Dashboard shows the new version without a launcher restart.
  • Share-URI round-trip keeps more of your node. Hysteria2 / TUIC / AnyTLS exports now carry fp= (the uTLS fingerprint) — the parsers read it, the emitters used to drop it. WARP IPv6 endpoints keep their brackets and stay parseable.

Technical / Internal

  • core/config/subscription/node_parser_core.go: userinfo is taken verbatim from parsedURL.User (no second QueryUnescape); an out-of-range port returns a node-level error. node_parser_ssh.go: the same double-decode removed from query values (private_key, passphrase, host_key, client_version). xray_outbound_convert.go: the Xray-JSON REALITY path now gates on isValidRealityPublicKey and normalizes short_id, mirroring node_parser_transport.go.
  • core/services/file_service.go: log rotation renamed sing-box.log out from under the open ChildLogFile descriptor — after the first mid-session rotation all sing-box output went to .old, which never rotates again (unbounded growth). The fd is now closed before the rename and reopened after.
  • core/process_service.go: Start() re-checks RunningState under CmdMutex — two concurrent starts could launch two sing-box processes and orphan the first Cmd handle.
  • core/core_downloader.go: installBinary rolls back to .old on a failed io.Copy; a successful DownloadCore invalidates the session core-version cache (new InvalidateInstalledCoreVersionCache). core/wintun_downloader.go: dllPath is assigned only after a fully successful copy.
  • core/debugapi: PATCH /state/* and /settings/user-agent load-modify-save cycles are serialized behind dedicated mutexes; the Serve goroutine snapshots httpSrv/listener under the mutex (Stop() nils them → nil-deref crash); GET /state/* maps state.ErrNotFound to 404, matching /state/full; DELETE /traffic/sessions/{id} takes the deletion fact from DeleteSession (now returning bool) instead of a count-delta heuristic that raced concurrent stop/clear.
  • api/: pingTestURL / pingTestAllConcurrency are mutex-guarded (UI writes, ping workers read); api-log writes happen under the read lock instead of after releasing it.
  • core/warp/{account,masque}.go: share-URI hosts are built with net.JoinHostPort, which re-brackets IPv6.
  • Lint sweep: staticcheck 49 → 5 findings. gofmt on 7 files; S1000/S1003/S1011/S1021 simplifications; ST1005/ST1006/ST1019 (incl. the duplicate core/state import in business/sources.go); dead code in clash_remote_ui.go (parsed the secret as a port and discarded it); the srstag stability test rewritten via locals (SA4000); deprecated Fyne APIs replaced (SelectTabSelect, OnChangedOnSelected, Wrapping = TextTruncateTruncation, PlaceHolderColor, Window.ClipboardApp.Clipboard); dead never-nil checks removed in core/template/substitute.go and dns_preset_bundled.go, with the actual contract documented (SA4023).
  • Docs: docs/API.md synced with the code — examples refreshed to v1.2.2 / lx.5, the non-existent "Copy snapshot" button removed, Debug API UI references point at Settings (not Diagnostics), /debug/snapshot shows errors as an object with an omitempty note, the traffic rolling buffer documented as 60s (last= clamps at 10 min), wiring path corrected to core/debugapi_wiring.go. docs/TRAFFIC_PROFILER.md: router.find_processroute.find_process.

RU

Основное

  • Ядро обновлено до sing-box-lx 1.14.0-lx.5. Те же build-теги (with_clash_api + with_xhttp + with_awg + with_quic/with_gvisor + with_naive_outbound; в Windows-архивах по-прежнему лежит libcronet.dll). Приезжает «энергетическая ревизия» форка: приостановленные WireGuard/AmneziaWG-туннели больше не «воскресают» от выключения/включения экрана или смены сети; idle-suspend больше не рвёт активную передачу (перед засыпанием тик смотрит на счётчик установленных TCP и дельту трафика); покинутая urltest-группа перестаёт пинговать — и будить — свои ноды. Две новые опциональные настройки: route.lx_idle_suspend_reachable (второе, более длинное окно простоя, после которого засыпают даже достижимые endpoint'ы, просыпаясь лениво на следующем дозвоне) и urltest.passive_check (недавний успешный дозвон засчитывается как доказательство живости и экономит целые циклы пинга). (SPEC 072)
  • Пароли из подписок больше не портятся. Парсер дважды декодировал userinfo — url.Parse уже percent-декодирует его, а лишний проход превращал + в пароле trojan / hysteria2 / tuic / ssh / socks / naive в пробел и повторно декодировал последовательности %XX. Такие ноды доходили до сервера и молча не проходили аутентификацию. Сильнее всего страдал SSH: тело PEM-ключа полно +, поэтому любой inline-ключ приезжал битым.
  • Одна битая нода больше не ломает весь конфиг. Порт вне диапазона 1..65535 теперь деградирует только свою ноду, а не доезжает до config.json и не валит sing-box check целиком. Та же защита теперь работает для мусорного REALITY public_key из Xray-JSON подписок — в URI-пути она есть с v1.1.7, в JSON-пути её не было; такая нода откатывается на обычный TLS с предупреждением.
  • Выключенное автообновление уважается при старте. При отключённом автообновлении стартовый проход (через 30 секунд после запуска) всё равно фетчил все подписки один раз за запуск.
  • Неудачное обновление ядра больше не уничтожает рабочее. Если копирование обрывается на середине, прежний бинарь восстанавливается из .old, а не остаётся обрезанный файл; частично распакованный wintun.dll больше не ставится как валидный. После успешной установки ядра Core Dashboard показывает новую версию без перезапуска лаунчера.
  • Round-trip share-URI сохраняет больше полей ноды. Экспорт Hysteria2 / TUIC / AnyTLS теперь несёт fp= (uTLS-отпечаток) — парсеры его читали, а эмиттеры теряли. IPv6-endpoint'ы WARP сохраняют квадратные скобки и остаются парсибельными.

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

  • core/config/subscription/node_parser_core.go: userinfo берётся как есть из parsedURL.User (без второго QueryUnescape); порт вне диапазона возвращает ошибку уровня ноды. node_parser_ssh.go: то же двойное декодирование убрано из query-значений (private_key, passphrase, host_key, client_version). xray_outbound_convert.go: REALITY-путь Xray-JSON проверяет isValidRealityPublicKey и нормализует short_id — как в node_parser_transport.go.
  • core/services/file_service.go: ротация переименовывала sing-box.log из-под открытого дескриптора ChildLogFile — после первой ротации в течение сессии весь вывод sing-box уходил в .old, который больше никогда не ротируется (неограниченный рост). Теперь дескриптор закрывается до rename и переоткрывается после.
  • core/process_service.go: Start() перепроверяет RunningState под CmdMutex — два конкурентных старта могли запустить два процесса sing-box и потерять трекинг первого.
  • core/core_downloader.go: installBinary откатывается на .old при неудачном io.Copy; успешный DownloadCore сбрасывает сессионный кэш версии ядра (новый InvalidateInstalledCoreVersionCache). core/wintun_downloader.go: dllPath присваивается только после полностью успешного копирования.
  • core/debugapi: циклы load-modify-save в PATCH /state/* и /settings/user-agent сериализованы отдельными мьютексами; goroutine Serve снимает httpSrv/listener под мьютексом (Stop() их обнуляет → nil-deref и падение); GET /state/* маппит state.ErrNotFound в 404 — как /state/full; DELETE /traffic/sessions/{id} берёт факт удаления из DeleteSession (теперь возвращает bool), а не из разницы счётчиков, которая гонялась с конкурентным stop/clear.
  • api/: pingTestURL / pingTestAllConcurrency под мьютексом (пишет UI, читают ping-воркеры); запись в api-лог идёт под read-локом, а не после его освобождения.
  • core/warp/{account,masque}.go: хосты share-URI собираются через net.JoinHostPort, который возвращает скобки IPv6.
  • Прогон линтеров: staticcheck 49 → 5 находок. gofmt на 7 файлах; упрощения S1000/S1003/S1011/S1021; ST1005/ST1006/ST1019 (в т.ч. двойной импорт core/state в business/sources.go); мёртвый код в clash_remote_ui.go (парсил секрет как порт и выбрасывал результат); тест стабильности srstag переписан через локальные переменные (SA4000); заменены deprecated Fyne API (SelectTabSelect, OnChangedOnSelected, Wrapping = TextTruncateTruncation, PlaceHolderColor, Window.ClipboardApp.Clipboard); убраны мёртвые never-nil проверки в core/template/substitute.go и dns_preset_bundled.go с документированием фактического контракта (SA4023).
  • Документация: docs/API.md синхронизирован с кодом — примеры обновлены на v1.2.2 / lx.5, убрана несуществующая кнопка «Copy snapshot», UI-ссылки Debug API указывают на Settings (а не Diagnostics), /debug/snapshot показывает errors как объект с пометкой про omitempty, rolling buffer профайлера описан как 60 секунд (last= клампится до 10 минут), исправлен путь wiring-файла на core/debugapi_wiring.go. docs/TRAFFIC_PROFILER.md: router.find_processroute.find_process.

Migration notes

Миграция не нужна: формат config.json и state.json не менялся. После обновления лаунчера зайдите в Core Dashboard → Download/Reinstall, чтобы забрать ядро 1.14.0-lx.5; новые энергетические настройки (route.lx_idle_suspend_reachable, urltest.passive_check) — opt-in, по умолчанию поведение прежнее.

No migration needed: neither config.json nor state.json changed shape. After updating the launcher, use Core Dashboard → Download/Reinstall to pull core 1.14.0-lx.5; the new energy knobs (route.lx_idle_suspend_reachable, urltest.passive_check) are opt-in and default to the previous behaviour.

Don't miss a new singbox-launcher release

NewReleases is sending notifications on new releases.