Release v2.0.1
Downloads
macOS (Universal) - Supports both Apple Silicon and Intel
Option 1: Installation Script (Recommended)
Install with a single command (version v2.0.1):
curl -fsSL https://raw.githubusercontent.com/Leadaxe/singbox-launcher/develop/scripts/install-macos.sh | bash -s -- v2.0.1The 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.1-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.1-win64.zip- or
singbox-launcher-v2.0.1-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.1-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.1
EN
Patch release on top of v2.0.0. Core sing-box-lx 1.14.1-lx.8, contract unchanged.
Fixed
- Servers tab: sorting the list now changes the visible order immediately while a filter is active. The filtered view was cached without regard to order, so the new order only showed up after clicking a server.
- Remote machines: Save no longer leaves the previous build of
config.jsonin place. The local check could never accept a remote machine's config — itsrule_set[].pathentries point into that machine's file system — so the file was silently not replaced while the log and the dialog said "wrote", and Deploy carried the old build to the machine. - Remote machines: the "core rejected node" check now works for a remote machine's config (rule-set paths are mapped to the local profile for the duration of the check only). A core refusal that is not about a node is shown as an error with the core's own text instead of "Remote config exported".
RU
Патч-релиз поверх v2.0.0. Ядро sing-box-lx 1.14.1-lx.8, контракт без изменений.
Исправлено
- Вкладка Servers: сортировка сразу меняет порядок видимых строк при активном фильтре. Отфильтрованный срез кэшировался без учёта порядка, и новый порядок появлялся только после клика по серверу.
- Удалённые машины: Save больше не оставляет прошлую сборку
config.json. Локальная проверка не могла принять конфиг удалённой машины — путиrule_set[].pathведут в её файловую систему, — файл молча не заменялся, лог и диалог сообщали «wrote», а Deploy уносил на машину старую сборку. - Удалённые машины: страховка «ядро отвергло узел» теперь работает и для конфига удалённой машины (на время проверки пути наборов правил подменяются локальными). Отказ ядра «не про узел» показывается ошибкой с текстом ядра, а не сообщением «Remote config exported».
Everything from v2.0.0 / Всё из v2.0.0
v2.0.1 is a patch on top of v2.0.0; the full v2.0.0 notes are repeated below so they are not lost on the latest-release page.
v2.0.1 — патч поверх v2.0.0; полные заметки v2.0.0 повторены ниже, чтобы не потерялись на странице последнего релиза.
EN
2.0.0 marks the start of a unified ecosystem with LxBox: one data contract (contract/registry + corpus), parsing of share links / Xray JSON / wg-quick .conf and building share links all go through a single registry-driven engine, with the same behaviour on the same subscriptions. This is the start of convergence, not its completion: LxBox v2.25.0 ships on contract 1.1.37, the launcher on 1.1.46 — the gap is additive.
Highlights
- 2.0.0 — one contract with LxBox. Desktop and mobile now read the same protocol tables: a subscription parsed here produces the same node bodies, warning codes and share links as LxBox on the same input. What changed is not a longer feature list but the end of parallel copies that had drifted apart.
- Every node now takes one path into your config: whatever it came from — a share link, a pasted JSON object, a subscription, a backup or a WireGuard
.conf— it is checked against a single set of protocol rules and written out by a single emitter. Before, those rules lived in three separate copies that had drifted apart, so the same broken node could be accepted from a link and rejected from JSON. - A node that lost something now says so. Fields the core would reject are removed with a named reason attached to the node (⚠ in the lists, details in the node card) instead of disappearing silently. Previously a subscription could "update" and quietly strip a node's obfuscation or certificate with nothing to show for it. Three new warning codes name what was dropped: an unrecognised link parameter (
uri_param_unknown), an unrecognised JSON field (json_field_unknown), or an empty password (password_empty). - Shadowsocks default port is now 8388 (was 443), matching the scheme canon and LxBox.
- The Servers list can be narrowed without endless scrolling: a filter window matches names by regex, protocol, transport, source and a latency threshold, with optional emoji chips on Direction names.
- While the core runs, the Core window shows your Tailscale tailnet status — connected peers, your own address, and whether a remote
state_directoryis in play. - One bad server no longer costs you the whole VPN. The core checks a config as a whole and refuses to start on the first node it does not accept — so a single line in a single node of a subscription used to leave you with no connection at all. Now that node is turned off by itself, the config is rebuilt and checked again, and the VPN starts on everything else. Each server turned off this way says why, in the core's own words (✖ on its row, the text in its card), and
config.jsonis only replaced by a config the core has accepted — a rejected build leaves the previous working one on disk. Turn a server back on with its checkbox and the next build will try it again. A bar on the Local tab reports what was turned off and opens the full list; if the count reaches ten, you are asked whether to keep checking or stop. The same check now runs on the Configurator's last step before Save (and when writing a remote machine's config); on macOS daemon mode a rejected real start also names the node and retries. The bar is not brought back after an app restart — the node's own mark still shows why it is off.
Fixed
-
Copying a node's link now writes it the way other clients read it. The link is built from the same protocol rules that read one back, so a node and its link can no longer drift apart. Five things that used to be lost are now carried: keep-alive settings, XHTTP field names in the spelling Xray expects, tuic's zero-RTT flag, spaces inside paths (
%20, not+, which servers were answering with 404), and a hysteria2 node with an empty password — which previously could not be shared at all. TLS is now stated explicitly on vless/trojan/anytls links, and the "skip certificate check" flag is written under the name each protocol's clients actually read. -
An Xray link that spells the XHTTP transport the old way (
type=splithttp) no longer arrives as a plain TCP node with nothing to show for it — the transport and all its settings are kept. -
A tuic link with an empty password is accepted and marked, instead of being dropped. The server will most likely refuse it, and now the node says so rather than looking broken for no reason.
-
A link that carries a node's private key is only copied after you confirm it. SSH nodes with an inline key and WireGuard/AmneziaWG nodes hand out a key that lets anyone connect as you, so "Copy server link", "Copy jump server link" and the bulk export now ask first — once per operation, not once per node. Links with only a password or a UUID are copied as before. An SSH node whose key is a path to a file carries no key in the link and is not affected.
-
An SSH node with an inline private key can be copied as a link at all. It used to fail with "this outbound cannot be turned into a share link"; the link is now written as
?private_key=— the same form the mobile app writes and this launcher already reads back. -
macOS: "Hide app from Dock" now survives a restart. The tray toggle is saved to
bin/settings.json(hide_app_from_dock) and applied on the next launch before the window appears. Only the Dock icon is hidden: the main window still opens at startup (use-trayto start without it). -
Core pinned to sing-box-lx 1.14.1-lx.8: a REALITY
short_idlonger than 16 hex characters no longer crashes the core (it is a config error now), an unknowntuic.udp_relay_modeis rejected at load instead of silently becomingnative, MASQUEstandardprofile withouturigets one clear error, core errors name the node type and tag (initialize outbound[0] vless[proxy-de-1]: …), and a gRPCservice_namestarting with/is taken as a ready-made request path. -
gRPC: a
serviceNamewritten in Xray's absolute-path form (/my-service/Tun,/a/b/Tun,/a/Stream) now reaches the server at that exact path, so a node that used to answer404 Not Foundworks (issue #130). The core reads the leading slash itself and escapes each segment separately; the launcher passes the value through untouched. Without a leading slash nothing changes — it is a service name and the core appends/Tunas before. -
vmess: channel cipher list now matches the core exactly —
aes-128-ctr(which the core never supported and which killed the whole config) is gone,aes-128-cfbis accepted instead of silently falling back toauto. -
xhttp:
mode,seq_placement,session_placement,uplink_data_placement,x_padding_placementandx_padding_methodvalues outside the core's enum are dropped with anxhttp_param_resetwarning instead of being passed through and aborting the whole config. -
A hand-written JSON object added as a source is no longer copied into the config verbatim. A typo in a key used to abort the whole config — leaving you with no VPN at all and no hint which node was at fault; now the unknown key is removed and the node says why.
-
Several node shapes that the core rejects outright no longer reach your config: an empty server address, a WireGuard node with
jminbut nojmax, a WebSocket/HTTP path with broken percent-encoding, and a naive node carrying TLS options naive does not read. Each of these used to abort the entire config. -
AmneziaWG obfuscation survives. The
h1–h4magic headers, the packet-size fields and the WARPreservedtriplet are written in the numeric form the core expects, so an AWG node keeps the obfuscation it was configured with. -
Fields your core is too old for (currently
tls.reality.key_share, which needs 1.14.1-lx.4) are left out when the config is built, and the node keeps working without them. The node itself is unchanged, so the field comes back as soon as you update the core. -
Long but valid share links are accepted again: the launcher's own 8 KB limit is gone in favour of the 64 KB limit the contract defines, which is what the mobile app already used. Amnezia and MASQUE links with full key material were being rejected on the desktop and accepted on the phone.
-
Shadowsocks nodes using the older ciphers the core still supports (
rc4-md5,aes-128/192/256-cfband-ctr,chacha20-ietf,xchacha20) are no longer thrown away. Nine working ciphers were being dropped together with the node, leaving you with neither the node nor an explanation; they now connect and carry a note that the cipher is dated. -
A link spelling a parameter the other way round no longer loses it:
insecurein all nine spellings providers use, hysteria2 bandwidth asup_mbps/down_mbpsas well asupmbps/downmbps. The name table comes from the contract, so both apps read the same link the same way. -
naive+https://secret@hostreads the single credential as the password, matching the spec, the mobile app, and the links this launcher itself produces. A link shared from here used to be read back with the credential in the wrong slot. -
The Xray
ech=parameter is now removed with a stated reason instead of vanishing: it carries another client's key and the handshake cannot succeed with it. Atls.echblock from a sing-box configuration is untouched and still works. -
A junk REALITY public key is reported instead of being dropped in silence, and keys that merely look like base64 (
enabled,true) no longer slip through — the key is checked to be 32 bytes after decoding, which is what the core checks. -
socks4://andsocks4a://links work again — the protocol version is carried in the scheme name, not guessed from the port. -
Windows: the tray icon is sharp again — Fyne was picking a 16-colour thumbnail layer instead of the full icon (#83).
-
Port hopping no longer costs you the whole VPN. A provider who writes the address and the port list as one value (
mport=198.51.100.24:443,20000-30000) used to put the host name inside the port list, and the core refused to load the entire configuration with «bad port range». Now that one entry is dropped with a stated reason and the ranges written correctly keep working. A bogus ALPN entry is handled the same way. -
A REALITY key written with the other base64 alphabet now works. The key is the same 32 bytes either way, but the core decodes this particular field in one spelling only and answered «decode public_key: illegal base64 data» — refusing the whole configuration. The spelling is now normalised on the way in.
-
A subscription whose port ranges are written both in the address and in
mport=(host:443,20000-30000?mport=40000-50000) no longer loses half of them: port hopping used only one of the two lists, and it did so silently — the node connected and looked healthy. -
A subscription packed into base64 twice is now readable. Before, it produced no nodes and no error at all: the launcher mistook the inner layer for plain text and folded the whole subscription into one unreadable element.
-
A subscription file or
.confsaved with a BOM (Windows Notepad writes one) is read normally. The invisible leading character threw off format detection, and the configuration turned into a list of nonexistent links. -
Xray subscriptions that fragment TLS ClientHello through a freedom outbound (
dialerProxy→fragment) no longer lose their servers: the node is kept withtls.fragmentinstead of being dropped as a broken chain hop.
Technical / Internal
- Contract 1.1.46: registry-driven link mapper (SPEC 133), node insurance (SPEC 132), warning copy review in
warnings.json— self-sufficient list subtitles, alignedparamsplaceholders, updated developergopointers. - Linux builds now obtain Wayland header paths from
pkg-configand fall back to X11 when the optional native Wayland/EGL development files are incomplete, fixing local builds on openSUSE (PR #128, issue #127). - New
docs/TROUBLESHOOTING.md: known problems by platform with links to their solutions (first entry — repeatedsystemd-resolvedpassword prompts on Linux, issue #126). - Node bodies are produced by
Sanitize+Emitover the contract registry (contract/registry/**): the per-protocol emitter chain, the TLS/transport field allowlists and the special-case naive filter are gone. Adding a field to a protocol is now a registry edit, not four code edits. - The per-field core gate is table-driven from the registry's
min_core/platforminstead of one probe per field;RealityKeyShareSupportProbeand its cache are removed. Node-level gates (naive/chain/tailscale/AWG3) are unchanged — they drop a node, which the registry does not express. warningsare recomputed once on load for nodes saved before the pipeline. A node's stored body is rewritten only when the sanitizer actually removes or coerces something, and each such rewrite is a WARN line naming the node and the codes.- The contract corpus now asserts the body the launcher stores, not the output of the old emitter, and a new test feeds every corpus case into one config and runs the pinned core's
checkover it. - Share URIs are emitted by the same registry engine that parses them — the per-protocol
shareuri_*.goencoders are gone with the hand-written parsers. - Scheme parsers are mappers now: they translate a link's dialect into the sing-box key paths and decide nothing about values. 21 value rules were deleted as duplicates of the registry, 6 moved into it (
base64_32,hex_only, conditionaladvisory,requires+equals,default_when), 14 stay as genuine dialect translation. Per-scheme parsers shrank to a screen each. - Rules that existed in three or four copies are single now: the
insecurespelling set (six copies), the REALITY fingerprint hint (four), the hysteria v1 bandwidth default (three), the shadowsocks cipher list (three). A link and the same node as JSON produce identical bodies and identical warning codes with identical paths — the corpus pairvless/junk_pair_with_bodyasserts it. - The registry-sync watchdog no longer lies: greping
parse_warnings.gowould have declared a dozen live rules dead once the codes moved. A counter-test now requires every node-field code to have a producer — a Go constant or a registry rule. - A Go 1.21+ construct in shared code is now caught on every pull request instead of at release time. The new guard
tools/win7guardfails the test job onslices/mapsimports, builtinmin/max/clear,rangeover an integer andRequest.PathValue— the things the Windows 7 build's Go 1.20 toolchain cannot compile. It reads the AST and the build tags, so a comment mentioningmin, a method namedclearand thego1.22twin files stay untouched; the same step also runs inbuild-win7. Coverage upload moved tocodecov-action@v5(itsfileinput is gone, the report is named infiles), and the Ubuntu runner is pinned toubuntu-24.04instead of the floating label. - Dead legacy helpers left after the registry-driven link mapper (orphaned URI transport builders,
uri_params.go,node_ref.go,detour_topo.go, legacy JSON transport emitter) removed;LEGACY_AUDITaddresses updated to match. - Contract 1.1.43–1.1.46:
merge: appendon registry entries is executed as list concatenation (deltaD133-41), a doubly wrapped base64 document is unwrapped up to the declared depth (D133-42), a BOM is stripped at source-kind detection (D133-43), and thelabel.value_mapreplacement order plus theon_len_gtsource chain are brought to the norm (D133-44). Counterpart task for LxBox —contract/TASKS_LXBOX.md§39.
RU
2.0.0 — начало единой экосистемы с LxBox: один контракт данных (contract/registry + corpus), разбор ссылок / Xray JSON / wg-quick .conf и сборка share-ссылок идут общим движком от таблиц реестра, одинаковое поведение на одних и тех же подписках. Это начало сближения, не завершение: LxBox v2.25.0 выходит на контракте 1.1.37, лаунчер на 1.1.46, разница аддитивная.
Основное
- 2.0.0 — один контракт с LxBox. Десктоп и телефон читают одни таблицы протоколов: подписка, разобранная здесь, даёт те же тела узлов, коды предупреждений и share-ссылки, что LxBox на том же входе. Изменилось не количество фич, а конец параллельных копий правил, которые разъезжались.
- Узел попадает в конфиг одной дорогой: откуда бы он ни пришёл — ссылкой, вставленным JSON-объектом, подпиской, бэкапом или
.confWireGuard, — его проверяет один набор правил протокола и записывает один эмиттер. Раньше эти правила жили в трёх разошедшихся копиях, и один и тот же битый узел ссылкой принимался, а объектом — нет. - Если у узла что-то сняли, он об этом говорит. Поля, которые ядро отвергает, снимаются с названной причиной на самом узле (⚠ в списках, подробности в карточке) вместо молчаливого исчезновения. Прежде подписка «обновлялась», у узла молча срезали обфускацию или сертификат, и увидеть это было негде. Три новых кода предупреждений называют, что снято: неизвестный параметр ссылки (
uri_param_unknown), неизвестное поле JSON (json_field_unknown) или пустой пароль (password_empty). - Порт Shadowsocks по умолчанию — 8388 (был 443), как в каноне схемы и в LxBox.
- Список Servers можно сузить без бесконечной прокрутки: окно фильтра отбирает по regex имени, протоколу, транспорту, источнику и порогу пинга, с чипами-эмодзи у имён Направлений.
- Пока ядро работает, окно Core показывает статус Tailscale tailnet — подключённые пиры, свой адрес и задействован ли удалённый
state_directory. - Один негодный сервер больше не стоит вам всего VPN. Ядро проверяет конфиг целиком и отказывается стартовать на первом же узле, который не принимает, — одна строка в одном узле подписки оставляла без связи вовсе. Теперь такой узел выключается сам, конфиг пересобирается и проверяется заново, а VPN поднимается на всех остальных. Каждый выключенный так сервер объясняет причину словами ядра (✖ в его строке, текст в карточке), а
config.jsonзаменяется только тем конфигом, который ядро приняло: отвергнутая сборка оставляет на диске предыдущий рабочий. Вернуть сервер — его же чекбоксом, следующая сборка проверит узел заново. О выключенных сообщает плашка на вкладке Local, она же открывает полный список; если их набралось десять, приложение спросит, проверять дальше или остановиться. Та же проверка идёт на последнем шаге Конфигуратора до Save и при записи конфига удалённой машины; в daemon-режиме на macOS отказ реального старта тоже называет узел и повторяет apply. Плашку после перезапуска приложения не восстанавливаем — на самом узле значок и причина остаются.
Исправления
-
Копирование ссылки узла теперь пишет её так, как её читают другие клиенты. Ссылка собирается по тем же правилам протокола, по которым читается, и узел со своей ссылкой больше не расходятся. Пять потерь исправлены: настройки keep-alive, имена полей XHTTP в написании Xray, флаг zero-RTT у tuic, пробелы в путях (
%20, а не+— на него серверы отвечали 404) и узел hysteria2 с пустым паролем, которым прежде нельзя было поделиться вовсе. TLS у ссылок vless/trojan/anytls теперь указывается явно, а флаг «не проверять сертификат» пишется тем именем, которое читают клиенты каждого протокола. -
Ссылка Xray со старым написанием транспорта XHTTP (
type=splithttp) больше не приезжает голым TCP-узлом без объяснений — транспорт и все его настройки сохраняются. -
Ссылка tuic с пустым паролем принимается с пометкой, а не отбраковывается. Сервер её, скорее всего, отклонит — и теперь узел об этом говорит, а не выглядит сломанным без причины.
-
Ссылка с приватным ключом узла копируется только после подтверждения. У SSH-узла со встроенным ключом и у WireGuard/AmneziaWG в ссылке лежит ключ, по которому подключиться от вашего имени сможет любой, поэтому «Копировать ссылку сервера», «Копировать ссылку узла-перехода» и массовый экспорт сначала спрашивают — один раз на операцию, а не на каждый узел. Ссылки, где есть только пароль или uuid, копируются как раньше. У SSH-узла, где ключ задан ПУТЁМ к файлу, ключа в ссылке нет — его это не касается.
-
SSH-узел со встроенным приватным ключом вообще стало можно скопировать ссылкой. Раньше он отбивался сообщением «этот outbound нельзя преобразовать в share-ссылку»; теперь ключ пишется как
?private_key=— в той же форме, что пишет мобильное приложение и что этот лаунчер уже умеет читать обратно. -
macOS: «Скрыть из Dock» переживает перезапуск. Переключатель в трее сохраняется в
bin/settings.json(hide_app_from_dock) и применяется при следующем запуске до показа окна. Скрывается только иконка Dock: главное окно при старте открывается как обычно (запуск без окна — флагом-tray). -
Ядро закреплено на sing-box-lx 1.14.1-lx.8: REALITY
short_idдлиннее 16 hex больше не роняет ядро паникой (теперь ошибка конфигурации), неизвестныйtuic.udp_relay_modeотвергается при загрузке, а не молча становитсяnative, MASQUE-профильstandardбезuriполучает одну понятную ошибку, ошибки ядра называют тип и тег узла (initialize outbound[0] vless[proxy-de-1]: …), а gRPC-service_name, начинающийся с «/», понимается как готовый путь запроса. -
gRPC:
serviceNameв Xray-форме «абсолютного пути» (/my-service/Tun,/a/b/Tun,/a/Stream) теперь приходит на сервер ровно этим путём, и узел, отвечавший404 Not Found, работает (issue #130). Ведущий «/» разбирает само ядро, экранируя каждый сегмент по отдельности; лаунчер значение не трогает. Без ведущего «/» ничего не меняется — это имя сервиса, и ядро дописывает/Tunкак раньше. -
vmess: набор шифров канала сверен с ядром —
aes-128-ctr, которого ядро не знало и который валил весь конфиг, убран;aes-128-cfbпринимается вместо молчаливого отката кauto. -
xhttp: значения
mode,seq_placement,session_placement,uplink_data_placement,x_padding_placementиx_padding_methodвне набора ядра снимаются с кодомxhttp_param_reset, а не уезжают в конфиг, роняя его целиком. -
Ручной JSON-объект больше не копируется в конфиг дословно. Опечатка в имени ключа валила весь конфиг — человек оставался вообще без VPN и без подсказки, какой узел виноват; теперь неизвестный ключ снимается, а узел объясняет почему.
-
Несколько форм узла, которые ядро отвергает сразу, до конфига больше не доходят: пустой адрес сервера, WireGuard с
jminбезjmax, путь WebSocket/HTTP с битым percent-кодированием и naive с TLS-полями, которых naive не читает. Каждая из них роняла конфиг целиком. -
Обфускация AmneziaWG больше не теряется: магические заголовки
h1–h4, поля размеров пакетов и тройкаreservedу WARP пишутся в числовой форме, которую ждёт ядро, и AWG-узел сохраняет настроенную обфускацию. -
Поля, которых не знает ваше ядро (сейчас это
tls.reality.key_share, ему нужно 1.14.1-lx.4), при сборке конфига опускаются, и узел продолжает работать без них. Сам узел не меняется, так что поле вернётся сразу после обновления ядра. -
Длинные, но валидные ссылки снова принимаются: собственный предел лаунчера в 8 КБ заменён контрактными 64 КБ — теми же, что давно у мобильного приложения. Ссылки Amnezia и MASQUE с полным ключевым материалом отбивались на десктопе и принимались на телефоне.
-
Узлы shadowsocks на старых шифрах, которые ядро по-прежнему принимает (
rc4-md5,aes-128/192/256-cfbи-ctr,chacha20-ietf,xchacha20), больше не выбрасываются. Девять рабочих шифров отбивались вместе с узлом — человек не получал ни узла, ни объяснения; теперь они подключаются с пометкой, что шифр устарел. -
Ссылка, написавшая параметр иначе, больше не теряет его:
insecureво всех девяти написаниях, которыми пользуются панели, полоса hysteria2 и какup_mbps/down_mbps, и какupmbps/downmbps. Таблица имён берётся из контракта, поэтому обе программы читают одну ссылку одинаково. -
naive+https://secret@hostчитает единственный кредентиал как пароль — как велит спека, как читает мобильное приложение и как этот же лаунчер такие ссылки выписывает. Ссылка, отданная отсюда, прежде читалась обратно с кредентиалом не в том поле. -
Параметр
ech=формата Xray снимается с названной причиной, а не исчезает молча: он несёт ключ чужого клиента, и рукопожатие с ним не состоится. Блокtls.echиз конфигурации sing-box не затрагивается и продолжает работать. -
Мусорный публичный ключ REALITY теперь называется, а не снимается молча, и ключи, которые лишь ПОХОЖИ на base64 (
enabled,true), больше не проходят: ключ проверяется на 32 байта после декода — ровно так, как проверяет ядро. -
Ссылки
socks4://иsocks4a://снова работают — версия протокола несётся в имени схемы, а не угадывается по порту. -
Windows: иконка в трее снова чёткая — Fyne брал 16-цветный слой-миниатюру вместо полноцветной (#83).
-
Прыжки по портам больше не стоят всего VPN. Провайдер, записавший адрес и список портов одним значением (
mport=198.51.100.24:443,20000-30000), укладывал имя хоста внутрь списка портов, и ядро отказывалось загрузить весь конфиг с «bad port range». Теперь такая запись снимается с названной причиной, а диапазоны, записанные правильно, продолжают работать. С негодной записью ALPN — так же. -
Ключ REALITY, записанный другим алфавитом base64, теперь работает. Байты ключа в обоих написаниях одни и те же, но ядро декодирует именно это поле в одном написании и отвечало «decode public_key: illegal base64 data», отвергая конфиг целиком. Написание приводится к одному на входе.
-
Подписка, у которой диапазоны портов записаны И в адресе, и в
mport=(host:443,20000-30000?mport=40000-50000), больше не теряет половину: прыжки по портам шли только по одному из двух списков, причём молча — узел подключался и выглядел исправным. -
Подписка, упакованная в base64 ДВАЖДЫ, теперь читается. Прежде из неё не получалось ни одного узла и не появлялось ни одной ошибки: лаунчер принимал внутренний слой за текст и складывал всю подписку в один нечитаемый элемент.
-
Файл подписки или
.conf, сохранённый с меткой BOM (так пишет, например, «Блокнот» Windows), читается как обычно. Невидимый символ в начале сбивал распознавание формата, и конфигурация превращалась в список несуществующих ссылок. -
Xray-подписки с фрагментацией TLS ClientHello через freedom-outbound (
dialerProxy→fragment) больше не теряют серверы: узел сохраняется сtls.fragmentвместо отбраковки как битый хоп цепочки.
Техническое / Внутреннее
- Контракт 1.1.46: движок linkmap по реестру (SPEC 133), страховка узла (SPEC 132), вычитка текстов предупреждений в
warnings.json— самодостаточные подзаголовки в списке узлов, согласованныеparams, обновлённые служебныеgo. - Linux-сборка теперь получает пути к заголовкам Wayland через
pkg-configи использует X11 при неполном наборе опциональных Wayland/EGL-файлов разработки, исправляя локальную сборку в openSUSE (PR #128, issue #127). - Новый
docs/TROUBLESHOOTING.ru.md: известные проблемы по платформам со ссылками на решения (первая запись — повторные запросы пароляsystemd-resolvedна Linux, issue #126). - Тело узла делают
Sanitize+Emitпо реестру контракта (contract/registry/**): per-scheme цепочка эмиттера, allowlist-ы полей TLS и транспорта и частный фильтр naive сняты. Новое поле протокола — правка реестра, а не четырёх мест в коде. - Полевой гейт ядра стал табличным (
min_core/platformреестра) вместо пробы на каждое поле;RealityKeyShareSupportProbeи её кэш удалены. Узловые гейты (naive/chain/tailscale/AWG3) не тронуты — они выбрасывают узел, и реестром это не выражается. warningsразово досчитываются при загрузке у узлов, сохранённых до конвейера. Тело переписывается, только если санитайзер реально что-то снял или привёл, и каждая такая перезапись — строка WARN с тегом узла и кодами.- Корпус контракта сверяет тело, которое лаунчер СОХРАНЯЕТ, а не вывод старого эмиттера; добавлен тест, который собирает все кейсы корпуса в один конфиг и прогоняет
checkядром пина. - Share-ссылки собирает тот же движок реестра, что их читает — эмиттеры
shareuri_*.goушли вместе с рукописными парсерами. - Парсеры схем стали мапперами: переводят диалект ссылки в пути sing-box и не решают ничего о значениях. 21 правило значения снято как дубль реестра, 6 перенесено в реестр (
base64_32,hex_only, условныйadvisory,requires+equals,default_when), 14 осталось настоящим переводом диалекта. Пер-схемные парсеры ужались до экрана каждый. - Правила, лежавшие в трёх-четырёх копиях, стали одним: набор написаний
insecure(шесть копий), подсказка об отпечатке REALITY (четыре), дефолт полосы hysteria v1 (три), список шифров shadowsocks (три). Ссылка и тот же узел объектом дают одинаковые тела И одинаковые коды с одинаковыми путями — это сверяет пара корпусаvless/junk_pair_with_body. - Сторожевой тест реестра перестал врать: грепа
parse_warnings.goпосле переезда кодов хватило бы, чтобы объявить дюжину живых правил мёртвыми. Встречная проверка требует, чтобы у каждого кода полей узла был производитель — константа Go или правило реестра. - Конструкция Go 1.21+ в общем коде теперь ловится на каждом pull request, а не на выпуске. Новый страж
tools/win7guardкрасит джобу тестов на импортslices/maps, builtinmin/max/clear,rangeпо целому иRequest.PathValue— то, что не собирает тулчейн Go 1.20 сборки Windows 7. Он смотрит AST и build-теги, поэтомуminв комментарии, метод с именемclearи файлы-близнецы заgo1.22его не интересуют; тот же шаг стоит и вbuild-win7. Выгрузка покрытия переехала наcodecov-action@v5(входаfileу него нет, файл отчёта называется вfiles), а раннер Ubuntu пригвождён кubuntu-24.04вместо плавающей метки. - Снят мёртвый легаси после перевода разбора на реестр: осиротевшие транспортные хелперы URI,
uri_params.go,node_ref.go,detour_topo.go, эмиттер transport вoutbound_jsonbuilder.go; адреса вLEGACY_AUDITпересажены на живые файлы. - Контракт 1.1.43–1.1.46:
merge: appendу записей реестра исполняется слиянием списков (дельтаD133-41), двойная оболочка base64 раскрывается до объявленного предела (D133-42), BOM снимается на входе определения вида источника (D133-43), порядок заменlabel.value_mapи цепочка источниковon_len_gtприведены к норме (D133-44). Встречная задача LxBox —contract/TASKS_LXBOX.md§39.