Skip to content

LxBox v2.24.4

Choose a tag to compare

@github-actions github-actions released this 17 Sep 22:56
· 328 commits to main since this release

L×Box v2.24.4

A one-fix patch. Opening the JSON tab of a DNS server that comes from a
template or a preset showed “This section failed” instead of the server
(#143). It is a regression of
v2.24.0: the storage format moved to records 1.0 and the model lost its direct
serialization, but this tab still encoded the model as is. Servers you added
yourself were not affected. Nothing else changed since v2.24.3; the v2.24.3 notes are included below, since that release has no page of its own.

Патч с одним исправлением. Вкладка JSON у DNS-сервера из шаблона или
пресета показывала «В этом разделе произошла ошибка» вместо содержимого
(#143). Это регрессия v2.24.0:
формат хранения перешёл на записи 1.0, у модели не осталось прямой
сериализации, а вкладка по-прежнему кодировала модель как есть. Серверы,
добавленные вручную, затронуты не были. Больше с v2.24.3 ничего не менялось; описание v2.24.3 приведено ниже — своей страницы у того релиза нет.


🇬🇧 English

🩹 JSON tab of a template/preset DNS server (#143, docs/spec/tasks/458)

  • Was: for a DNS server of kind template or preset the JSON tab failed on
    open with Converting object to an encodable object failed: Instance of 'DnsServerTemplate' in Debug → Logs. The tab's “storage shape” block handed
    the controller's snapshot straight to the JSON encoder; since v2.24.0 the
    model has no toJson, the storage record is produced by a codec. The
    sibling tab of the rule editor was moved to the codec back then, this one
    was missed. Affected: v2.24.0 through v2.24.3.
  • Now: the block is built by the same codec and shows the record exactly as
    it sits in lxbox_settings.json (kind/tag/vars for a template,
    kind/ref for a preset). The resolved body preview below it and the
    editable JSON of an inline server are unchanged.

🧪 Tests

  • A widget test of the tab for both kinds; it is red without the fix.
  • flutter analyze clean over the whole project, the full test suite, four
    l10n checkers and the docs parity check green.

🇷🇺 Русский

🩹 Вкладка JSON у DNS-сервера из шаблона или пресета (#143, docs/spec/tasks/458)

  • Было: у DNS-сервера вида template или preset вкладка JSON падала при
    открытии, в Отладка → Логи: Converting object to an encodable object failed: Instance of 'DnsServerTemplate'. Блок «storage shape» вкладки
    отдавал снимок контроллера прямо в JSON-кодировщик; с v2.24.0 у модели нет
    toJson, запись хранения строит кодек. Парную вкладку редактора правил
    тогда перевели на кодек, эту пропустили. Затронуты v2.24.0 – v2.24.3.
  • Стало: блок строится тем же кодеком и показывает запись в том виде, в
    каком она лежит в lxbox_settings.json (kind/tag/vars у шаблона,
    kind/ref у пресета). Превью отрезолвленного тела ниже и редактируемый
    JSON inline-сервера не менялись.

🧪 Тесты

  • Виджет-тест вкладки для обоих видов; без фикса красный.
  • flutter analyze чист по всему проекту, полный набор тестов, четыре
    l10n-чекера и проверка паритета доков зелёные.

Also in this build: v2.24.3 / В этой сборке также: v2.24.3

v2.24.3 (17.09.2026) was tagged and shipped to Google Play, but its GitHub
release page was never published: the asset upload to GitHub failed on every
attempt. v2.24.4 is built on top of it, so everything below is in these APKs.

v2.24.3 (17.09.2026) получил тег и ушёл в Google Play, но страница релиза на
GitHub так и не была опубликована: загрузка файлов на GitHub падала при каждой
попытке. v2.24.4 собран поверх него, поэтому всё ниже есть в этих APK.

v2.24.3 — release notes / описание релиза

The node editor stops losing what you type. A NaïveProxy node with its own
root certificate in tls.certificate used to lose that field on Save, because
the TLS model knew six fields and dropped the rest (#140).
Now the model carries every TLS key the core knows, and the editor itself is
rebuilt around the source: the Source tab is the node's original text and the
only place you edit, the JSON tab shows what the core will receive. A node
whose source is a sing-box JSON object goes to the core verbatim, checked by the
core on Save. WireGuard .conf files are stored as files again, tag aside. The
core moves to v1.14.1-lx.4: TLS fragmentation finally applies to REALITY, and
REALITY gets a per-node key_share choice between the hybrid post-quantum
ClientHello and the classical one.

Редактор узла перестал терять то, что вы вписали. Узел NaïveProxy со своим
корневым сертификатом в tls.certificate терял это поле на Save: модель TLS знала
шесть полей, остальное отбрасывала (#140).
Теперь модель несёт все TLS-ключи ядра, а сам редактор перестроен вокруг
источника: вкладка Source — исходный текст узла и единственное место правки,
вкладка JSON — то, что получит ядро. Узел, чей источник — sing-box JSON,
уходит в ядро дословно, с проверкой ядром при сохранении. WireGuard .conf
снова хранится как файл, тег отдельно. Ядро поднято до v1.14.1-lx.4:
TLS-фрагментация наконец действует на REALITY, а у REALITY появился по-узловой
выбор key_share между гибридным постквантовым ClientHello и классическим.


🇬🇧 English

🔐 TLS fields survive the node editor (#140, docs/spec/tasks/454)

  • Was: the TLS model carried server_name, alpn, insecure, utls,
    reality and the public-key pin. Anything else in a node's tls block —
    certificate, certificate_path, min_version, cipher_suites,
    client_certificate, fragment — was dropped silently on Save, for every
    protocol. NaïveProxy was trimmed further, to enabled + server_name, so a
    self-signed server could not be reached at all: insecure is rejected by
    the core on naive.
  • Now: the model carries the full OutboundTLSOptions of the core. Keys the app
    does not reason about pass through in the form they arrived — a string stays
    a string, an array stays an array. Naive keeps certificate and
    certificate_path on top of the two it had; everything the core rejects on
    naive is still trimmed. The pin certificate_public_key_sha256 is now read
    from sing-box JSON too. A value of the wrong type drops the field, not the
    node. Nodes without the new fields emit byte-for-byte as before. The
    allowlist is shared with the launcher (contract TASKS_LXBOX §22).
  • Alongside: a node imported from JSON used to lose everything the share-URI
    cannot carry when moved into a folder or copied as a link — it was rebuilt
    through toUri(). It now keeps its own outbound object as its source.

📝 Node editor: Source and JSON tabs (docs/spec/tasks/455)

  • Was: the JSON tab showed the model's re-emission of the node, and Save wrote
    that copy over the original. Whatever the model did not know was gone.
  • Now: Source is the stored text of the node — a link, a WireGuard config
    or a sing-box JSON object — and the only place you edit; Save writes it as
    is (the Tag field goes into the link's fragment or the JSON body). JSON
    is read-only: what the core will receive. A node whose source is a JSON
    object goes into the config verbatim, including keys the app does not know;
    the core validates it on Save (Libbox.checkConfig), a rejected body is not
    stored and the core's message is shown. Edit JSON replaces the source
    with the shown JSON after a warning and takes you to Source; there is no way
    back to a link. Storage is unchanged: the kind of the source is derived from
    the text, there is no flag — the same rule the launcher applies to a manual
    object.

📄 WireGuard .conf is stored as the file (docs/spec/tasks/456)

  • Was: an imported INI was converted into a synthetic wg://…#name link and
    that link was stored as the source — comments and line order were lost, the
    record kind was uri.
  • Now: the source is the INI text byte for byte, the record kind is wg_ini
    (as in the launcher) and the tag is a record field applied on read. The
    initial name comes from the comment right under [Peer] (Proton writes
    # CH-FREE#11 there), else the file name, else WireGuard. An Amnezia
    vpn:// link gives each node its own INI. Records made earlier keep reading
    as before.
  • Two regressions caught on the way and fixed: the auto-emoji was lost on INI
    nodes, and a Tailscale node lost its body when moved into a folder.

⚙️ Core v1.14.1-lx.4 and REALITY key_share (docs/spec/tasks/457)

  • tls.fragment and tls.record_fragment now apply to REALITY nodes (core
    SPEC 088). Before, the REALITY client built its handshake on the bare socket
    and skipped both, including the automatic record_fragment the core enables
    under a detour; the global fragmentation toggle looked on and did nothing
    there. Nothing changed in the app — the toggle simply works now.
  • New per-node option tls.reality.key_share (core SPEC 089). hybrid
    requires X25519MLKEM768: the ClientHello grows to ~1.5–1.9 KB and leaves
    in two TCP segments — Xray servers from v26.9.8 demand it. classical
    strips the hybrid from key_share and supported_groups: ~0.5 KB, one
    segment — for older servers and for networks that drop the big handshake.
    Unset means whatever the fingerprint carries, as before. Set it in sing-box
    JSON or as key_share= in a vless/anytls share link; a value outside the
    pair is dropped silently and the node stays alive, because the core would
    reject the whole config on it. The URI parameter name is shared with the
    launcher.
  • Wire format, AAR tag sets and toolchain unchanged; the Java surface is
    identical to lx.3 (javap over all 253 classes — empty diff).

🔧 TCP keep-alive per node (docs/spec/tasks/453)

  • Was: the core accepts disable_tcp_keep_alive, tcp_keep_alive and
    tcp_keep_alive_interval since 1.13, but the app did not know them and
    dropped them on Save.
  • Now: the fields are part of the node and travel in all three forms it is
    stored and shared in — sing-box JSON, share-URI and Xray JSON (sockopt,
    whole seconds; a negative value means keep-alive off). Nine carriers, the
    protocols that dial over TCP; QUIC/UDP nodes have no socket to apply them
    to. A value that is not a Go duration is dropped on input. Nodes without the
    fields emit byte-for-byte as before. No form fields — the JSON tab and
    import only.

🧪 Tests

  • 4846 tests green, flutter analyze clean over the whole project, four l10n
    checkers and the docs parity check at zero.
  • New: TLS passthrough (naive from #140, the full allowlist on vless, wrong
    types, ech, byte parity, QUIC), verbatim body in the build, the core check
    payload, INI name from the [Peer] comment, wg_ini records with a tag,
    key_share round-trips and guards.
  • Core bump verified by a javap diff of the AAR (empty) and by the version
    string inside the built APK (1.14.1-lx.4).
  • Device verification of the naive certificate path and of key_share is
    still pending — please report on the issue if something does not behave.

🇷🇺 Русский

🔐 TLS-поля переживают редактор узла (#140, docs/spec/tasks/454)

  • Было: модель TLS держала server_name, alpn, insecure, utls,
    reality и пин публичного ключа. Всё остальное в блоке tls узла —
    certificate, certificate_path, min_version, cipher_suites,
    client_certificate, fragment — молча отбрасывалось на Save, для любого
    протокола. NaïveProxy резался ещё сильнее, до enabled + server_name, и
    до сервера с самоподписанным сертификатом было не достучаться: insecure
    ядро на naive отвергает.
  • Стало: модель несёт полный OutboundTLSOptions ядра. Ключи, о которых
    приложение не рассуждает, проходят насквозь в форме прибытия — строка
    остаётся строкой, массив массивом. Naive пропускает certificate и
    certificate_path в дополнение к двум прежним; всё, что ядро на naive
    отвергает, по-прежнему режется. Пин certificate_public_key_sha256 теперь
    читается и из sing-box JSON. Значение не того типа отбрасывает поле, а не
    узел. Узлы без новых полей эмитятся байт в байт прежними. Allowlist общий с
    лаунчером (контракт TASKS_LXBOX §22).
  • Заодно: узел из JSON терял всё, чего не несёт share-ссылка, при переезде в
    папку и в «Copy link» — его пересобирали через toUri(). Теперь он хранит
    свой объект outbound'а как источник.

📝 Редактор узла: вкладки Source и JSON (docs/spec/tasks/455)

  • Было: вкладка JSON показывала пересборку узла моделью, и Save записывал эту
    копию поверх оригинала. Чего модель не знала, то пропадало.
  • Стало: Source — хранимый текст узла (ссылка, конфиг WireGuard или
    sing-box JSON) и единственное место правки; Save пишет его как есть (поле
    Tag уходит во фрагмент ссылки или в тело JSON). JSON — только чтение: то,
    что получит ядро. Узел, чей источник — JSON-объект, идёт в конфиг дословно,
    включая ключи, которых приложение не знает; ядро проверяет его при
    сохранении (Libbox.checkConfig), отвергнутое тело не сохраняется, ошибка
    показывается словами ядра. Edit JSON после предупреждения заменяет
    источник показанным JSON и уводит в Source; обратно в ссылку узел не
    превращается. Хранение не менялось: вид источника выводится из текста,
    флага нет — то же правило, что у лаунчера для ручного объекта.

📄 WireGuard .conf хранится как файл (docs/spec/tasks/456)

  • Было: импортированный INI переводился в синтетическую ссылку wg://…#имя,
    и источником хранилась она — комментарии и порядок строк терялись, вид
    записи был uri.
  • Стало: источник — текст INI байт в байт, вид записи wg_ini (как у
    лаунчера), тег — поле записи и применяется при чтении. Начальное имя —
    комментарий сразу под [Peer] (Proton пишет туда # CH-FREE#11), иначе имя
    файла, иначе WireGuard. Ссылка Amnezia vpn:// даёт каждому узлу свой
    INI. Старые записи читаются как раньше.
  • По пути пойманы и починены две регрессии: у INI-узлов терялось
    авто-эмодзи, а узел Tailscale терял тело при переезде в папку.

⚙️ Ядро v1.14.1-lx.4 и key_share у REALITY (docs/spec/tasks/457)

  • tls.fragment и tls.record_fragment теперь действуют на REALITY-узлы
    (ядро SPEC 088). Раньше REALITY-клиент строил рукопожатие на голом сокете и
    оба пропускал, включая авто-record_fragment, который ядро включает под
    detour; глобальный тумблер фрагментации выглядел включённым и ничего там
    не делал. В приложении ничего не менялось — тумблер просто заработал.
  • Новая по-узловая опция tls.reality.key_share (ядро SPEC 089). hybrid
    требует X25519MLKEM768: ClientHello вырастает до ~1,5–1,9 КБ и уезжает
    двумя TCP-сегментами — этого требуют Xray-серверы от v26.9.8. classical
    вырезает гибрид из key_share и supported_groups: ~0,5 КБ и один сегмент
    — для серверов постарше и для сетей, которые большое рукопожатие роняют.
    Без поля — как несёт отпечаток, прежнее поведение. Задаётся в sing-box JSON
    или как key_share= в share-ссылке vless/anytls; значение вне пары
    отбрасывается молча, узел остаётся жив, потому что ядро на нём отвергло бы
    весь конфиг. Имя URI-параметра общее с лаунчером.
  • Провод, наборы тегов AAR и тулчейн без изменений; Java-поверхность совпадает
    с lx.3 (javap по всем 253 классам — дифф пуст).

🔧 TCP keep-alive на узле (docs/spec/tasks/453)

  • Было: ядро принимает disable_tcp_keep_alive, tcp_keep_alive и
    tcp_keep_alive_interval с 1.13, но приложение их не знало и теряло на
    Save.
  • Стало: поля — часть узла и ходят во всех трёх формах, в которых он хранится
    и передаётся: sing-box JSON, share-URI и Xray JSON (sockopt, целые секунды;
    отрицательное значение — keep-alive выключен). Носителей девять — протоколы с
    TCP-дозвоном; QUIC/UDP-узлам поля не к чему применить. Значение не в формате
    Go-duration отбрасывается на входе. Узлы без полей эмитятся байт в байт
    прежними. Полей в форме нет — только JSON-вкладка и импорт.

🧪 Тесты

  • 4846 тестов зелёные, flutter analyze чист по всему проекту, четыре
    l10n-чекера и проверка паритета доков на нуле.
  • Новые: сквозные TLS-поля (naive из #140, полный allowlist на vless, не те
    типы, ech, байт-паритет, QUIC), дословное тело в сборке, нагрузка проверки
    ядром, имя INI из комментария [Peer], записи wg_ini с тегом, round-trip
    и guard'ы key_share.
  • Бамп ядра подтверждён javap-диффом AAR (пуст) и строкой версии внутри
    собранного APK (1.14.1-lx.4).
  • Проверка на устройстве пути naive с сертификатом и key_share ещё не
    сделана — если что-то ведёт себя не так, напишите в issue.

Install / Установка

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

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

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


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