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

3 hours ago

Release v2.0.2

Downloads

macOS (Universal) - Supports both Apple Silicon and Intel

Option 1: Installation Script (Recommended)

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

curl -fsSL https://raw.githubusercontent.com/Leadaxe/singbox-launcher/develop/scripts/install-macos.sh | bash -s -- v2.0.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-v2.0.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-v2.0.2-win64.zip
    • or singbox-launcher-v2.0.2-win64-full.zipfull 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.0.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.

Release notes — v2.0.2

EN

Patch release on top of v2.0.1. Core sing-box-lx 1.14.1-lx.8, contract 1.1.52.

Everything here is about subscriptions that looked broken while the file itself was fine, and about nodes that arrived looking healthy and could never connect. In most cases the problem was not a field inside a node but what the container turned out to be, or a property that was dropped without a word.

Fixed

  • An expired subscription now says so instead of handing you one dead server. Provider panels put their notice at the top of the subscription as a syntactically valid link — Remnawave writes vless://…@0.0.0.0:1#⚠ Subscription expired with a zero uuid, 3x-ui writes socks://127.0.0.1:1080 — and on an expired or depleted account that line is the only entry in the file. It used to be imported as a real server, so a subscription that had run out looked like a working one with a single node. The banner is now recognised and the provider's own text after # is shown as the reason.
  • A vpn:// link holding a plain WireGuard config is read. Such a link can carry either an Amnezia profile package or the text of a wg-quick / AmneziaWG .conf file. The second shape went down the compressed-profile path, where the first four bytes of the INI were read as a declared length, and the error said "declared uncompressed size out of range" — pointing at the wrong thing entirely. Missing = padding, which provider panels strip, is accepted too.
  • amneziawg:// links work. The scheme is now a third spelling alongside wg:// and awg://. The same nodes already arrived next to them through vpn://; only the scheme name was missing.
  • A subscription that is a single Xray configuration is read. One configuration object rather than a list used to go to the sing-box branch, and every entry was refused with "missing type". It now takes the same path as an array — a single config is an array of one.
  • Xray configs: WireGuard, SOCKS and HTTP outbounds are imported at last — such a node used to disappear entirely, reported as an unsupported protocol.
  • Xray configs: a node asking for the kcp or quic transport is now rejected with a clear reason. Before, it was imported as a plain TCP node that looked fine and could never connect.
  • Xray configs: alpn, mux, WebSocket and HTTP headers, transport timeouts, TLS versions, cipher suites, certificates and ECH settings are no longer dropped. A node whose server only speaks h2 or h3 failed to connect without alpn; a server requiring multiplexing refused single connections.
  • A node camouflaging plain TCP with an HTTP header is now rejected with a reason instead of arriving broken. The link form was mapped to the sing-box HTTP/2 transport — a different protocol on the wire — and the server expecting camouflage dropped the handshake; the JSON form lost the setting silently and the node was assembled as plain TCP. Either way it could never connect. none and an empty value mean "no camouflage" and are unaffected.
  • A SOCKS link with its credentials packed in base64 (socks://base64(user:pass)) keeps the password. v2rayN always writes them this way, and the credentials were split on a : that is not there, so the password vanished silently.
  • vmess: XHTTP settings carried in extra are read. The same input parsed fully on a vless node, while on vmess the settings were lost without a word.
  • Hysteria2: the short up / down spellings are read. up=100&down=500 produced two "unknown parameter" warnings and lost the bandwidth limit entirely.
  • Hysteria2 bandwidth written with a unit is kept. Forks write the limit as text (100mbps, 300 Mbps), and a strict numeric read gave no value at all. kbps and bps round down, gbps multiplies by a thousand, an unknown suffix yields nothing.
  • Hysteria2 Salamander obfuscation and its password are kept. The whole block was declared read-only, so the obfuscation type and the secret were lost together and the node could not come up.
  • Hysteria / Hysteria2: a port-hopping range outside 0–65535 or with leading zeros (99999:99999, 00443:00444) is dropped from the node with a warning instead of making the core reject the whole configuration.
  • XHTTP: sessionIDPlacement and sessionIDKey are read in the spelling Xray writes them in, in extra and as flat query parameters (issue #131). The session id used to go into the path — the core's default — while the server was looking for a cookie with a custom key.
  • Routing lines from provider panels are skipped quietly. incy://routing/… and happ://routing/… are commands, not servers. They used to be reported as refusals — a working subscription with eight live nodes showed five errors. They are now skipped with an informational note.
  • A list with one unusable element (for example alpn containing a number) now loses just that element instead of the whole setting, so the working values next to it survive.
  • Long but perfectly valid links are no longer rejected as too long — a VLESS link with a post-quantum key, an Amnezia profile with certificates, an awg:// link, or a vless link with XHTTP extra. In the Configurator a long direct link also brought the whole parse to a halt, because its own 8192-character limit was checked separately from the subscription path's. Both now use the shared contract's limit of 65536.
  • An unknown key nested inside a declared block is reported instead of being lost in silence. Only the top level of an element was checked, so anything inside streamSettings, extra and the like that no source claimed disappeared absolutely quietly. Node bodies are unchanged — this only adds notes.

Technical / Internal

  • Contract 1.1.47 — XHTTP aliases sessionIDPlacement / sessionIDKey in extra and in the flat query (#131); dropped[] now carries the element index and a machine code, including entries that could not be parsed at all (new code form_unrecognized); a group's genus is declared as its sing-box body type; the "a comment containing = is not a node name" rule for .conf moved out of engine code into the registry; server_ports elements for hysteria and hysteria2 are a uint16 pair with no leading zeros.
  • Contract 1.1.48 — source wrappers: payload_forms on the amnezia_link branch (bare_conf), the amneziawg scheme as a third alias of the wireguard section, service_schemes on uri_lines (incy://routing/…, happ://routing/… with the service_record_ignored info code), the xray_config source kind for a single configuration, normalize: bandwidth_mbps for up_mbps / down_mbps, and hysteria2 obfuscation from finalmask.udp[0]. New machine codes for refusals that had none: scheme_unsupported, service_record_ignored, body_dialect_unrecognized.
  • Contract 1.1.49 — the dropped-entry wave renumbered (1.1.48 was taken by the source wrappers): dropped[] index and code, server_ports as uint16, group genus, label.comment.
  • Contract 1.1.50 — the Xray dialect audit (see Fixed). Internally: a JSON array or map in the source now reaches the registry record (previously an array spelling of a field was silently unreadable), and the declared on_invalid of the transport selector is actually executed.
  • Contract 1.1.51 — the direct-link length limit comes from the registry (limits.json max_uri_length) through one CheckURILength, instead of the Configurator's own MaxURILength constant; the refusal carries uri_too_long with length and limit and no longer loses it through the ValidateParserConfig wrapper. Dead clash_yaml_unsupported code removed.
  • Contract 1.1.52 — panel banners as data (banner_targets, address only: 3x-ui's port 1080 is legitimate), decode_requires_separator for base64 SOCKS userinfo (both halves mandatory — without the separator check a bare useridonly decodes into garbage), the extra overlay declared on vmess and read through the nested object Marzban writes, hysteria2 short up/down with bandwidth_mbps, header camouflage over plain TCP as a node drop with its own transport_header_unsupported reason, and unknown keys inside declared containers (silence declared by source paths plus a parent path and nested_quiet; today only streamSettings.sockopt). Three new closed schema attributes.
  • The line-based body corpus runner now goes through the plain body parser: its own line walk returned no refusals at all, so a quiet skip was indistinguishable from a silent loss and a case would have passed green on wrong behaviour.
  • The Windows 7 build is fixed: the banner check used sync.OnceValue, which needs Go 1.21, while the legacy build runs on the Go 1.20 toolchain. The win7guard check that exists to catch exactly this only inspected Request.PathValue among selector calls, so the three sync.Once* wrappers slipped past it; the rule is in place now.
  • golangci-lint 2.12.2 is green on all three operating systems.
  • google.golang.org/grpc bumped to 1.84.0 (#132).

RU

Патч-релиз поверх v2.0.1. Ядро sing-box-lx 1.14.1-lx.8, контракт 1.1.52.

Всё здесь — про подписки, которые выглядели битыми при исправном файле, и про узлы, которые приезжали «рабочими» и не могли соединиться никогда. В большинстве случаев дефект не про поле внутри узла, а про то, чем оказался контейнер, или про свойство, которое терялось молча.

Исправления

  • Истёкшая подписка сообщает об этом, а не отдаёт один мёртвый сервер. Панели провайдеров ставят своё уведомление в начало подписки синтаксически валидной ссылкой — Remnawave пишет vless://…@0.0.0.0:1#⚠ Subscription expired с нулевым uuid, 3x-ui пишет socks://127.0.0.1:1080, — а на истёкшем или исчерпанном аккаунте эта строка в файле единственная. Прежде она импортировалась настоящим сервером, и закончившаяся подписка выглядела рабочей с одним узлом. Теперь баннер распознаётся, а текст провайдера после # показывается причиной.
  • Ссылка vpn:// с обычным конфигом WireGuard читается. Внутри такой ссылки бывает либо пакет-профиль Amnezia, либо сам текст wg-quick / AmneziaWG .conf. Вторая форма уходила в ветку сжатого профиля, первые четыре байта INI читались как объявленная длина, и ошибка «declared uncompressed size out of range» уводила чинить совсем не то. Отсутствие паддинга =, который срезают панели провайдеров, тоже принимается.
  • Ссылки amneziawg:// работают. Схема стала третьим написанием рядом с wg:// и awg://. Те же узлы уже приезжали рядом через vpn:// — не хватало только имени схемы.
  • Подписка из одного конфига Xray читается. Один объект конфига, а не список, уходил в ветку sing-box, и каждая запись отбраковывалась с «missing type». Теперь разбор идёт тем же входом, что массив: одиночный конфиг — массив из одного.
  • Конфиги Xray: outbound'ы WireGuard, SOCKS и HTTP наконец импортируются — прежде такой узел пропадал целиком с диагнозом «протокол не поддержан».
  • Конфиги Xray: узел, запрашивающий транспорт kcp или quic, теперь отбраковывается с внятной причиной. Прежде он импортировался обычным TCP-узлом, который выглядел рабочим и не мог соединиться никогда.
  • Конфиги Xray: больше не теряются alpn, mux, заголовки WebSocket и HTTP, таймауты транспортов, версии TLS, наборы шифров, сертификаты и настройки ECH. Узел, чей сервер говорит только по h2 или h3, без alpn не соединялся; сервер, требующий мультиплексирования, не принимал одиночные соединения.
  • Узел, камуфлирующий чистый TCP HTTP-заголовком, теперь отбраковывается с причиной, а не приезжает сломанным. Ссылочная форма маппилась в транспорт HTTP/2 sing-box — на проводе это другой протокол, и сервер, ждущий камуфляж, обрывал рукопожатие; JSON-форма теряла настройку молча, и узел собирался чистым TCP. В обоих случаях соединиться он не мог. none и пустое значение означают отсутствие камуфляжа и не затронуты.
  • Ссылка SOCKS с логином и паролём в base64 (socks://base64(user:pass)) сохраняет пароль. v2rayN пишет их всегда так, а строка резалась по :, которого в ней нет, и пароль исчезал молча.
  • vmess: настройки XHTTP из extra читаются. Тот же вход у vless разбирался полностью, а у vmess настройки терялись без единого слова.
  • Hysteria2: короткие написания up / down читаются. up=100&down=500 давало два предупреждения «неизвестный параметр» и теряло ограничение полосы целиком.
  • Полоса hysteria2 с единицей измерения сохраняется. Форки пишут ограничение текстом (100mbps, 300 Mbps), и строгое чтение числа не давало значения вовсе. kbps и bps округляются вниз, gbps умножается на тысячу, неизвестный суффикс значения не даёт.
  • Обфускация Salamander у hysteria2 и её пароль сохраняются. Весь блок был объявлен чтением без записи, тип обфускации вместе с секретом терялся целиком, и узел не поднимался.
  • Hysteria / Hysteria2: диапазон прыжков по портам вне 0–65535 или с ведущими нулями (99999:99999, 00443:00444) снимается с узла с предупреждением, а не заставляет ядро отвергнуть весь конфиг.
  • XHTTP: sessionIDPlacement и sessionIDKey читаются в том написании, в котором их пишет Xray, — в extra и плоским параметром query (#131). Session id уходил в path, дефолт ядра, хотя сервер ждал cookie с кастомным ключом.
  • Строки роутинга от панелей провайдеров пропускаются молча. incy://routing/… и happ://routing/… — команды, а не серверы. Прежде они шли отказами: у рабочей подписки с восемью живыми узлами пользователь читал пять ошибок. Теперь пропускаются с информационной пометкой.
  • Список с одним негодным элементом (например число в alpn) теперь теряет только этот элемент, а не всю настройку — рабочие значения рядом с ним остаются.
  • Длинные, но полностью корректные ссылки больше не отбраковываются как слишком длинные — VLESS с постквантовым ключом, профиль Amnezia с сертификатами, ссылка awg://, vless с XHTTP extra. В конфигураторе длинная прямая ссылка вдобавок останавливала весь разбор: собственный предел 8192 символа проверялся отдельно от предела, по которому шли подписки. Теперь у обоих путей общий контрактный предел — 65536.
  • Неизвестный ключ внутри объявленного блока сообщается, а не теряется молча. Проверялся только верхний уровень элемента, и всё, что лежало внутри streamSettings, extra и им подобных и не было названо ни одним источником, исчезало совершенно бесшумно. Тела узлов не меняются — добавляются только пометки.

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

  • Контракт 1.1.47 — алиасы XHTTP sessionIDPlacement / sessionIDKey в extra и в плоском query (#131); dropped[] несёт index элемента и машинный code, включая записи, которые не удалось прочитать вовсе (новый код form_unrecognized); род группы объявлен типом тела sing-box; правило «комментарий с = — не имя узла» для .conf перенесено из кода движка в реестр; элемент server_ports у hysteria и hysteria2 — пара uint16 без ведущих нулей.
  • Контракт 1.1.48 — обёртки источника: payload_forms у ветки amnezia_link (bare_conf), схема amneziawg третьим алиасом секции wireguard, service_schemes у uri_lines (incy://routing/…, happ://routing/… с info-кодом service_record_ignored), вид источника xray_config для одиночного конфига, normalize: bandwidth_mbps у up_mbps / down_mbps, обфускация hysteria2 из finalmask.udp[0]. Машинные коды отбраковкам, которые их не имели: scheme_unsupported, service_record_ignored, body_dialect_unrecognized.
  • Контракт 1.1.49 — волна отбраковок перенумерована (1.1.48 занят обёртками источника): index и code у dropped[], server_ports как uint16, род группы, label.comment.
  • Контракт 1.1.50 — аудит диалекта Xray (см. «Исправления»). Внутреннее: массив и карта в источнике теперь доезжают до записи реестра (прежде массивное написание поля было нечитаемым молча), а объявленный on_invalid селектора транспорта действительно исполняется.
  • Контракт 1.1.51 — предел длины прямой ссылки берётся из реестра (limits.json max_uri_length) одной функцией CheckURILength, а не константой MaxURILength конфигуратора; отказ несёт uri_too_long с длиной и пределом и не теряет его сквозь обёртку ValidateParserConfig. Снят мёртвый код clash_yaml_unsupported.
  • Контракт 1.1.52 — баннеры панелей данными (banner_targets, судится только адрес: у 3x-ui порт 1080 законный), decode_requires_separator для base64-userinfo socks (обе половины обязательны — без проверки разделителя одиночное useridonly декодируется мусором), слой extra объявлен у vmess и читается через вложенный объект, которым его пишет Marzban, короткие up/down у hysteria2 с bandwidth_mbps, камуфляж заголовком поверх чистого TCP — отбраковкой узла со своей причиной transport_header_unsupported, и неизвестные ключи внутри объявленных контейнеров (молчание объявляется путями source плюс путь-родитель и nested_quiet; сегодня только streamSettings.sockopt). Три новых закрытых атрибута схемы.
  • Раннер корпуса построчных тел переведён на чистый парсер тела: свой обход строк отбраковки не отдавал вовсе, тихий игнор был неотличим от молчаливой пропажи, и кейс проходил бы зелёным при неверном поведении.
  • Починена сборка Windows 7: проверка баннеров объявляла список через sync.OnceValue, которой нужна Go 1.21, а легаси-сборка идёт тулчейном Go 1.20. Страж win7guard, который существует ровно для такого случая, среди вызовов-селекторов смотрел ТОЛЬКО Request.PathValue, и все три обёртки sync.Once* проходили мимо него; правило добавлено.
  • golangci-lint 2.12.2 зелёный на всех трёх ОС.
  • google.golang.org/grpc поднят до 1.84.0 (#132).

Don't miss a new singbox-launcher release

NewReleases is sending notifications on new releases.