L×Box v1.3.0
Android VPN client powered by sing-box.
Большой релиз: parser v2 переписан с нуля, автообновление подписок, AntiDPI mixed-case SNI, haptic feedback.
✨ Highlights
- Parser v2 — полностью переписан под sealed
NodeSpec, 3-слойный pipeline, 9 протоколов включая новый TUIC v5. - Автообновление подписок — 4 триггера (app start, VPN connected +2мин, каждый час, VPN stopped) с жёсткими гейтами против спама.
- Mixed-case SNI — новый AntiDPI трюк для обхода наивного exact-match DPI.
- Haptic feedback — вибро-отклик на VPN события (default on).
- Restart warning теперь sticky — не пропадает при отмене Stop-диалога.
🔧 Parser v2 (spec 026, 5 фаз)
Полная переработка внутреннего pipeline:
- Типизированная sealed-иерархия
NodeSpec— 9 протоколов: VLESS, VMess, Trojan, Shadowsocks, Hysteria2, TUIC v5 (новый), SSH, SOCKS, WireGuard. - Полиморфный
emit(vars)— WireGuard эмитится в Endpoint, остальные в Outbound, без runtime-проверок типа. Компилятор гарантирует exhaustive switch. - Round-trip invariant —
parseUri(spec.toUri()) ≈ specпротестирован для каждого варианта. - XHTTP fallback через sealed
TransportSpec— компилятор не даёт забыть fallback. ServerList(sealed) —SubscriptionServers(fetch по URL) vsUserServers(paste/file/qr/manual). Одноразовая миграцияproxy_sources→server_listsпри первом чтении.- Функциональный pipeline —
parseFromSource(source) → buildConfig(lists, settings) → BuildResult(config, validation, warnings). ValidationResultс типизированнымиValidationIssue: dangling outbound refs, empty urltest, invalid selector default.- Удалено ~2700 LOC v1 —
node_parser,config_builder,source_loader,subscription_decoder/fetcher,xray_json_parser,parsed_node,proxy_source. - 116 тестов покрывают модели, парсер, round-trip, builder, validator, migration, subscription pipeline, e2e.
🔄 Subscription auto-update (spec 027)
- 4 триггера автообновления: app start · через 2 мин после VPN connected · раз в час · сразу по VPN disconnected. Manual refresh (⟳) — пятый, force.
- Жёсткие gates против спама:
minRetryInterval=15min(persisted черезlastUpdateAttempt),maxFailsPerSession=5(in-memory, размораживается на рестарт app),perSubscriptionDelay=10s ± 2sмежду подписками,_running/_inFlightdedup-флаги,inProgressguard от двойных кликов. - Crash-safe init sweep — зависший
inProgressна диске (kill -9) сбрасывается вfailed, fetch возможен после 15-min cooldown. - UI в строках подписок —
124 nodes · 🔄 24h · 🕐 3h ago · (2 fails)с interval/ago/failcount. - Subscription block в detail screen — URL (tap=copy), Update interval picker (1/3/6/12/24/48/72/168h), Status row, Refresh now кнопка.
- Rebuild config НЕ триггерит HTTP — только локальная сборка из уже-загруженных nodes.
🛡️ AntiDPI: Mixed-case SNI (spec 028)
Новый toggle в Settings → DPI Bypass. Рандомизирует регистр букв в server_name (WwW.gOoGle.CoM). По RFC 6066 SNI case-insensitive — сервер обязан принять любой регистр; трюк ломает наивный exact-match DPI у региональных провайдеров.
- First-hop only, per-outbound независимая рандомизация, punycode-метки (
xn--…) не трогаем. - Default off. Honest help-text:
Bypasses simple exact-match DPI; ineffective against GFW-class filtering.
📳 Haptic feedback (spec 029)
Toggle в App Settings → Feedback, default on. Респектит системную настройку Android Touch feedback.
| Событие | Интенсивность |
|---|---|
| Tap Start/Stop | Лёгкий tick |
| VPN connected | Средний impact |
| User disconnect | Лёгкий impact |
| Revoked / heartbeat fail (первый только) | Тяжёлый impact |
| Manual subscription fetch success/fail | Лёгкий / средний |
Auto-триггеры (periodic update, ping, scroll) — не вибрируют. Throttle 100мс защищает от спама.
🔔 Restart warning — sticky flag
Розовая плашка «Config changed — restart VPN to apply» под Stop теперь показывается надёжно после любого сценария (Routing Apply, settings change, Debug import, manual rebuild) и не пропадает при отмене Stop-диалога.
Реализовано через derived getter _needsRestart поверх sticky-флага state.configStaleSinceStart в HomeState — сбрасывается только на реальном tunnel up↔down.
📝 Прочее
- Subscription title fallback — если нет
profile-titleheader, имя берётся изContent-Disposition: filename=...(RFC 5987 support, стрип.txt/.yaml/.json/.conf). - User-Agent для HTTP к подпискам →
LxBox Android subscription client(былSubscriptionParserClient). - DNS rules — inline
domain_suffix: [ru, xn--p1ai, su]для Yandex DoH, без промежуточного ruleset reference. - Local build marker —
scripts/build-local-apk.shсобирает APK с git describe; About screen показывает розовую плашку «🧪 LOCAL BUILD · N commits since vX.Y.Z». CI builds чистые.
📦 Install
Скачай APK ниже, установи на Android (8.0+). При первом запуске разреши VPN-permission.
128/128 тестов · debug + release APK собираются чисто (71.3 MB).