github Leadaxe/singbox-launcher v0.8.8
release v0.8.8

11 hours ago

Release v0.8.8

Downloads

macOS (Universal) - Supports both Apple Silicon and Intel

Option 1: Installation Script (Recommended)

Install with a single command (version v0.8.8):

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

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-v0.8.8-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-v0.8.8-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-v0.8.8-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.

What's New from 0.8.7

Version 0.8.8 Release Notes


EN

Highlights

  • NaïveProxy (naive+https:// / naive+quic://) support in the subscription parser and share-URI encoder. Parses user/pass, extra-headers, and QUIC vs HTTP/2 transport per the de-facto DuckSoft URI spec; emits a sing-box "type": "naive" outbound with the TLS block limited to server_name (matching sing-box naive capabilities). padding= URI param is logged and ignored (no sing-box equivalent). Right-click → Copy link on a naive node round-trips back to a valid URI (extra-headers keys sorted lexicographically for determinism). Requires sing-box ≥ 1.13.0 built with with_naive_proxy — stock binaries without the build tag will reject the outbound. See SPEC 044 and docs/ParserConfig.mdNaïveProxy.

  • macOS wake-from-sleep re-sync — closes the macOS half of SPEC 011 (Windows + Linux landed in 0.8.7). New internal/platform/power_darwin.go registers with IOKit via IORegisterForSystemPower; the CFRunLoop runs on a runtime.LockOSThread-pinned goroutine. After resume the launcher resets the Clash API HTTP transport, refreshes the proxies list, and re-pings nodes if auto-ping is on — exactly the same flow as Windows / Linux. Sleep transitions are auto-acknowledged via IOAllowPowerChange so the system isn't held in a 30-second timeout waiting for a reply.

Added — Resilience & observability

  • Per-source counts in the Update toast. When at least one subscription source returned an error or silently produced zero nodes, the success toast now reads Config updated: 2/3 source(s) succeeded (1 failed) instead of a blanket "successfully". Silent-empty is treated as failure — from the user's perspective an empty subscription is indistinguishable from a network error. All-sources-OK case still shows the original toast text. Counters live on OutboundGenerationResult (already present in 0.8.7 but not surfaced).
  • Tooltips on Core Dashboard reveal keyboard shortcuts. Hover over Update or the round-arrow Restart button to see "Update subscriptions (⌘+U)" / "Restart sing-box (⌘+R)" — Ctrl on Windows / Linux. New helper platform.ShortcutModifierLabel() resolves the platform-specific symbol; both buttons switched to ttwidget.Button for hover support. Shortcuts themselves were added in 0.8.7 (SPEC 042) — this just makes them discoverable.

Fixed

  • Wizard template — object-form options: [{title, value}] no longer corrupts substituted values. Previously, type: "text" + object-form options rendered as a free-text combo widget (SelectEntry). The display shows the title (e.g. "5m (default)"), but the title→value mapping only fires when the user picks from the dropdown — typing or even selecting could leave the literal title in the entry, which then went straight into config.json. Concretely: picking "5m (default)" for urltest_interval ended up writing "interval": "5m (default)" instead of "5m", and sing-box rejected the config. Fix: at JSON-unmarshal time (TemplateVar.UnmarshalJSON), any options element in object form forces Type = "enum" regardless of the declared type. Strict dropdowns can't be typed into. Legacy plain-string options (["a","b"]) are unaffected — combo with free typing still works for them. Six new unit tests cover the matrix.

Changed — Template defaults

  • URLTest vars in bin/wizard_template.json rewired to match the new rule:
    • urltest_url keeps type: "text", but options are now plain-string URLs (no titles). User sees a combo with presets and can still type a custom test endpoint.
    • urltest_interval and urltest_tolerance are now explicitly type: "enum" — values stay object-form {title, value} (so the dropdown shows "5m (default)" / "100 ms (twitchy)" labels), but the widget is a strict dropdown. No more accidental typing of garbage.

Technical / Internal

  • CI: per-version release-notes file required. The release job no longer concatenates RELEASE_NOTES.md (the repo-level index) into the GitHub Release body — that was leaking sections from older versions and the upcoming-release draft into every release. CI now reads docs/release_notes/<slug>.md (e.g. 0-8-8.md for v0.8.8). If the file is missing, the workflow fails before creating the release with a clear error pointing at the expected path. For prereleases the slug is computed from git describe. Prereleases also get an automatic > ⚠️ Pre-release build banner on top of the body.
  • New runbook docs/RELEASE_PROCESS.md. Canonical procedure for stable and prerelease cuts: pre-flight, develop→main merge, separate tag push, post-tag main→develop merge-back (so the tag stays in develop's ancestry), troubleshooting, copy-paste checklists. Linked from AGENTS.md §4 / §5 / §6.
  • FallbackVersion bumped 1.13.6 → 1.13.11 in core/core_version.go — fallback used when GitHub API is unreachable / rate-limited.
  • SPEC groundwork for v0.9.x — two planning artifacts landed without code changes:
    • SPEC 045 — State / Config Decoupling. Split Wizard Save (writes state.json only) from Build Config (writes config.json from state + outbounds cache), modelled after the LxBox mobile client. Includes LXBOX_NOTES.md (deep-dive of the mobile architecture) and CURRENT_ARCH_NOTES.md (audit of every config.json write-point in the desktop launcher today).
    • SPEC 046 — Pinned Core and Template. Pin a specific sing-box version and a specific wizard_template.json commit per launcher version, so a launcher release doesn't silently float onto an untested sing-box.
  • OutboundGenerationResult counters now populatedTotalSources / SucceededSources / FailedSources are filled by GenerateOutboundsFromParserConfig. UpdateConfigFromSubscriptions now returns (*OutboundGenerationResult, error) instead of just error so callers can craft truthful messages.

Migration notes

  • Custom wizard_template.json files using type: "text" + object-form options: [{title, value}] are silently normalized to type: "enum" at load time. Functionally a strict dropdown. If you actually want a free-text combo with presets, switch the options to plain strings (["a", "b"]).
  • The shipped bin/wizard_template.json URLTest section was rewired (see above). If you maintain a custom template, the change is purely cosmetic on the launcher side — sing-box still receives the same @urltest_url / @urltest_interval / @urltest_tolerance substitutions.

Known issues / deferred for redesign

These items are not in 0.8.8 and are tracked for v0.9+:

  • State / Config decoupling (SPEC 045) — the dirty marker on Update is still semantically smeared (fires on any wizard save, not just source-list edits). The next major release will split state-save from config-build and surface two independent markers (Update * for source-set changes vs. a separate Restart marker for template changes).
  • Auto-update event-driven model — the polling loop in core/auto_update.go is still the old "wake every minute, check timestamp, maybe run" pattern. Documented redesign: typed triggers (onAppStart, onVpnConnected, onPeriodic, onWakeFromSleep) through a single dispatcher, no background goroutine between events.
  • Right-click context menu on the Update buttonSecondaryTapWrap does not route secondary taps into widget.Button; needs a custom RightClickableButton that implements fyne.SecondaryTappable directly.
  • Last-auto-update failure pill on Core Dashboard — wired but not visible in practice; layout / flag-path diagnosis pending.
  • Ping-all button cancel — current design fully disables the button during a run; a click-to-cancel redesign (label swap to "Cancel" / "Cancelling…", pingAllCancelled atomic) is tracked.
  • Pinned core + template per launcher version (SPEC 046) — currently the launcher fetches the latest sing-box and the HEAD of bin/wizard_template.json from a branch. Pinning eliminates floating drift between launcher releases and untested core versions.

RU

Основное

  • Поддержка NaïveProxy (naive+https:// / naive+quic://) в парсере подписок и share-URI энкодере. Парсятся user/pass, extra-headers, выбор транспорта (HTTP/2 vs QUIC) по де-факто спеке DuckSoft; собирается sing-box outbound "type": "naive" с TLS-блоком только server_name (ровно то, что умеет sing-box naive). Параметр padding= логируется и игнорируется (нет соответствия в sing-box). ПКМ → «Copy link» на naive-ноде корректно round-trip-ит обратно в валидный URI (ключи extra-headers сортируются лексикографически — детерминированный round-trip). Требует sing-box ≥ 1.13.0 со сборкой with_naive_proxy — стоковые бинарники без тега отклонят outbound. См. SPEC 044 и docs/ParserConfig.mdNaïveProxy.

  • macOS wake-from-sleep re-sync — закрытие macOS-половины SPEC 011 (Windows + Linux появились в 0.8.7). Новый internal/platform/power_darwin.go регистрируется в IOKit через IORegisterForSystemPower; CFRunLoop крутится на goroutine с runtime.LockOSThread. После резюма лаунчер сбрасывает HTTP-транспорт Clash API, перечитывает список прокси и re-пингует ноды, если включён автопинг — то же поведение, что на Windows / Linux. Sleep-транзишены авто-подтверждаются через IOAllowPowerChange, чтобы система не залипала на 30-секундном таймауте ожидания ответа.

Устойчивость и наблюдаемость

  • Счётчик источников в тосте Update. Если хотя бы один источник вернул ошибку или молча отдал ноль нод, тост успеха пишет Config updated: 2/3 source(s) succeeded (1 failed) вместо общего «successfully». «Молчаливый ноль» считается failure — для пользователя это неотличимо от сетевой ошибки. Полный успех — оставлен старый текст. Счётчики живут в OutboundGenerationResult (присутствовали ещё в 0.8.7, но не отображались).
  • Tooltips на Core Dashboard показывают горячие клавиши. При наведении на Update и круговую стрелку Restart показываются «Обновить подписки (⌘+U)» / «Перезапустить sing-box (⌘+R)» (Ctrl на Windows / Linux). Helper platform.ShortcutModifierLabel() отдаёт платформенный символ; обе кнопки переведены на ttwidget.Button для поддержки hover-а. Сами шорткаты появились в 0.8.7 (SPEC 042) — теперь они discoverable.

Исправлено

  • Шаблон визарда — object-форма options: [{title, value}] больше не портит подставляемые значения. Раньше type: "text" + object-form options рендерился как combo-виджет (SelectEntry) со свободным вводом. Видимый текст — это title (например, «5m (default)»), а маппинг title→value срабатывал только при выборе из дропдауна; ручной ввод или даже клик мог оставить буквальный title в entry, и эта строка уезжала прямо в config.json. Конкретно: выбор «5m (default)» для urltest_interval приводил к "interval": "5m (default)" вместо "5m", и sing-box ругался на конфиг. Фикс: на этапе JSON-unmarshal-а (TemplateVar.UnmarshalJSON) любая object-форма в options принудительно выставляет Type = "enum", независимо от заявленного типа. Строгий дропдаун невозможно ввести вручную. Legacy plain-string options (["a","b"]) не затронуты — combo со свободным вводом для них продолжает работать. Шесть новых юнит-тестов покрывают всю матрицу.

Шаблон по умолчанию

  • URLTest-vars в bin/wizard_template.json переразведены под новое правило:
    • urltest_url остался type: "text", но options теперь plain-string URL-ы (без подписей). Юзер видит combo с пресетами, но всё ещё может вписать свой test-endpoint.
    • urltest_interval и urltest_tolerance теперь явно type: "enum" — значения остаются в object-форме {title, value} (чтобы дропдаун показывал «5m (default)» / «100 ms (twitchy)»), но виджет — строгий дропдаун. Случайно вписать мусор больше нельзя.

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

  • CI: per-version release-notes файл обязателен. Release-job больше не конкатенирует RELEASE_NOTES.md (репо-уровневый index) в тело GitHub Release — это протекало в каждый релиз секции старых версий и черновика следующего. CI теперь читает docs/release_notes/<slug>.md (например 0-8-8.md для v0.8.8). Если файла нет — workflow падает до создания релиза с явной ошибкой, указывающей на ожидаемый путь. Для пререлизов slug вычисляется из git describe. Пререлизы автоматически получают баннер > ⚠️ Pre-release build поверх тела.
  • Новый runbook docs/RELEASE_PROCESS.md. Canonical-процедура для stable и prerelease: pre-flight, мердж develop→main, отдельный пуш тега, обязательный возврат main→develop (чтобы тег остался в истории develop), траблшутинг, чеклисты для копипасты. Ссылки в AGENTS.md §4 / §5 / §6.
  • FallbackVersion 1.13.6 → 1.13.11 в core/core_version.go — fallback используется, когда GitHub API недоступен / rate-limit.
  • Спеки-задел на v0.9.x — два планировочных артефакта без правок кода:
    • SPEC 045 — State / Config Decoupling. Развести Wizard Save (пишет только state.json) и Build Config (собирает config.json из state + кэш outbounds), по образцу мобильного клиента LxBox. В папке: LXBOX_NOTES.md (deep-dive архитектуры мобилки) и CURRENT_ARCH_NOTES.md (карта всех write-point-ов config.json в десктопе).
    • SPEC 046 — Pinned Core and Template. Жёстко закрепить версию sing-box и коммит wizard_template.json за каждой версией лаунчера, чтобы релиз не уплывал на непротестированную версию ядра.
  • Счётчики OutboundGenerationResult теперь заполняютсяTotalSources / SucceededSources / FailedSources пишутся в GenerateOutboundsFromParserConfig. UpdateConfigFromSubscriptions возвращает (*OutboundGenerationResult, error) вместо просто error, чтобы caller мог собрать честное сообщение пользователю.

Миграция

  • Кастомные wizard_template.json с type: "text" + object-form options: [{title, value}] тихо нормализуются в type: "enum" при загрузке. Функционально — строгий дропдаун. Если хочется combo со свободным вводом и пресетами — переключите options в plain-string форму (["a", "b"]).
  • Поставляемый bin/wizard_template.json URLTest-секция переразведена (см. выше). Если вы держите кастомный шаблон — изменение чисто косметическое на стороне лаунчера; sing-box всё равно получает те же подстановки @urltest_url / @urltest_interval / @urltest_tolerance.

Known issues / отложено на редизайн

Не входит в 0.8.8, отслеживается для v0.9+:

  • State / Config decoupling (SPEC 045) — dirty-маркер * на Update всё ещё семантически размазан (срабатывает на любое сохранение визарда, а не только на правки списка источников). Следующий major-релиз разнесёт сохранение state и сборку config и выведет два независимых маркера (Update * для изменения source-set vs. отдельный маркер на Restart для правок шаблона).
  • Event-driven автообновление подписок — polling loop в core/auto_update.go остаётся старого образца («просыпайся каждую минуту, проверь timestamp, может пора»). Запланированный редизайн: типизированные триггеры (onAppStart, onVpnConnected, onPeriodic, onWakeFromSleep) через единый dispatcher, без background-goroutine между событиями.
  • Правый клик по кнопке UpdateSecondaryTapWrap не маршрутизирует secondary-тапы в widget.Button; нужен кастомный RightClickableButton с прямой реализацией fyne.SecondaryTappable.
  • Плитка «Last auto-update failed» на Core Dashboard — реализована, но в практике не показывается; диагностика layout / wiring отложена.
  • Кнопка Ping-all → Cancel — сейчас во время теста кнопка полностью disable; редизайн на click-to-cancel (лейбл «Cancel» / «Cancelling…», атомарный pingAllCancelled) запланирован.
  • Pinned core + template (SPEC 046) — сейчас лаунчер тащит latest sing-box и HEAD ветки bin/wizard_template.json. Жёсткое закрепление избавит от плавающего drift между релизами лаунчера и непротестированными версиями ядра.

Don't miss a new singbox-launcher release

NewReleases is sending notifications on new releases.