github Leadaxe/LxBox v2.23.2
LxBox v2.23.2

latest releases: v2.25.18, v2.25.18-rc.3, v2.25.18-rc.2...
one month ago

L×Box v2.23.2

Tailscale nodes and “node sections”. A sing-box tailscale endpoint is
now a first-class node: it is accepted from JSON without an address, built
with its own state directory, and the new Add server → Tailscale wizard
creates it together with the DNS server, the .ts.net rule and the
100.64.0.0/10 route it needs. Those companion records live inside the
node (sections, in the contract 1.0 record form), show up in Routing and DNS
with a “from node” mark, and follow the node when it is renamed or moved. On
top of that: a route rule can carry several rule-sets, the QR engine is
zxing-cpp 3.1.1, the core is v1.14.0-lx.38 (Tailscale in the AAR, REALITY
against Xray 26.9.8+), a real adaptive icon, and three fixes from 4PDA
reports.

Узлы Tailscale и «секции узла». Endpoint tailscale sing-box стал
полноценным узлом: принимается из JSON без адреса, собирается со своим
каталогом состояния, а новый мастер Add server → Tailscale создаёт его
сразу вместе с DNS-сервером, правилом .ts.net и маршрутом 100.64.0.0/10.
Эта связка живёт внутри узла (sections, форма записей контракта 1.0),
видна в Routing и DNS с пометкой «from node» и едет за узлом при
переименовании и переносе. Сверх того: правило маршрута может нести
несколько rule-set’ов, движок QR — zxing-cpp 3.1.1, ядро — v1.14.0-lx.38
(Tailscale в AAR, REALITY против Xray 26.9.8+), настоящая адаптивная иконка и
три исправления по отчётам с 4PDA.


🇬🇧 English

🪢 Tailscale nodes and node sections (docs/spec/features/435, contract ## 13)

A Tailscale node is useless on its own: it needs a DNS server of type
tailscale bound to it, a DNS rule for *.ts.net and a route rule for
100.64.0.0/10. The same goes for a WireGuard node with subnets behind the
peer. From this release a free node (a single server or a folder member)
carries that bundle with it, and the config builder appends the records to
the common lists before the usual resolve.

Before Now
type: tailscale in a pasted config was an unsupported protocol Accepted from outbounds[] and endpoints[] without server/server_port; the body is kept as is (auth_key, control_url, hostname, ephemeral, accept_routes, exit_node, …) and emitted into endpoints[]
— state_directory per node: <app files>/tailscale/<final tag> is written at build time only, never into the stored body — two tailnet nodes no longer share the core's default directory
— Add server → Tailscale: Tag, Auth key (secret), Control URL, Hostname, Ephemeral, Accept routes, Exit node; the wizard produces the node with the canonical bundle (@{self}-dns server, .ts.net rule, @{self} network route at position 945) and a note that a one-time key is consumed on the first login
A whole sing-box config with one node imported the outbound only The node arrives with its bundle: DNS servers whose detour/endpoint point at it, the DNS rules on those servers, the route rules with its outbound — references rewritten to the @self placeholder, so the bundle survives a rename or a folder prefix
— Routing shows a node's rules on the shared order axis with a “from node ” mark: the toggle and drag write into the node; there is no separate edit/delete. DNS shows the node's servers and rules read-only
— The node editor accepts a bare body, a document with sections, or a sing-box document with dns/route; a Sections block shows the count, the stored JSON and a Clear button

A node without exit_node gives access to the tailnet only — it is not a
Direction candidate (and therefore not listed on Home, which shows the
selector's members), but it is a legal detour target and the target of its
own route rule. With exit_node it is a candidate like any other node.

Core gate. The AAR must be built with Tailscale (v1.14.0-lx.38 or
newer) — this release ships exactly that core. On an older core (a downgrade,
a custom build) a Tailscale node is stored and shown, but skipped at build
with a warning
— the rest of the config builds. It survives a core update
and a backup from a desktop.

Backup. Sections are stored in the contract 1.0 record form (kind,
name/tag, enabled, num, body = the sing-box object as is) but are not
exported to the LX Backup until contract 1.0; the export names the loss, and a
0.12 file's servers[].sections is ignored silently.

🧩 A route rule can carry several rule-sets (docs/spec/tasks/434, contract ## 12)

Before Now
One .srs URL per rule; a desktop backup with three sets kept one The URL field takes one address per line. The core gets a rule_set per file and a single rule with the list; with one address the config is byte-for-byte the same
One cache file per rule One cache file and one auto-update entry per set; the rule is on only when all files are downloaded. Delete and URL change clear all of them
Backup ref ref stays the first address, refs lists all of them when there are two or more; import prefers refs — a desktop file round-trips without loss

🏷 NaïveProxy: a dropped extra-headers pair is visible on the node (docs/spec/tasks/433, contract 0.12.10)

A pair without : or with an invalid header name was already dropped at parse
time, but only the log knew. The node now carries an info warning with the
dropped pair; the other headers and the node itself do not change. For REALITY
nodes with a fingerprint outside the Chrome family the warning has existed since
§281; it is now bound to the contract code, and under REALITY the chrome
fingerprint is written explicitly even when the node has none.

📷 QR scanner: zxing-cpp 3.1.1 (docs/spec/tasks/432)

flutter_zxing 2.4 → 3.0. Small codes and version-1 QR are picked up better,
scanning on ARM is 10–40 % faster. Besides plain QR, Micro QR and rMQR with the
same link inside are read. The scanner screen and its settings did not change.

⚙️ Core v1.14.0-lx.38 (docs/KERNEL.md)

The pin was lx.34. lx.38 brings Tailscale into the Android AAR
(with_tailscale plus the ts_omit_* trims; the AAR grew by 2.6 MB, the
build time did not change) and the SPEC 084 hotfix (an ABBA deadlock of nested
selectors, fork issue #20); the upstream base is sing-box 1.14.0 + 33 commits
(lx.37 sync), submodules wireguard-go v0.0.6 / sing-tun v0.9.3. The libbox
Java surface is unchanged. On the way:

  • lx.35 — core issue #14: XHTTP behind a CDN that resets HTTP/2 streams
    could hold 100 % CPU until a restart (the reset error leaked with the HTTP/2
    library's type into a client running through the node — DoH with detour,
    rule-sets, chains).
  • lx.36 — REALITY against Xray-core ≥ 26.9.8: the core itself stripped the
    post-quantum key share the server now requires, and every REALITY node on an
    updated server died silently. Verified on an AVD against a local Xray 26.9.9:
    with lx.34 the nodes failed with reality verification failed, with
    lx.36 they carry traffic.

📦 File pickers and dependencies (docs/spec/tasks/431)

Play Console flagged the outdated file_picker. file_picker 12, share_plus
13, device_info_plus 13, package_info_plus 10, connectivity_plus 7 and
smaller dependencies were bumped. A picked file now reaches the screens already
read (PickedFile): config, backup, rules and subscription import read text
the same way through UTF-8 — three screens used to decode bytes as UTF-16 and
mangled Cyrillic names. freezed, json_serializable, build_runner and
their annotations left the project (there was not a single generated model),
and so did Apache Tika (465 classes) pulled in by the old file_picker.

🐛 Fixes

  • REALITY nodes with a non-Chrome fingerprint no longer die silently on Xray
    26.9.8+ servers
    (docs/spec/tasks/281,
    core SPEC 083). The updated REALITY server requires the post-quantum
    X25519MLKEM768 key share in the hello, which only the Chrome fingerprints
    carry; a node with firefox, safari, ios and the like was silently sent
    to the camouflage site. Such a node gets a warning at import and chrome
    goes into the core config; the stored value stays. The default random is
    replaced without a warning.
  • The app icon is no longer in a white frame. Android 8+ wrapped the old
    icon into an adaptive one by itself — shrunk it and put a white background
    under it, visible in the launcher and in the F-Droid catalogue. The app now
    has a real two-layer adaptive icon: an orange gradient across the whole
    background and the artwork inside the safe zone, so it looks whole under any
    mask. The legacy and catalogue icons got transparent corners.
  • A stale “VPN is running” notification from a killed process is cleared when
    the app opens
    (docs/spec/tasks/430).
    A 4PDA report: “L×Box [final = vpn-1] • 14 h” in the shade, Start in the app.
    The process was killed by the system, there is no tunnel, and the
    foreground-service notification stays because of a race inside Android
    itself. The app cannot dismiss it, so on open the service comes up for an
    instant under the same notification and stops normally — the system removes
    the notification.
  • Buttons and list tails no longer slide under the system navigation bar
    (docs/spec/tasks/429).
    A 4PDA report: “Save” in the DNS-rule sheet half under the three-button bar.
    All sheets moved to the shared showAppBottomSheet with insets for the
    keyboard and the bar; 18 list screens (About, Backup, Speed test, the
    WARP/Add server wizards, direction editors, logs, Stats, Debug, the node list,
    the config editor, QR) got a bottom inset; two contract tests keep it that
    way.

🛠 Toolchain and process

  • Google Play upload from CI (docs/spec/tasks/436):
    on a vX.Y.Z tag CI uploads the built AAB to the Play Console through the
    Google Play Developer API as a service account. The track and the release
    status are repository variables (PLAY_TRACK, PLAY_RELEASE_STATUS; by
    default production and draft — a person publishes the draft). The
    release text comes from the same fastlane/…/changelogs/ files F-Droid
    reads; their length (≤ 500 characters) is now checked on every push.
  • Contract with the launcher synced to 0.12.11; the record codec for the
    contract 1.0 form (lib/models/record_codec.dart) is the future root
    parser of custom_rules / dns_options.
  • docs/STORAGE.md, docs/PROTOCOLS.md (Tailscale), docs/ARCHITECTURE.md,
    the Debug API reference (sections in /subs and folder members,
    read-only).

🧪 Tests

flutter analyze (0 issues), the full test suite, all four l10n checkers and
the docs parity check. New: the record codec, node sections (round-trip,
@self substitution, containers), Tailscale parsing and emission, bundle
extraction from a whole config, build injection (order axis, DNS, the
endpoint sanitizer, state_directory, the core gate, Direction candidates),
backup (silent sections, named export loss), the Routing rows, the DNS form in
Tailscale mode, the wizard.

🇷🇺 Русский

🪢 Узлы Tailscale и секции узла (docs/spec/features/435, контракт ## 13)

Узел Tailscale сам по себе бесполезен: ему нужен DNS-сервер типа tailscale,
привязанный к нему, DNS-правило на *.ts.net и правило маршрута на
100.64.0.0/10. То же у WireGuard-узла с подсетями за пиром. С этого релиза
свободный узел (одиночный сервер или член папки) носит связку с собой, а
сборка конфига дописывает записи к общим спискам перед обычным резолвом.

Было Стало
type: tailscale во вставленном конфиге — неподдерживаемый протокол Принимается из outbounds[] и endpoints[] без server/server_port; тело хранится как есть (auth_key, control_url, hostname, ephemeral, accept_routes, exit_node, …) и эмитится в endpoints[]
— state_directory на узел: <файлы приложения>/tailscale/<финальный тег> пишется только при сборке и никогда в хранимое тело — два узла tailnet больше не садятся в общий дефолтный каталог ядра
— Add server → Tailscale: Tag, Auth key (секрет), Control URL, Hostname, Ephemeral, Accept routes, Exit node; мастер отдаёт узел с канонической связкой (сервер @{self}-dns, правило .ts.net, маршрут @{self} network на позиции 945) и подсказкой, что одноразовый ключ расходуется при первом входе
Целый sing-box-конфиг с одним узлом привозил только outbound Узел приезжает со связкой: DNS-серверы, чьи detour/endpoint указывают на него, DNS-правила на эти серверы, правила маршрута с его outbound — ссылки переписаны в плейсхолдер @self, поэтому связка переживает переименование и префикс папки
— Routing показывает правила узла на общей оси порядка с пометкой «from node <тег>»: тумблер и перетаскивание пишут в узел; отдельного редактирования/удаления нет. DNS показывает серверы и правила узла read-only
— Редактор узла принимает голое тело, документ с sections или sing-box-документ с dns/route; блок Sections показывает счётчик, хранимый JSON и кнопку Clear

Узел без exit_node даёт только доступ в tailnet — он не кандидат
Направлений (и потому не виден на Home, где показаны члены селектора), но
законная цель detour и цель собственного правила маршрута. С exit_node он
кандидат, как любой другой узел.

Гейт ядра. AAR должен быть собран с Tailscale (v1.14.0-lx.38 или
новее) — этот релиз несёт именно такое ядро. На более старом ядре (даунгрейд,
своя сборка) узел Tailscale хранится и показывается, но при сборке
пропускается с предупреждением
— остальной конфиг собирается. Он переживает
обновление ядра и бэкап с десктопа.

Бэкап. Секции хранятся в форме записей контракта 1.0 (kind,
name/tag, enabled, num, body = объект sing-box как есть), но до
контракта 1.0 в LX Backup не экспортируются; экспорт называет потерю, а
servers[].sections из файла 0.12 игнорируется молча.

🧩 Правило маршрута может нести несколько rule-set’ов (docs/spec/tasks/434, контракт ## 12)

Было Стало
Один URL .srs на правило; бэкап с десктопа с тремя наборами оставлял один В поле URL по одному адресу на строку. Ядро получает rule_set на каждый файл и одно правило со списком; с одним адресом конфиг байт-в-байт прежний
Один файл кэша на правило Файл кэша и запись автообновления на каждый набор; правило включается только когда скачаны все файлы. Удаление и смена URL чистят их все
В бэкапе ref ref остаётся первым адресом, refs перечисляет все при двух и более; импорт предпочитает refs — файл с десктопа приезжает без потерь

🏷 NaïveProxy: отброшенная пара из extra-headers видна на узле (docs/spec/tasks/433, контракт 0.12.10)

Пара без : или с недопустимым именем и раньше выбрасывалась при разборе, но
об этом знал только лог. Теперь у узла есть информационное предупреждение с
отброшенной парой; остальные заголовки и сам узел не меняются. Для
REALITY-узлов с отпечатком не из семейства Chrome предупреждение существовало
с §281; теперь оно привязано к коду контракта, а под REALITY отпечаток chrome
пишется явно и тогда, когда в узле его нет вовсе.

📷 QR-сканер: zxing-cpp 3.1.1 (docs/spec/tasks/432)

flutter_zxing 2.4 → 3.0. Лучше берутся мелкие коды и QR первой версии,
сканирование на ARM быстрее на 10–40 %. Кроме обычного QR читаются Micro QR и
rMQR с той же ссылкой внутри. Экран сканера и его настройки не менялись.

⚙️ Ядро v1.14.0-lx.38 (docs/KERNEL.md)

Пин был lx.34. lx.38 приносит Tailscale в Android AAR
(with_tailscale плюс ts_omit_*; AAR вырос на 2,6 МБ, время сборки не
изменилось) и хотфикс SPEC 084 (ABBA-дедлок вложенных selector'ов, issue #20
форка); база апстрима — sing-box 1.14.0 + 33 коммита (синк lx.37),
сабмодули wireguard-go v0.0.6 / sing-tun v0.9.3. Java-поверхность libbox не
изменилась. По дороге:

  • lx.35 — issue #14 ядра: XHTTP за CDN, сбрасывающим HTTP/2-стримы, мог
    держать 100 % CPU до перезапуска (ошибка сброса утекала типом
    HTTP/2-библиотеки в клиента, идущего сквозь узел, — DoH с detour,
    rule-set’ы, цепочки).
  • lx.36 — REALITY против Xray-core ≥ 26.9.8: ядро само вырезало из
    приветствия постквантовый ключ, который сервер теперь требует, и все
    REALITY-узлы на обновлённых серверах молча умирали. Проверено на AVD против
    локального Xray 26.9.9: с lx.34 узлы падали с reality verification failed, с lx.36 ходят.

📦 Выбор файлов и зависимости (docs/spec/tasks/431)

Play Console указал на устаревший file_picker. Подняты file_picker 12,
share_plus 13, device_info_plus 13, package_info_plus 10,
connectivity_plus 7 и зависимости по мелочи. Выбранный файл приходит в
экраны уже прочитанным (PickedFile): импорт конфига, бэкапа, правил и
подписок читает текст одинаково через UTF-8 — три экрана раньше декодировали
байты как UTF-16 и портили кириллицу в именах. Из проекта ушли freezed,
json_serializable, build_runner и их аннотации (ни одной сгенерированной
модели в коде не было), а с ними и Apache Tika (465 классов), которую тянул
старый file_picker.

🐛 Исправления

  • REALITY-узлы с отпечатком не из семейства Chrome больше не умирают молча на
    серверах Xray 26.9.8+
    (docs/spec/tasks/281,
    ядро SPEC 083). Обновлённый REALITY-сервер требует в приветствии
    постквантовый ключ X25519MLKEM768, который несут только отпечатки Chrome;
    узел с firefox, safari, ios и им подобными сервер молча уводил на
    камуфляжный сайт. Такой узел получает предупреждение при импорте, а в
    конфиг ядра уходит chrome; сохранённое значение не меняется. Дефолтный
    random подменяется без предупреждения.
  • Иконка приложения больше не в белой рамке. Android 8+ сам заворачивал
    старую иконку в адаптивную: уменьшал её и подкладывал белый фон — рамка была
    видна и в лаунчере, и в каталоге F-Droid. Теперь у приложения настоящая
    адаптивная иконка из двух слоёв: оранжевый градиент во весь фон и графика в
    безопасной зоне, поэтому под любой маской она цельная. Иконки для старых
    Android и для каталога получили прозрачные углы.
  • Зависшее уведомление «VPN работает» от убитого процесса снимается при
    открытии приложения
    (docs/spec/tasks/430).
    Жалоба с 4PDA: в шторке «L×Box [final = vpn-1] • 14 ч.», в приложении Start.
    Процесс убит системой, туннеля нет, а уведомление foreground-сервиса остаётся
    из-за гонки внутри самого Android. Снять его приложение не может, поэтому
    при открытии сервис на мгновение поднимается под тем же уведомлением и
    штатно останавливается — уведомление снимает система.
  • Кнопки и хвосты списков больше не уезжают под системную панель навигации
    (docs/spec/tasks/429).
    Жалоба с 4PDA: «Save» в шторке DNS-правила наполовину под трёхкнопочной
    панелью. Все шторки переведены на общий showAppBottomSheet с отступом под
    клавиатуру и панель; 18 экранов со списками (About, Backup, Speed test,
    мастера WARP/Add server, редакторы направлений, логи, Stats, Debug, список
    узлов, редактор конфига, QR) получили нижний отступ; два контракт-теста не
    дают вернуть проблему.

🛠 Тулчейн и процесс

  • Выпуск в Google Play из CI (docs/spec/tasks/436):
    на тег vX.Y.Z CI заливает собранный AAB в Play Console через Google Play
    Developer API от имени сервисного аккаунта. Трек и статус выпуска —
    переменные репозитория (PLAY_TRACK, PLAY_RELEASE_STATUS; по умолчанию
    production и draft — черновик публикует человек). Текст выпуска берётся
    из тех же файлов fastlane/…/changelogs/, что читает F-Droid; их длина
    (≤ 500 символов) теперь проверяется на каждом push.
  • Контракт с лаунчером синхронизирован до 0.12.11; кодек записей формы
    контракта 1.0 (lib/models/record_codec.dart) — будущий корневой парсер
    custom_rules / dns_options.
  • docs/STORAGE.md, docs/PROTOCOLS.md (Tailscale), docs/ARCHITECTURE.md,
    справочник Debug API (sections в /subs и у членов папок, read-only).

🧪 Тесты

flutter analyze (0 issues), полный набор тестов, все четыре l10n-чекера и
проверка паритета документации. Новое: кодек записей, секции узла
(round-trip, подстановка @self, контейнеры), разбор и эмиссия Tailscale,
извлечение связки из целого конфига, инъекция при сборке (ось порядка, DNS,
санитайзер endpoint, state_directory, гейт ядра, кандидаты Направлений),
бэкап (молчаливое sections, названная потеря при экспорте), строки Routing,
форма DNS в режиме Tailscale, мастер.


Install / Установка

adb install -r LxBox-v2.23.2-arm64-v8a.apk

Без uninstall! Поверх существующей установки. Настройки и подписки сохранятся.

No uninstall needed — install over the existing one. Settings and
subscriptions are preserved.


Previous release / Предыдущий релиз: v2.23.1.

Don't miss a new LxBox release

NewReleases is sending notifications on new releases.