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.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-v2.0.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-v2.0.2-win64.zip- or
singbox-launcher-v2.0.2-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
- or
- 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 regular archive downloads
sing-boxandwintun.dllon first launch
Windows 7 (x86, legacy)
- Download:
singbox-launcher-v2.0.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
⚠️ 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 expiredwith a zero uuid, 3x-ui writessocks://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.conffile. 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 alongsidewg://andawg://. The same nodes already arrived next to them throughvpn://; 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 withoutalpn; 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.
noneand 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
extraare read. The same input parsed fully on a vless node, while on vmess the settings were lost without a word. - Hysteria2: the short
up/downspellings are read.up=100&down=500produced 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:
sessionIDPlacementandsessionIDKeyare read in the spelling Xray writes them in, inextraand 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/…andhapp://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
alpncontaining 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 XHTTPextra. 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,extraand 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/sessionIDKeyinextraand in the flat query (#131);dropped[]now carries the elementindexand a machinecode, including entries that could not be parsed at all (new codeform_unrecognized); a group's genus is declared as its sing-box body type; the "a comment containing=is not a node name" rule for.confmoved out of engine code into the registry;server_portselements for hysteria and hysteria2 are auint16pair with no leading zeros. - Contract 1.1.48 — source wrappers:
payload_formson theamnezia_linkbranch (bare_conf), theamneziawgscheme as a third alias of the wireguard section,service_schemesonuri_lines(incy://routing/…,happ://routing/…with theservice_record_ignoredinfo code), thexray_configsource kind for a single configuration,normalize: bandwidth_mbpsforup_mbps/down_mbps, and hysteria2 obfuscation fromfinalmask.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_portsas 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_invalidof 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 oneCheckURILength, instead of the Configurator's ownMaxURILengthconstant; the refusal carriesuri_too_longwith length and limit and no longer loses it through theValidateParserConfigwrapper. Deadclash_yaml_unsupportedcode removed. - Contract 1.1.52 — panel banners as data (
banner_targets, address only: 3x-ui's port 1080 is legitimate),decode_requires_separatorfor base64 SOCKS userinfo (both halves mandatory — without the separator check a bareuseridonlydecodes into garbage), theextraoverlay declared on vmess and read through the nested object Marzban writes, hysteria2 shortup/downwithbandwidth_mbps, header camouflage over plain TCP as a node drop with its owntransport_header_unsupportedreason, and unknown keys inside declared containers (silence declared by source paths plus a parent path andnested_quiet; today onlystreamSettings.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. Thewin7guardcheck that exists to catch exactly this only inspectedRequest.PathValueamong selector calls, so the threesync.Once*wrappers slipped past it; the rule is in place now. golangci-lint2.12.2 is green on all three operating systems.google.golang.org/grpcbumped 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 с XHTTPextra. В конфигураторе длинная прямая ссылка вдобавок останавливала весь разбор: собственный предел 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-lint2.12.2 зелёный на всех трёх ОС.google.golang.org/grpcподнят до 1.84.0 (#132).