release v1.2.2
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.2The 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
- Download:
singbox-launcher-v1.2.2-macos.zip - Extract the ZIP file
- Remove quarantine attribute (required):
xattr -cr "singbox-launcher.app" && chmod +x "singbox-launcher.app/Contents/MacOS/singbox-launcher"
- Double-click
singbox-launcher.appto 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)
- Download:
singbox-launcher-v1.2.2-win64.zip - Extract the ZIP file to a folder, for example:
C:\Program Files\singbox-launcher\ - Run
singbox-launcher.exefrom that folder- You may need administrator rights to install to Program Files
- The launcher will automatically download
sing-boxandwintun.dllon first launch
Windows 7 (x86, legacy)
- Download:
singbox-launcher-v1.2.2-win7-32.zip - 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
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 shiplibcronet.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) andurltest.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.Parsealready percent-decodes it, and the extra pass turned a+in a trojan / hysteria2 / tuic / ssh / socks / naive password into a space and decoded literal%XXsequences 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..65535now degrades just that node instead of reaching config.json and failingsing-box checkfor everything. The same guard now covers junk REALITYpublic_keyvalues 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
.oldinstead of leaving a truncated file in place; a partially extractedwintun.dllis 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 fromparsedURL.User(no secondQueryUnescape); 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 onisValidRealityPublicKeyand normalizesshort_id, mirroringnode_parser_transport.go.core/services/file_service.go: log rotation renamedsing-box.logout from under the openChildLogFiledescriptor — 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-checksRunningStateunderCmdMutex— two concurrent starts could launch two sing-box processes and orphan the first Cmd handle.core/core_downloader.go:installBinaryrolls back to.oldon a failedio.Copy; a successfulDownloadCoreinvalidates the session core-version cache (newInvalidateInstalledCoreVersionCache).core/wintun_downloader.go:dllPathis assigned only after a fully successful copy.core/debugapi:PATCH /state/*and/settings/user-agentload-modify-save cycles are serialized behind dedicated mutexes; theServegoroutine snapshotshttpSrv/listenerunder the mutex (Stop()nils them → nil-deref crash);GET /state/*mapsstate.ErrNotFoundto 404, matching/state/full;DELETE /traffic/sessions/{id}takes the deletion fact fromDeleteSession(now returningbool) instead of a count-delta heuristic that raced concurrent stop/clear.api/:pingTestURL/pingTestAllConcurrencyare 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 withnet.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/stateimport inbusiness/sources.go); dead code inclash_remote_ui.go(parsed the secret as a port and discarded it); the srstag stability test rewritten via locals (SA4000); deprecated Fyne APIs replaced (SelectTab→Select,OnChanged→OnSelected,Wrapping = TextTruncate→Truncation,PlaceHolderColor,Window.Clipboard→App.Clipboard); dead never-nil checks removed incore/template/substitute.goanddns_preset_bundled.go, with the actual contract documented (SA4023). - Docs:
docs/API.mdsynced 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/snapshotshowserrorsas an object with an omitempty note, the traffic rolling buffer documented as 60s (last=clamps at 10 min), wiring path corrected tocore/debugapi_wiring.go.docs/TRAFFIC_PROFILER.md:router.find_process→route.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целиком. Та же защита теперь работает для мусорного REALITYpublic_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сериализованы отдельными мьютексами; goroutineServeснимает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 (SelectTab→Select,OnChanged→OnSelected,Wrapping = TextTruncate→Truncation,PlaceHolderColor,Window.Clipboard→App.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_process→route.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.