LxBox v2.24.4
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 withConverting 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 notoJson, 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 inlxbox_settings.json(kind/tag/varsfor a template,
kind/reffor 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 analyzeclean 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,
realityand the public-key pin. Anything else in a node'stlsblock —
certificate,certificate_path,min_version,cipher_suites,
client_certificate,fragment— was dropped silently on Save, for every
protocol. NaïveProxy was trimmed further, toenabled+server_name, so a
self-signed server could not be reached at all:insecureis rejected by
the core on naive. - Now: the model carries the full
OutboundTLSOptionsof 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 keepscertificateand
certificate_pathon top of the two it had; everything the core rejects on
naive is still trimmed. The pincertificate_public_key_sha256is 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
throughtoUri(). 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://…#namelink and
that link was stored as the source — comments and line order were lost, the
record kind wasuri. - 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#11there), else the file name, elseWireGuard. 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.fragmentandtls.record_fragmentnow apply to REALITY nodes (core
SPEC 088). Before, the REALITY client built its handshake on the bare socket
and skipped both, including the automaticrecord_fragmentthe core enables
under adetour; 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
requiresX25519MLKEM768: 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 fromkey_shareandsupported_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 askey_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_aliveand
tcp_keep_alive_intervalsince 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 analyzeclean 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_inirecords with a tag,
key_shareround-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_shareis
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. Ссылка Amneziavpn://даёт каждому узлу свой
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.