Release v2.3.0
Downloads
macOS (Universal) - Supports both Apple Silicon and Intel
Option 1: Installation Script (Recommended)
Install with a single command (version v2.3.0):
curl -fsSL https://raw.githubusercontent.com/Leadaxe/singbox-launcher/develop/scripts/install-macos.sh | bash -s -- v2.3.0The 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.3.0-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)
Option 1: Installer (Recommended)
- Download:
singbox-launcher-v2.3.0-win64-setup.exe - Run it and confirm the administrator prompt once. The launcher goes to
C:\Program Files\singbox-launcher, your data to%LOCALAPPDATA%\singbox-launcher;sing-box.exe,wintun.dll, the config template and Mesa3D are included, nothing is downloaded on first launch- Tasks: desktop shortcut, Start with Windows, software OpenGL (Mesa3D) for RDP / VMs without a GPU
- Installing over a previous version keeps your data; uninstall from Settings → Apps asks whether to remove the data too
- The installer is not code-signed yet: if SmartScreen shows "Windows protected your PC", click More info → Run anyway
Option 2: Portable ZIP
- Download:
singbox-launcher-v2.3.0-win64.zip- or
singbox-launcher-v2.3.0-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 your account can write to, for example
D:\Tools\singbox-launcher\— settings stay next to the program (portable.txt)- Under
C:\Program Files\the launcher keeps its data in%LOCALAPPDATA%\singbox-launcherinstead
- Under
- Run
singbox-launcher.exefrom that folder — no administrator rights, no UAC prompt- TUN needs administrator rights: Start offers Restart as administrator or Switch to proxy mode
- The regular archive downloads
sing-boxandwintun.dllon first launch
Windows 7 (x86, legacy)
- Download:
singbox-launcher-v2.3.0-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
- Runs as administrator (UAC prompt on every start), settings stay next to the program (
portable.txt); no installer and no daemon service
Linux Support
⚠️ Linux build temporarily unavailable - мы ищем тестировщика для ручного тестирования перед включением автоматической сборки.
Checksums
See checksums.txt for SHA256 checksums of all files.
Release notes — v2.3.0 (2026-09-26)
EN
Minor release on top of v2.2.0. Core sing-box-lx 1.14.2-lx.4 (was 1.14.2-lx.2), contract 1.1.80 (was 1.1.53).
The release is about nodes. The registry is now the only source of node rules (SPEC 142): links, subscriptions, sing-box and Xray configs and backups are judged by the same rules on every input, and warnings that used to reach only the log now show up in the node list and the build report. The config template gets one canonical walker (SPEC 143). WireGuard and AmneziaWG nodes show their live state in Servers and can be switched off and on without editing the config.
Highlights
WireGuard / AmneziaWG: live state and on/off switch
- State in the Servers list. The subtitle of a WireGuard/AmneziaWG node row ends with its state in the running core —
up,sleep,freed,startingoroff— refreshed every 10 s while the tab is visible. A node switched off by hand shows a greyoffinstead of a red Error: it is not broken. - Disable WireGuard / Enable WireGuard in the row's menu switches the node off and on in the running core without editing the config.
- WireGuard section in the Info window of the node: its state (up, asleep, released, not started yet, disabled…) with the time since its last traffic, refreshed every 10 s, and a Disable / Enable button next to the state.
- The switch lasts until the core restarts or the config is applied. Works in daemon mode and on remote machines (the classic core has no such API); needs core 1.14.2-lx.4.
Config template: one canonical walker (SPEC 143)
- Config template: one canonical walker now substitutes
@var/#ifeverywhere — the main config,on_change.set, preset bodies and template DNS servers (SPEC 143). Template warnings (undeclared variable, unknown directive, number out of range or not a number, a preset rule / rule set / DNS rule / DNS server dropped because a setting is empty) now come first in the build report Summary instead of the log; they never block Save. Language changes: an array in a branch of a conditional array element is spliced one level up (nest with[[...]]);["@name"]is no longer collapsed into a scalar;optionswork with any type exceptbool,options_open: trueallows free input,enumistextwith closedoptions; a value becomes a number only by its declaredtype: int(clamped to [0, 65535], a non-number stays a string) — the hard-coded list of numeric variable names is gone, so custom templates that declare MTU or a port astype: textnow get a string;@runtime.platform/@runtime.arch/@runtime.targetare allowed as values inconfig. Settings draws a dropdown fromoptions, a combo box withoptions_open, and checksintfields as numbers. Contract 1.1.68–1.1.70.
Nodes: registry rules on every input (SPEC 142)
- Hysteria2 links from 3x-ui with gecko obfuscation no longer lose the packet size range:
minPacketSize/maxPacketSizeare read, andsecurity=tlsis accepted silently (contract 1.1.54). - VLESS nodes with Vision flow over a transport (xhttp, ws, …) keep
flowwhen the node has VLESS Encryption: the flow was stripped before and the server dropped the connection (contract 1.1.55). - XHTTP
uplink_data_placement=header|cookiewithoutmodegetsmode: packet-upfilled in on every input, sing-box JSON included; with another explicit mode the placement is dropped instead of the core rejecting the whole config.body/autoplacements are no longer touched: links withuplinkDataPlacement=bodyandmode=stream-oneused to get a false warning. Shadowsocksplugin_optswithoutpluginis dropped (contract 1.1.56). - A node link is now recognised by the registry itself:
amneziawg://links paste as servers instead of being taken for a subscription URL, and a new registry scheme needs no code change to be recognised. sing-box JSON import judgestlsof a wrong shape and a missingserver/server_portby registry rules on every input: the node gets atype_invalid/field_missing/port_invalidcode instead of a silent fix or a bare rejection. The AmneziaWG obfuscation form saves only what the pipeline emits; a body the pipeline cannot build is not written, as in the JSON editor. - Hysteria (v1) nodes whose
obfsarrives as a Hysteria2-style object{type, password}keep their obfuscation on every input — sing-box import, a JSON body pasted by hand, a restored backup: the password becomes the obfuscation string, with an info notice (obfs_object_flattened); an object without a password is removed withobfs_password_missing. MASQUE bodies with the legacy flatnetwork/sni/skip_cert_verifykeys have them removed with anunknown_keynotice on every input, not only on sing-box import and not silently (contract 1.1.57). - The global TLS anti-DPI option (ClientHello/record fragmentation) now also applies to MASQUE nodes: the core fragments on the h2 leg and ignores it on h3. The set of schemes it applies to comes from the registry, not a list in code. The "Add WireGuard" form no longer accepts an MTU above 1500 (the registry limit; values above were silently dropped later) and, with Allowed IPs left empty, the node gets both
0.0.0.0/0and::/0. Invalid keys, MTU or keepalive are reported with the registry's own message. The AmneziaWG obfuscation form accepts any masquerade domain the core accepts (a name without a dot, underscores) and checks it by the registry; expliciti1/i2next to the masquerade are removed with afield_conflictnotice instead of silently; there is no longer a made-up ceiling on jc/jmin/jmax. The Tailscale form reports "LAN access without an exit node" instead of dropping the checkbox silently. Node subtitles show the transport the same way in the list and in the preview: only schemes with a transport showtcp, MASQUE withoutvhttpshowsauto(contract 1.1.58). - REALITY nodes: one registry rule instead of two contradicting ones. A REALITY node without uTLS (a hand-written sing-box body, an Xray config without
fingerprint) keeps REALITY and gets uTLS switched on with an info notice (reality_utls_enabled); before, the registry removed REALITY and the build then patched uTLS back in — the node went out as plain uTLS to a REALITY server. The fingerprintrandomunder REALITY is pinned tochromeon every input with an info notice (reality_fp_random_pinned): the core resolvesrandomonce per start to one of five fingerprints, and two of them (edge, ios) lack the hybrid key exchange modern REALITY servers require. Nodes already stored in the state and manualconfig_jsonobjects get the same fix at build time by the same registry rule, with the code in the log instead of the former silent patch; a node whose REALITY is dropped for a broken public key keeps its fingerprint. A chain that strips uTLS while one of its positions is a REALITY node is no longer excluded: stripping uTLS is turned off for that chain, withchain_strip_utls_on_realityin the log (contract 1.1.61). - AmneziaWG nodes with
jmingreater thanjmaxno longer make the core refuse the whole config: on every input both bounds are removed with afields_order_invalidnotice and the node keeps working. Tailscale advertised routes are fixed up by the registry on every input: a bare address becomes a host route, host bits are cleared (192.168.10.5/24→192.168.10.0/24), and a default route (0.0.0.0/0,::/0) is removed with atailscale_default_route_advertisednotice (the exit-node setting is for that) — before, only the add-server form checked this. In Xray configs, a chain hop that dials out through afreedomwithfragmentgets ClientHello fragmentation itself; before, it was put on the chain owner, whose TLS runs inside the hop's tunnel.vpn://links are recognised regardless of letter case (contract 1.1.63). - MASQUE nodes: TLS settings the core does not use for MASQUE —
alpn,ech,reality,kernel_tx/kernel_rx— are removed on every input with an info notice (masque_tls_field_ignored); TLS fragmentation is removed only onvhttp: h3(masque_tls_fragment_h3) and the global anti-DPI option no longer adds it there, while h2/auto keep it. VLESS from an Xray config withencryption: "None"(capital N) is now rejected withvless_encryption_invalid, as on the link and sing-box inputs, instead of silently losing the encryption layer (contract 1.1.64). - Warnings that used to reach only the log now reach the user, translated from the registry: an imported sing-box config whose detour chain was cut (target missing, a group, a loop, too deep) marks the node with ⚠; a group left without members appears as an unsupported record with
group_empty; a subscription cut by the node limit, a broken chain (core without chains, missing hop, nested chain, malformed), a chain whose uTLS strip was turned off for REALITY, a chain left out of a Direction it runs through, and a lost detour target of a source (one line per source and target, with the number of excluded nodes) are shown in the build report by their registry code. - WireGuard nodes with a listening port (
listen_port, from a.confor a link) that you route through a hop no longer make the core refuse the whole config: the build removes the listening port with adetour_with_listen_portnotice in the build report and keeps the hop. A node routed through itself or through a loop of hops is reported by code (source_detour_self,source_detour_cycle). An SSH entry without a user in an imported sing-box config getsrootwritten explicitly with an info notice (ssh_user_default) — the core used root anyway (contract 1.1.65). - An imported group (selector / urltest / Xray balancer) that lost some of its members now shows ⚠ with the number of missing members right in its row of the node list (
group_member_missing), next to the pool size; before, the loss was only a line in the subscription update status. The notice survives backup export/import (contract 1.1.66).
Amnezia profiles and link names
- The Origin of a node from an Amnezia
vpn://profile is now a complete.conf. The node keeps its own.conftext as its origin, with the container values written into it: the MTU from the profile (when[Interface]has none) and the profile DNS addresses instead of$PRIMARY_DNS/$SECONDARY_DNS. Regen from raw rebuilds the node from that text alone, without thevpn://link; tags do not change. Nodes imported earlier keep working as before (contract 1.1.72); a subscription refresh rewrites their origin. Avpn://line inside a list of links (in a subscription, pasted into Add or into the Source field of Add server, or a.vpnfile) now gives every WireGuard/AmneziaWG container of the profile, each with its own.conforigin — the same nodes as a subscription made of that one link (contract 1.1.80). - Links whose
sni=carries a label instead of a host name (Germany, an emoji) now get the server address as SNI for AnyTLS and Hysteria2, as the registry prescribes. The notice about the ignored WireGuard link DNS shows the address list decoded (1.1.1.1, 1.0.0.1, not1.1.1.1,+1.0.0.1). Avmess://link whose payload is neither a v2rayN JSON object nor a legacymethod:uuid@host:portlink — e.g. junk instead of base64 — is rejected withform_unrecognizedinstead of an unclear failure (contract 1.1.73, 1.1.74). - A broken byte in a node name (for example, a cp1251 label from an aggregator, percent-encoded in the link fragment or raw inside a base64 vmess payload) no longer costs the node: the node is imported and the damaged spot in its name shows as
�, instead of being cut out or rejecting the whole link (contract 1.1.74).
Backup: folder and subscription fold
- The fold of a folder or subscription (one group instead of its nodes in Directions) now travels in the backup file in the same form as in the app state —
replace {mode, tag, auto}inside the folder/subscription record — instead of the formerfold+fold_tagpair. The old form is not read and not migrated: an older backup that carries a fold in thefoldform loses it on import (the keys are dropped with an "unknown field" warning, rules that pointed at the fold group arrive disabled), and the fold has to be set up again (contract 1.1.78, 1.1.79).
Fixed
- The Overview tab of a node inside a folder now describes the node itself — its type, tag, enabled state and its own storage record — instead of the folder ("Type: Group", the folder's name and "Nodes: 5").
- Preset DNS servers now honour their own
#enablecondition: with DNS override switched off, therussianpreset's Yandex DoH/DoT servers no longer end up inconfig.json(they were emitted regardless; only the preset's rules respected the switch).
Migration notes
Custom templates
- A template value becomes a number only when its variable declares
type: int(clamped to [0, 65535]). The hard-coded list of numeric variable names is gone: if your ownwizard_template.jsondeclares an MTU, a port or another numeric setting astype: text,config.jsonnow gets a string and the core may refuse it — declare the variabletype: int. The bundled template already does. - Also check custom templates that relied on the old walker: an array in a branch of a conditional array element is now spliced one level up (nest with
[[...]]), and["@name"]is no longer collapsed into a scalar. Template warnings are listed first in the build report Summary.
Core
- The WireGuard state and Disable / Enable WireGuard need core
sing-box-lx 1.14.2-lx.4; the launcher offers to update the core. In daemon mode and on remote machines it is the daemon's core that has to be lx.4.
Technical / Internal
- Core pinned to sing-box-lx 1.14.2-lx.4 (was 1.14.2-lx.3): the core reports the state of every WireGuard/AmneziaWG node and can switch a node off and on at runtime (fork SPEC 106). No configuration or state migration. gRPC stubs (
internal/daemonpb) synced to the fork at 1.14.2-lx.4. - Core pinned to sing-box-lx 1.14.2-lx.3 (was 1.14.2-lx.2). Vision (
flow: xtls-rprx-vision) now runs on top of VLESS Encryption on any transport,xhttpincluded (fork SPEC 105, sing-box-lx#29); together with contract 1.1.55 such nodes connect instead of being dropped by the server. No configuration or state migration. - Contract 1.1.56 primitives: body
conditionaccepts value predicates on paths (in/not_in/scalar, mapperwhengrammar), relations takewhen, mapperwhengets the$valueselector (own source value; a miss is a silent skip, noon_when_false). Sanitizer rules: relations and conditions see fields of the object still being walked; an empty string or anabsent_valuesliteral counts as a missing key fordefault_when; an empty string does not satisfyany_set. All engine-level, no scheme names in code. - SPEC 142 wave 1: hand-written copies of registry rules removed — sing-box type → scheme table and the endpoint-scheme table (now
registry.NodeSchemeForSingboxTypebysingbox_type+sources,registry.ProtocolKind), the direct-link prefix list (linkmap.SelectURI), the Xray socks hop builder (the engine'ssocks#xraysection),tlsshape and address pre-checks of sing-box import, dead uTLS/WS-early-data/Vision/MASQUE helpers. Achaintype from a foreign config does not become a node (the scheme has nosources). Registry data unchanged, no contract bump. - SPEC 142 wave 2: the hand-written sing-box import sanitizer (
SanitizeSingboxOutboundMap,singbox_sanitize.go) is removed. Its last two rules are registry data: new engine primitiveon_invalid: unwrap(an object wrapper of a neighbouring dialect → the value of itskeymember,else_codeotherwise; no scheme names in code) onhysteria.body.obfs, and the MASQUE legacy keys moved tobody.skipped. Contract 1.1.57. - SPEC 142 wave 3: chain
stripcatalogue, AmneziaWGip=quic→idrequirement and LDH pattern ofid, Tailscaleexit_node_allow_lan_access→exit_nodebecame registry data; the chain form, the AWG/WireGuard/Tailscale/WARP forms, TLS transforms, node transport labels and the MASQUEvhttpcoercion read the registry (Field.AllowedForScheme,FieldAllowed,FieldStrings,FieldsWithBuildTag). The linkmap engine no longer names the transport-drop code or its parameter. Contract 1.1.58. - SPEC 142 wave 4: new registry field attribute
role(credential|private_key). A node's credential (ParsedNode.UUID) is read by role the same way on every input (registry.Credential) — three hand-written per-scheme copies removed; link and JSON inputs no longer disagree (ssh/naive/socks/http JSON now carry the user name, an ss link the password instead of the cipher, WireGuard/MASQUE links no longer put the private key there; no user-facing reader depends on it). Share links that need confirmation (private key inside) are decided by theprivate_keyrole instead of a type table in code; same set (ssh, WireGuard, MASQUE). Contract 1.1.59. - SPEC 142 wave 5: node-level core gates are registry data. New attributes
on_core_unsupported(protocol body, field, range form),range_form(awg_rangeonly),levels/level/level_mark. One generic gate (nodeflow.NodeCoreRefusal) replaces the three per-protocol probes (naive, Tailscale, AmneziaWG 3.x) and the hand-written AWG 3.x field list (awg3.goremoved); the app only reports the core's build tags. The AmneziaWG level label and the form's "remove AmneziaWG" (ranged keepalive collapsed to its lower bound) read the registry. Same nodes are dropped with the same codes; the build report now shows one line per code, titled from the registry ("AmneziaWG 3.x: core too old — 2 node(s) skipped: …"). Contract 1.1.60. - SPEC 142 wave 6: REALITY ↔ uTLS is one registry rule executed by the sanitizer on every input. New primitives:
requires[].set(a missing required neighbour is materialised with a code instead of dropping the field), fieldcoerce_when(conditional replacement of a valid value), both judged on the finished body after the walk, codes placed inbody.orderposition; chain strip key attributeon_hop_required {action: unstrip}judged vianodeflow.StripBlocked(a hop body that would get the path back fromrequires … set). Bodies the build does not sanitize (frozen state bodies, manualconfig_json) get only the repair rules vianodeflow.Repairs. Removed:EnforceRealityFingerprint(node_parser_transport.godeleted),build.HealRealityFingerprintsand its build step,NodeUsesReality/ChainStripsUTLS/ChainRealityConflict. Contract 1.1.61. - SPEC 142 wave 8: registry pointers to code (
refs.go,impl,goof warning codes, notes) brought up to date — 141refs.goentries pointed at removed hand-written parsers/emitters or past-the-end line numbers (53 left, aspath.go[:Name]), ~335 notes point at where the rule runs now; new linterTestRegistryCodeRefsResolvechecks that every referenced file exists and every named identifier is declared in it (Contract job, triggered by any.gochange). Registry rules unchanged. Contract 1.1.62. - SPEC 142 wave 7: the last hand-written node rules are registry data. Body primitives: relation
ordered(+ actiondrop), fielditem_forbidden, normalizecidr_masked, protocol-bodyexit_capable_when(ParsedNode.IsExitCapablereads it). Mapper primitives: sourcescontext.<path>(value from the container unwrapper) andref.<as>.<path>(a neighbour in the document found by a record'sderef {key, as}), recordsubstitute,whenoperatortype_of. Removed:applyXrayFreedomFragmentand its helpers,amneziaPrepareConf(MTU fromlast_config,$PRIMARY_DNSsubstitution — now wireguardconfrecords), thevpn://literal (theamnezia_linksource-kind detect,linkmap.MatchesUnwrap), the jmin ≤ jmax check of the AWG form,parseTailscalePrefixList. Sanitizer norm (PARSING_PRINCIPLES §6.2): a field removed by a value or relation rule counts as absent for the following relations and conditions (before,ibstayed afteripwas removed). Contract 1.1.63. - SPEC 142 wave 9 (campaign tails): dead code removed —
subscription/hysteria2_ports.go(its rules are registry data),percentEncodeUserinfoSpaces, the unreadParsedNode.UUID/ParsedJump.UUIDfields andregistry.Credential; newregistry.Registry.FieldAllowedOn(would the sanitizer keep this field in this body) used by the build's TLS transforms; gendocs prints relationwhenconditions. - SPEC 142 wave 10b: new
registry.Registry.YieldsTo— fields of a finished body that aconflicts {with}relation would drop once a build-written managed field is present; the build asks it right after settingdetour. Warninggonotes point at where wave 10a delivers the codes;body_dialect_unrecognizedremoved (no event, dead constant);direction_filter_matched_nothingregistered (it was used by the Directions corpus only). Contract 1.1.65. - SPEC 142 wave 11a:
warnings[]allowed on a group node (kind=auto) in state and backup; backup import keeps a group's warnings as is (no body to recount from).group_member_missingmoved from a subscription-level code to the group node (subscription.markGroupMemberMissing, one entry per group, counts add up across parse steps). Corpus +1body/singbox/group_member_missing. Contract 1.1.66. - SPEC 142 wave 12: a group member that drops out at config build (missing, disabled or excluded) now reaches the build report with code
group_member_dropped(registry text, both languages) instead of a bare line. Contract text fixed: acore_rejectedentry is exported as is but dropped on backup import —enabledcomes from the file, import never turns the node on, the receiving core judges it again when the user enables it (the importer already worked this way). Contract 1.1.67. - Contract 1.1.68 → 1.1.80. Template language of SPEC 143 — splice one level, no
["@name"]collapse,optionsindependent oftypewithoptions_open,@runtime.*in values (1.1.68);template_*codes reach the build report Summary astemplate_degraded(1.1.69), plus the newtemplate_fragment_droppedfor a preset or template DNS-server fragment left without a required field (1.1.70); SPEC 143 closed on desktop (1.1.71); the origin of a node fromvpn://is a self-contained.conf(wg_ini, 1.1.72); the corpus brought to the norm (LxBox task 560, 1.1.73); invalid UTF-8 after base64 is replaced with U+FFFD instead of failing the unpack, a run of broken bytes giving one U+FFFD (1.1.74, 1.1.75); the parsing rules documentCANON.mdis renamed tocontract/docs/PARSING_PRINCIPLES.md("Source parsing principles"), gains an introductory section 0 on its terms and becomes the entry point to the contract, "envelope" → "parse result" throughout — no behaviour, codes, registry or corpus changes (1.1.76); the contract gets a human entry point —ARCHITECTURE.md(one page: registry, engine, the four parsing steps on a vless example, a diagram),GLOSSARY.mdand an introductory block in every main document (1.1.77); the fold of a folder or subscription is thereplace {mode, tag, auto?}field of its record, one form in the state, the backup file and LxBox, the state keyreplace.strategyrenamed toreplace.auto(the old key is still read); LxBox keeps the selector genus of imported groups, its backup corpus overrides dropped (1.1.78); the oldfold+fold_tag(1.0) andfold(0.x) are no longer read or migrated — unknown keys withbackup_unknown_field, the fold is set up again; theselector_as_autocode retired from the registry,dartrefs of twelve protocols point aturi_pipeline.dart:parseLinkViaPipeline(1.1.79); avpn://line in a list of links is the whole container, an invalidsni=(a label) yields to the next source of the chain —servername=— before the server address, anytls/hysteria2 readservername=, the unusedbackup_replace_tag_derivedcode retired, the last twodartrefs fixed, and two build codes for a folder/subscription fold:replace_tag_conflict(the fold tag is already declared by a Direction, another fold above or a template tag — the fold is not built, the config is; a node with the same name gets a-2suffix instead of breaking the config) andreplace_group_empty(a folded source with no enabled nodes), the fold editor warns about a taken tag (1.1.80).
Specifications: SPEC 142 — registry over local rules · SPEC 143 — canonical template walker
RU
Минорный релиз поверх v2.2.0. Ядро sing-box-lx 1.14.2-lx.4 (было 1.14.2-lx.2), контракт 1.1.80 (было 1.1.53).
Релиз про узлы. Реестр теперь единственный источник правил об узлах (SPEC 142): ссылки, подписки, конфиги sing-box и Xray и бэкапы судятся одними правилами на любом входе, а предупреждения, которые раньше доходили только до лога, видны в списке узлов и в отчёте сборки. Шаблон конфигурации получил один канонический обходчик (SPEC 143). Узлы WireGuard и AmneziaWG показывают своё живое состояние в Servers, их можно выключить и включить без правки конфига.
Основное
WireGuard / AmneziaWG: живое состояние и выключатель
- Состояние в списке Servers. Подзаголовок строки WireGuard/AmneziaWG-узла заканчивается его состоянием в работающем ядре —
up,sleep,freed,startingилиoff, — обновляется раз в 10 с, пока вкладка на экране. Узел, выключенный вручную, показывает сероеoffвместо красного Error: он не сломан. - Disable WireGuard / Enable WireGuard в меню строки выключает и включает узел в работающем ядре без правки конфига.
- Секция WireGuard в окне Info узла: состояние (поднят, усыплён, освобождён, ещё не поднимался, выключен…) и время с последнего трафика, обновляется раз в 10 с, и кнопка Выключить / Включить рядом с состоянием.
- Переключение действует до перезапуска ядра или применения конфига. Работает в daemon-режиме и на удалённых машинах (у classic-ядра такого API нет); нужно ядро 1.14.2-lx.4.
Шаблон конфигурации: один канонический обходчик (SPEC 143)
- Шаблон конфигурации:
@var/#ifтеперь везде подставляет один канонический обходчик — главный конфиг,on_change.set, тела пресетов и шаблонные DNS-серверы (SPEC 143). Предупреждения шаблона (незаявленная переменная, неизвестная директива, число вне диапазона или не число, выпавшие из-за пустой настройки правило / набор правил / DNS-правило / DNS-сервер пресета) идут первыми строками в «Итог» отчёта сборки, а не в лог; Save они не блокируют. Язык: массив в ветке условного элемента массива вливается на уровень выше (вложение — через[[...]]);["@name"]больше не сворачивается в скаляр;optionsработают при любом типе, кромеbool,options_open: trueразрешает свой ввод,enum=textс закрытымиoptions; в число значение превращается только по объявленномуtype: int(clamp [0, 65535], не-число остаётся строкой) — зашитый список числовых имён переменных удалён, поэтому в пользовательских шаблонах, где MTU или порт объявленыtype: text, теперь уедет строка;@runtime.platform/@runtime.arch/@runtime.targetразрешены в значенияхconfig. В Settings список рисуется поoptions, приoptions_open— комбобокс, полеintпроверяется как число. Контракт 1.1.68–1.1.70.
Узлы: правила реестра на любом входе (SPEC 142)
- Ссылки hysteria2 из 3x-ui с gecko-обфускацией больше не теряют диапазон размеров пакетов:
minPacketSize/maxPacketSizeчитаются,security=tlsпринимается молча (контракт 1.1.54). - VLESS-узлы с Vision поверх транспорта (xhttp, ws и др.) сохраняют
flow, если у узла есть VLESS Encryption: раньше flow снимался, и сервер рвал соединение (контракт 1.1.55). - XHTTP
uplink_data_placement=header|cookieбезmodeполучаетmode: packet-upна любом входе, включая sing-box JSON; при явном другом режиме placement снимается, а не роняет весь конфиг ядра.body/autoбольше не трогаются: ссылки сuplinkDataPlacement=bodyиmode=stream-oneраньше получали ложное предупреждение. У Shadowsocksplugin_optsбезpluginснимается (контракт 1.1.56). - Ссылку узла опознаёт сам реестр: ссылки
amneziawg://вставляются серверами, а не принимаются за URL подписки, и новая схема реестра опознаётся без правки кода. Импорт sing-box JSON судитtlsнегодной формы и отсутствиеserver/server_portправилами реестра, как и остальные входы: узел получает кодtype_invalid/field_missing/port_invalidвместо молчаливой правки или голого отказа. Форма обфускации AmneziaWG пишет только выход конвейера; тело, которое конвейер собрать не может, не записывается — как в редакторе JSON. - Узлы Hysteria (v1), у которых
obfsприходит объектом в духе Hysteria2{type, password}, сохраняют обфускацию на любом входе — импорт sing-box, JSON-тело, вставленное руками, восстановленный бэкап: пароль становится строкой обфускации, узел получает информационное предупреждение (obfs_object_flattened); объект без пароля снимается сobfs_password_missing. У тел MASQUE устаревшие плоские ключиnetwork/sni/skip_cert_verifyснимаются с предупреждениемunknown_keyна любом входе, а не только при импорте sing-box и не молча (контракт 1.1.57). - Глобальная анти-DPI опция TLS (фрагментация ClientHello и записей) теперь применяется и к узлам MASQUE: ядро фрагментирует на h2-ноге и игнорирует на h3. Набор схем берётся из реестра, а не из списка в коде. Форма «Добавить WireGuard» больше не принимает MTU выше 1500 (граница реестра; большее значение раньше молча снималось позже), а при пустом Allowed IPs узел получает и
0.0.0.0/0, и::/0. Негодные ключи, MTU или keepalive называются текстом кода реестра. Форма обфускации AmneziaWG принимает любой домен маскировки, который принимает ядро (без точки, с подчёркиванием), и проверяет его по реестру; явныеi1/i2рядом с маскировкой снимаются с предупреждениемfield_conflict, а не молча; выдуманного потолка jc/jmin/jmax больше нет. Форма Tailscale сообщает «доступ к LAN без exit node» вместо молчаливого сброса галки. Подпись транспорта узла одинакова в списке и в превью:tcpпоказывают только схемы с транспортом, MASQUE безvhttp—auto(контракт 1.1.58). - REALITY-узлы: одно правило реестра вместо двух противоречащих. REALITY-узел без uTLS (sing-box-тело, написанное руками, конфиг Xray без
fingerprint) сохраняет REALITY, uTLS включается с информационным предупреждением (reality_utls_enabled); раньше реестр снимал REALITY, а сборка потом дописывала uTLS — узел уходил голым uTLS на REALITY-сервер. Отпечатокrandomпод REALITY на любом входе закрепляется какchromeс информационным предупреждением (reality_fp_random_pinned): ядро разворачиваетrandomодин раз при запуске в один из пяти отпечатков, и у двух из них (edge, ios) нет гибридного обмена ключами, которого требуют современные REALITY-серверы. Узлы, уже сохранённые в состоянии, и ручные объектыconfig_jsonполучают ту же правку на сборке по тому же правилу реестра, с кодом в логе вместо прежней молчаливой; узел, чей REALITY снят за битый публичный ключ, сохраняет свой отпечаток. Цепочка, снимающая uTLS при REALITY-узле на одной из позиций, больше не исключается: снятие uTLS для неё отключается, в логе —chain_strip_utls_on_reality(контракт 1.1.61). - Узлы AmneziaWG с
jminбольшеjmaxбольше не заставляют ядро отвергать весь конфиг: на любом входе обе границы снимаются с предупреждениемfields_order_invalid, узел работает. Анонсируемые маршруты Tailscale приводит реестр на любом входе: голый адрес становится маршрутом хоста, биты хоста обнуляются (192.168.10.5/24→192.168.10.0/24), маршрут по умолчанию (0.0.0.0/0,::/0) снимается с предупреждениемtailscale_default_route_advertised(для этого есть настройка выхода) — раньше это проверяла только форма добавления узла. В конфигах Xray звено цепочки, которое выходит наружу черезfreedomсfragment, получает фрагментацию ClientHello само; раньше её получал владелец цепочки, чей TLS идёт внутри туннеля звена. Ссылкиvpn://опознаются без учёта регистра (контракт 1.1.63). - Узлы MASQUE: TLS-настройки, которые ядро у MASQUE не использует —
alpn,ech,reality,kernel_tx/kernel_rx, — снимаются на любом входе с info-уведомлением (masque_tls_field_ignored); фрагментация TLS снимается только приvhttp: h3(masque_tls_fragment_h3), и глобальная анти-DPI опция её там больше не дописывает, на h2/auto остаётся. VLESS из конфига Xray сencryption: "None"(с заглавной) теперь отбраковывается кодомvless_encryption_invalid, как на ссылке и в sing-box, а не теряет слой шифрования молча (контракт 1.1.64). - Предупреждения, которые раньше доходили только до лога, теперь видны пользователю текстом реестра: у импортированного sing-box-конфига с обрезанной цепочкой detour (цели нет, цель — группа, кольцо, слишком глубоко) узел получает ⚠; группа без членов встаёт неразобранной записью с кодом
group_empty; подписка, обрезанная лимитом узлов, несобравшаяся цепочка (ядро без цепочек, нет позиции, вложенная цепочка, нарушенное правило), цепочка, у которой снятие uTLS отключено ради REALITY, цепочка, не взятая в Направление, через которое идёт, и потерянная цель detour источника (одна строка на источник и цель с числом исключённых узлов) показываются в отчёте сборки по коду реестра. - Узлы WireGuard с портом прослушивания (
listen_portиз.confили ссылки), которые вы ведёте через хоп, больше не заставляют ядро отвергать весь конфиг: сборка снимает порт прослушивания с кодомdetour_with_listen_portв отчёте и сохраняет хоп. Узел, ведущийся через самого себя или через кольцо хопов, называется в отчёте кодом (source_detour_self,source_detour_cycle). Запись SSH без пользователя в импортированном конфиге sing-box получает явныйrootс info-кодом (ssh_user_default) — ядро и так подключалось как root (контракт 1.1.65). - Импортированная группа (selector / urltest / балансировщик Xray), потерявшая часть членов, теперь показывает ⚠ с числом потерянных прямо в своей строке списка узлов (
group_member_missing), рядом с размером пула; прежде потеря была только строкой в статусе обновления подписки. Отметка переживает экспорт/импорт бэкапа (контракт 1.1.66).
Профили Amnezia и имена в ссылках
- Origin узла из профиля Amnezia
vpn://теперь полный.conf. Узел хранит происхождением собственный текст.confс перенесёнными в него значениями контейнера: MTU профиля (если в[Interface]его нет) и адреса DNS профиля вместо$PRIMARY_DNS/$SECONDARY_DNS. Regen from raw пересобирает узел из одного этого текста, без ссылкиvpn://; теги не меняются. Узлы, импортированные раньше, работают как прежде (контракт 1.1.72); обновление подписки переписывает их происхождение. Строкаvpn://внутри списка ссылок (в подписке, во вставке в Add или в поле Source формы Add server, в файле.vpn) теперь даёт все WireGuard/AmneziaWG-контейнеры профиля, каждый со своим.confв происхождении, — те же узлы, что подписка из одной этой ссылки (контракт 1.1.80). - Ссылки, у которых в
sni=вместо имени хоста метка (Germany, эмодзи), у AnyTLS и Hysteria2 получают SNI с адреса сервера, как велит реестр. Уведомление о проигнорированном DNS WireGuard-ссылки показывает декодированный список адресов (1.1.1.1, 1.0.0.1, а не1.1.1.1,+1.0.0.1). Ссылкаvmess://, чей пейлоад не JSON-объект v2rayN и не legacy-ссылкаmethod:uuid@host:port— например, мусор вместо base64, — отвергается с кодомform_unrecognizedвместо невнятного отказа (контракт 1.1.73, 1.1.74). - Битый байт в имени узла (например, метка в cp1251 от агрегатора — percent-кодом во фрагменте ссылки или сырыми байтами внутри base64 у vmess) больше не стоит узла: узел импортируется, а место повреждения в имени видно как
�— прежде кусок вырезался или отвергалась вся ссылка (контракт 1.1.74).
Бэкап: свёртка папки и подписки
- Свёртка папки или подписки (одна группа вместо её узлов в Направлениях) едет в файле бэкапа той же формой, что в состоянии приложения, —
replace {mode, tag, auto}внутри записи папки/подписки — вместо прежней парыfold+fold_tag. Прежняя форма не читается и не мигрирует: старый бэкап со свёрткой формыfoldеё теряет (ключи отбрасываются с предупреждением о неизвестном поле, правила на группу свёртки приезжают выключенными) — свёртку настраивают заново (контракт 1.1.78, 1.1.79).
Исправления
- Вкладка Overview у узла внутри папки теперь описывает сам узел — вид, тег, включённость и его собственную запись хранилища, — а не папку («Type: Group», имя папки и «Nodes: 5»).
- Пресетные DNS-серверы теперь подчиняются своему условию
#enable: при выключенном DNS override серверы Yandex DoH/DoT пресетаrussianбольше не попадают вconfig.json(раньше они уезжали всегда, переключатель учитывали только правила пресета).
Что проверить после обновления
Свои шаблоны
- Значение шаблона становится числом, только если переменная объявлена
type: int(clamp [0, 65535]). Зашитого списка числовых имён больше нет: если в вашемwizard_template.jsonMTU, порт или другая числовая настройка объявленыtype: text, вconfig.jsonтеперь уедет строка, и ядро может отвергнуть конфиг — объявите переменнуюtype: int. Штатный шаблон это уже делает. - Проверьте и шаблоны, которые опирались на старый обходчик: массив в ветке условного элемента массива теперь вливается на уровень выше (вложение — через
[[...]]), а["@name"]больше не сворачивается в скаляр. Предупреждения шаблона идут первыми строками в «Итог» отчёта сборки.
Ядро
- Состояние WireGuard и Disable / Enable WireGuard требуют ядра
sing-box-lx 1.14.2-lx.4; лаунчер предложит обновить ядро. В daemon-режиме и на удалённых машинах lx.4 должно быть ядро демона.
Техническое / Внутреннее
- Пин ядра sing-box-lx 1.14.2-lx.4 (было 1.14.2-lx.3): ядро отдаёт состояние каждого WireGuard/AmneziaWG-узла и умеет выключать и включать узел на лету (SPEC 106 форка). Миграции конфига и состояния нет. gRPC-стабы (
internal/daemonpb) синхронизированы с форком на 1.14.2-lx.4. - Пин ядра sing-box-lx 1.14.2-lx.3 (было 1.14.2-lx.2). Vision (
flow: xtls-rprx-vision) теперь работает поверх VLESS Encryption на любом транспорте,xhttpв том числе (SPEC 105 форка, sing-box-lx#29); вместе с контрактом 1.1.55 такие узлы подключаются, а не рвутся сервером. Миграции конфига и состояния нет. - Примитивы контракта 1.1.56:
conditionтела принимает предикаты по значению путей (in/not_in/скаляр, грамматикаwhenмаппера), у связей появилсяwhen, вwhenмаппера — селектор$value(собственное значение записи; промах — молчаливый пропуск безon_when_false). Нормы санитайзера: связи и условия видят поля объекта, обход которого ещё идёт; пустая строка и литералabsent_valuesдляdefault_whenравны отсутствию ключа; пустая строка не выполняетany_set. Всё на уровне движка, без имён схем в коде. - SPEC 142 волна 1: сняты рукописные копии правил реестра — таблица «тип sing-box → схема» и таблица endpoint-схем (теперь
registry.NodeSchemeForSingboxTypeпоsingbox_type+sources,registry.ProtocolKind), список префиксов прямой ссылки (linkmap.SelectURI), сборка socks-звена Xray (секция движкаsocks#xray), предпроверки формыtlsи адреса в импорте sing-box, мёртвые хелперы uTLS / WS early data / Vision / MASQUE. Типchainиз чужого конфига узлом не становится (у схемы нетsources). Данные реестра не менялись, бампа контракта нет. - SPEC 142 волна 2: рукописный санитайзер импорта sing-box (
SanitizeSingboxOutboundMap,singbox_sanitize.go) снят. Два его последних правила стали данными реестра: новый примитив движкаon_invalid: unwrap(объект-обёртка соседнего диалекта → значение его членаkey, иначеelse_code; без имён схем в коде) уhysteria.body.obfsи перенос legacy-ключей MASQUE вbody.skipped. Контракт 1.1.57. - SPEC 142 волна 3: каталог
stripцепочки, требованиеip=quic→idи LDH-шаблонidу AmneziaWG, связь Tailscaleexit_node_allow_lan_access→exit_nodeстали данными реестра; форма цепочки, формы AWG/WireGuard/Tailscale/WARP, трансформы TLS, подписи транспорта и приведениеvhttpMASQUE читают реестр (Field.AllowedForScheme,FieldAllowed,FieldStrings,FieldsWithBuildTag). Движок linkmap больше не называет код отказа транспорта и его параметр. Контракт 1.1.58. - SPEC 142 волна 4: новый атрибут поля реестра
role(credential|private_key). Учётные данные узла (ParsedNode.UUID) читаются по роли одинаково на всех входах (registry.Credential) — три рукописные копии по имени схемы сняты; ссылка и JSON больше не расходятся (у JSON ssh/naive/socks/http теперь имя пользователя, у ссылки ss — пароль вместо шифра, у ссылок WireGuard/MASQUE приватный ключ туда больше не кладётся; пользовательских читателей у поля нет). Какие share-ссылки требуют подтверждения (несут приватный ключ), решает рольprivate_key, а не таблица типов в коде; состав тот же (ssh, WireGuard, MASQUE). Контракт 1.1.59. - SPEC 142 волна 5: узловые гейты ядра — данные реестра. Новые атрибуты
on_core_unsupported(тело протокола, поле, форма-диапазон),range_form(толькоawg_range),levels/level/level_mark. Один общий гейт (nodeflow.NodeCoreRefusal) заменил три пробы по протоколу (naive, Tailscale, AmneziaWG 3.x) и рукописный список полей AWG 3.x (awg3.goснят); приложение сообщает только теги сборки ядра. Подпись уровня AmneziaWG и кнопка формы «убрать AmneziaWG» (диапазонный keepalive схлопывается в нижнюю границу) читают реестр. Снимаются те же узлы с теми же кодами; в отчёте сборки — одна строка на код с заголовком из реестра («AmneziaWG 3.x: ядро слишком старое — пропущено узлов: 2 — …»). Контракт 1.1.60. - SPEC 142 волна 6: REALITY ↔ uTLS — одно правило реестра, его исполняет санитайзер на всех входах. Новые примитивы:
requires[].set(недостающий требуемый сосед материализуется с кодом вместо снятия поля), атрибут поляcoerce_when(условная замена годного значения) — оба судятся по готовому телу после обхода, коды встают на место поbody.order; атрибут ключа каталога strip цепочкиon_hop_required {action: unstrip}, судитnodeflow.StripBlocked(тело звена получило бы путь обратно отrequires … set). Тела, которые сборка не прогоняет через санитайзер (замороженные тела состояния, ручнойconfig_json), получают только правила-починки черезnodeflow.Repairs. Сняты:EnforceRealityFingerprint(файлnode_parser_transport.goудалён),build.HealRealityFingerprintsи его шаг сборки,NodeUsesReality/ChainStripsUTLS/ChainRealityConflict. Контракт 1.1.61. - SPEC 142 волна 8: указатели реестра на код (
refs.go,impl,goу кодов, заметки) приведены к правде — 141 записьrefs.goвела в удалённые рукописные парсеры/эмиттеры или на строки за концом файла (осталось 53, в формепуть.go[:Имя]), ~335 заметок указывают, где правило исполняется сейчас; новый линтерTestRegistryCodeRefsResolveпроверяет, что каждый файл существует, а названное имя в нём объявлено (джоба Contract, триггер — любой.go). Правила реестра не менялись. Контракт 1.1.62. - SPEC 142 волна 7: последние рукописные правила об узлах — данные реестра. Примитивы тела: связь
ordered(+ действиеdrop), атрибут поляitem_forbidden, normalizecidr_masked, атрибут тела протоколаexit_capable_when(его читаетParsedNode.IsExitCapable). Примитивы маппера: источникиcontext.<путь>(значение от распаковщика контейнера) иref.<as>.<путь>(сосед по документу, найденный записью сderef {key, as}), атрибут записиsubstitute, операторwhentype_of. Сняты:applyXrayFreedomFragmentс хелперами,amneziaPrepareConf(MTU изlast_config, подстановка$PRIMARY_DNS— теперь записи секцииconfwireguard), литералvpn://(detect вида источникаamnezia_link,linkmap.MatchesUnwrap), проверка jmin ≤ jmax в форме AWG,parseTailscalePrefixList. Норма санитайзера (PARSING_PRINCIPLES §6.2): поле, снятое правилом значения или связи, для последующих связей и условий отсутствует (раньшеibоставался после снятияip). Контракт 1.1.63. - SPEC 142 волна 9 (хвосты кампании): снят мёртвый код —
subscription/hysteria2_ports.go(его правила — данные реестра),percentEncodeUserinfoSpaces, нечитаемые поляParsedNode.UUID/ParsedJump.UUIDиregistry.Credential; новыйregistry.Registry.FieldAllowedOn(оставил бы санитайзер поле в этом теле) — им пользуются TLS-трансформы сборки; gendocs печатает условиеwhenу связей. - SPEC 142 волна 10b: новый
registry.Registry.YieldsTo— поля готового тела, которые связьconflicts {with}сняла бы, когда managed-поле, дописанное сборкой, уже есть; сборка спрашивает его сразу после проставленияdetour. Поляgoкодов указывают, где их доставляет волна 10a;body_dialect_unrecognizedснят (события нет, константа мёртвая);direction_filter_matched_nothingзарегистрирован (им пользовался только корпус Направлений). Контракт 1.1.65. - SPEC 142 волна 11a:
warnings[]разрешены у узла-группы (kind=auto) в состоянии и бэкапе; импорт бэкапа берёт записи группы как есть (тела нет — пересчитывать нечем).group_member_missingперенесён с уровня тела подписки на узел-группу (subscription.markGroupMemberMissing, одна запись на группу, число складывается по шагам разбора). Корпус +1body/singbox/group_member_missing. Контракт 1.1.66. - SPEC 142 волна 12: член группы, выпавший при сборке конфига (узла нет, он выключен или исключён), доходит до отчёта сборки с кодом
group_member_dropped(текст из реестра, оба языка) вместо голой строки. Поправлен текст контракта: записьcore_rejectedэкспортируется как есть, но при импорте бэкапа снимается —enabledберётся из файла, импорт узел не включает, ядро приёмника судит его заново, когда человек включит узел (импорт так уже и работал). Контракт 1.1.67. - Контракт 1.1.68 → 1.1.80. Язык шаблонов SPEC 143 — сплайс на один уровень, без коллапса
["@name"],optionsнезависимо отtypeиoptions_open,@runtime.*в значениях (1.1.68); кодыtemplate_*доходят до «Итога» отчёта сборки видомtemplate_degraded(1.1.69), плюс новыйtemplate_fragment_droppedдля фрагмента пресета или шаблонного DNS-сервера, оставшегося без обязательного поля (1.1.70); SPEC 143 закрыта на desktop (1.1.71); origin узла изvpn://— самодостаточный.conf(wg_ini, 1.1.72); корпус приведён к норме (задача LxBox 560, 1.1.73); невалидный UTF-8 после base64 заменяется U+FFFD, а не роняет раскрытие, серия битых байтов даёт один U+FFFD (1.1.74, 1.1.75); документ о правилах разбораCANON.mdпереименован вcontract/docs/PARSING_PRINCIPLES.md(«Принципы разбора источников»), получил вводный раздел 0 о терминах и стал точкой входа в контракт, «конверт» → «результат разбора» по всему контракту — поведение, коды, реестр и корпус не менялись (1.1.76); у контракта появился вход для человека —ARCHITECTURE.md(одна страница: реестр, движок, четыре шага разбора на примере vless, схема),GLOSSARY.mdи вводный блок в каждом основном документе (1.1.77); свёртка папки или подписки — полеreplace {mode, tag, auto?}её записи, одна форма в состоянии, файле бэкапа и у LxBox, ключ состоянияreplace.strategyпереименован вreplace.auto(старый читается); LxBox сохраняет род selector у импортированных групп, его override корпуса бэкапа сняты (1.1.78); прежниеfold+fold_tag(1.0) иfold(0.x) больше не читаются и не мигрируют — неизвестные ключи сbackup_unknown_field, свёртку настраивают заново; кодselector_as_autoвыведен из реестра,dart-ссылки двенадцати протоколов ведут наuri_pipeline.dart:parseLinkViaPipeline(1.1.79); строкаvpn://в списке ссылок — контейнер целиком, негодныйsni=(метка) уступает сперва следующему источнику цепочки —servername=, — и лишь потом адресу сервера, anytls/hysteria2 читаютservername=, неиспользуемый кодbackup_replace_tag_derivedвыведен, две последниеdart-ссылки поправлены, и два кода сборки для свёртки папки/подписки:replace_tag_conflict(тег свёртки уже объявлен Направлением, свёрткой выше по списку или тегом шаблона — свёртка не собирается, конфиг собирается; узел с тем же именем получает суффикс-2, а не ломает конфиг) иreplace_group_empty(у свёрнутого источника нет включённых узлов), редактор свёртки предупреждает о занятом теге (1.1.80).
Спецификации: SPEC 142 — реестр важнее локальных правил · SPEC 143 — канонический обходчик шаблона