79 KiB
Zapret KVN Android — план реализации
Рабочий TODO-лист проекта. Отмечать выполненное заменой
[ ]на[x]только после прохождения указанного gate. Архитектурные решения здесь не переопределяются: источник истины — главная архитектура, DNS ADR, Routing ADR, VPN Hiding ADR и политика форматов импорта.
| Поле | Значение |
|---|---|
| Статус | Публичный MVP завершён и выпущен как stable v0.2.4; расширенная физическая матрица продолжается по пользовательским отчётам |
| Текущий этап | Пострелизное тестирование и исправление подтверждённых проблем |
| Минимальная ОС | Android 8.0, API 26 |
| Устройства MVP | Телефоны |
| Ядро | sing-box-extended v1.13.14-extended-2.5.2 |
| Commit ядра | ff11f007ec798136a5de258f947a4f34011a37ea |
| Модули | Только app |
Как вести план
Организация исходников обеих платформ выполнена и описана в SOURCE_LAYOUT.md. Перенос пакетов не закрывает отдельные gates официального WG/AWG runtime и согласованного stable-выпуска.
- Выполнять этапы по порядку; внутри этапа допустимы небольшие независимые задачи.
- Не отмечать gate по факту компиляции: требуются перечисленные тесты и сохранённый результат.
- Баг, блокирующий gate, добавлять прямо под соответствующим этапом как
- [ ] BUG-.... - Решение, меняющее TUN, DNS, routing, хранение или источник истины, сначала вносить в соответствующий ADR.
- Не добавлять новый слой, dependency, service, process или фоновую задачу «на будущее».
- После этапа обновлять строку «Текущий этап» и раздел «Текущее состояние» внизу.
Зафиксировано до начала кода
- Принята архитектура с одним product-модулем
appи узкими направленными libraries (network-bootstrap, import), без Room, Hilt, WebView и WorkManager. - Разделены Android per-app scope и sing-box destination routing.
- Зафиксированы один process, один
VpnService, один TUN и один libbox instance. - Зафиксирована DNS-архитектура без FakeIP: Auto
профиль → Android → DoH, аfallback/parallelиспользуется только внутри защищённого этапа. - Зафиксирована маршрутизация
proxy/direct/rejectчерез настоящий JSON. - Зафиксирован точный commit ядра.
- Подготовлены и проверены 7/7 эталонных JSON, включая Android WireGuard ClientBind.
- Пройден
go test ./dns/... ./route/rule ./experimental/libbox. - До первой публичной сборки выбрать окончательные
applicationId, namespace и имя signing key. - Для production rule-set зафиксированы источники, лицензии, commit и SHA-256 списков.
Definition of Done MVP
- Публичный MVP принят владельцем 24 июля 2026 года после установки и проверки
production-signed stable
v0.2.4на реальном устройстве. - Новый пользователь может установить APK, импортировать VLESS/другой поддержанный URI или JSON, выбрать приложения и подключиться без ручного редактирования JSON.
- Автоматизированы проверки per-app bypass,
proxy/direct/reject, DNS, fail-close, JSON round-trip/backup, lifecycle, redaction и updater. - Пройдены обязательные для публикации fixture, unit, instrumented, manifest, signing, revision и release-аудиты.
- Release APK содержит ядро нужного commit, подписан постоянным ключом и опубликован с SHA-256.
Расширенная проверка редких сочетаний устройств, операторов, IPv6/NAT64, captive portal и физической энергии не блокирует статус MVP. Она продолжается после релиза по пользовательским отчётам и не считается выполненной без соответствующего evidence.
Этап 0 — каркас и воспроизводимая сборка
Цель: минимальный устанавливаемый APK и CI, доказывающий происхождение ядра.
Репозиторий и Android
I0-01Инициализировать Git-репозиторий и добавить.gitignore,docs/LICENSE,docs/README.md.I0-02Создать Gradle wrapper и один модульapp; Kotlin, Compose и Material 3, minSdk 26.I0-03Зафиксировать версии AGP/Kotlin/JDK; не добавлять convention plugins и version catalog без реальной необходимости.I0-04СоздатьZapretApplication, ручнойAppContainer,MainActivityи четыре нижние вкладки-заглушки.I0-05Реализовать системную светлую/тёмную тему, Dynamic Color на API 31+ и встроенную палитру на API 26–30.I0-06Добавить базовый manifest с минимальными разрешениями; не запрашиватьWAKE_LOCKиREQUEST_IGNORE_BATTERY_OPTIMIZATIONS.I0-07Добавить debug/release build types, R8 и resource shrinking для release.
Ядро и CI
I0-08Добавить скрипт сборки libbox AAR и audit CLI из полного pinned SHA.I0-09Проверять, что HEAD исходника и embedded revision CLI равны pinned SHA.I0-10Первоначально выбрать для MVParm64-v8a; решение замененоI7-10после проверки размера универсального тестового APK.I0-11В CI запускатьsing-box checkдля всех файловtestdata/**/*.json.I0-12В CI запускать pinned Go tests и Android unit tests.I0-13Собирать debug APK в Forgejo Actions и сохранять core version/revision рядом с artifact.I0-14Добавитьdocs/LICENSE/docs/NOTICEдля приложения, sing-box-extended и включённых библиотек.
Gate 0
- Чистый checkout одной командой собирает debug APK.
- APK устанавливается и открывает четыре вкладки на API 26 и современной версии Android.
- CI подтверждает exact core revision и принимает 7/7 fixtures.
- В release manifest нет запрещённых разрешений, второго process или второго VPN service.
Этап 1 — профили и настоящий JSON
Цель: импортировать, безопасно хранить, редактировать и проверять JSON без VPN.
I1-01Создать пакетыprofiles/,config/,importer/внутриapp, без отдельных Gradle-модулей.I1-02РеализоватьProfileStore:files/profiles/index.json,<id>.json, одна<id>.json.bakчерезAtomicFile.I1-03Хранить в index только UI metadata; transport, credentials, DNS и outbounds остаются в JSON профиля, а общая destination-policy — в отдельном ограниченном DataStore без копии профиля.I1-04Реализовать атомарные create/read/update/delete/restore операции и очистку orphan temp-файлов.I1-05Реализовать импорт raw JSON из системного file picker и буфера после явного нажатия.I1-06Вызывать libboxCheckConfig()до сохранения изменённого профиля и показывать понятную ошибку.I1-07Реализовать JSON-tree editor наkotlinx.serialization.json, сохраняющий неизвестные поля.I1-08Сделать простой raw editor: моноширинный текст, поиск, format, validate, отмена несохранённых изменений.I1-09Добавить список профилей, выбор активного профиля, переименование, удаление с подтверждением и восстановление backup.I1-10Хранить тему, активный профиль, UI-настройки и ограниченный общий routing intent в DataStore; не копировать туда transport JSON или credentials.I1-11РеализоватьManagedProfileFactory: маленькие base/protocol/selector builders вместо набора полноразмерных JSON-шаблонов.I1-12Для одиночной ссылки создавать один server outbound и selectorzapret-proxy; для subscription — несколько server outbounds в том же selector.I1-13Генерировать уникальные стабильные server tags без credentials; при совпадении имён добавлять детерминированный suffix.I1-14Сохранять выбранный сервер только вselector.defaultнастоящего JSON; не использовать DataStore илиexperimental.cache_file.I1-15Анализировать raw JSON: показывать существующие selector-группы без скрытой нормализации; managedzapret-proxyсоздавать только после явного выбора пользователя.I1-16Хранить профили только в app-private storage и выключить Android Auto Backup для файлов с credentials.
Тесты и Gate 1
- Unit: JSON round-trip и GUI edit сохраняют неизвестные extended-поля.
- Unit: сбой записи оставляет старый профиль читаемым; backup восстанавливается.
- Unit: malformed JSON не изменяет существующий профиль.
- Unit: index не содержит DNS/outbound/route objects или credentials.
- Unit + native instrumented: managed single/multi-server builders проходят
CheckConfig()и имеют валидные ссылки selector → server tags. - Unit: смена
selector.defaultне изменяет остальные outbounds, DNS, routes и unknown fields. - Instrumented: file picker и clipboard import работают на API 26 и современной ОС.
- Gate: пользователь может импортировать JSON, проверить, изменить, перезапустить приложение и получить тот же профиль.
Этап 2 — минимальный рабочий VPN
Цель: один профиль и выбранное приложение реально проходят через один TUN/libbox.
Per-app scope
I2-01Реализовать глобальный include allowlist в DataStore.I2-02Создать полноэкранный picker приложений: поиск, установленные пользовательские приложения, скрытые системные.I2-03Добавить встроенные suggestions для Instagram, X/Twitter, YouTube, Telegram Stable/Beta/Direct, Telegram X, ZaStoGram и распространённых форков, WhatsApp, Discord, Signal, ChatGPT, Claude, Gemini, Perplexity, Copilot, DeepSeek, Grok, Suno, Spotify, Notion, Chromium/Ultimatum и популярных браузеров; без TikTok. Только при первой инициализации выбирать все установленные recommendations, включая отключённые предустановленные Gemini/YouTube/YouTube Music, но не обычные системные службы. Установленные Android browser handlers и обработчикиtg://также выбирать без бесконечного списка package вручную. После инициализации обновления никогда автоматически не меняют include/exclude-список пользователя.I2-04Не запускать VPN при пустой effective allowlist или ошибкеaddAllowedApplication(); исчезнувшие package пропускать до Builder, писать в bounded diagnostic log и продолжать при наличии хотя бы одного установленного приложения. Отключённый, но установленный package разрешать выбрать и передавать Builder: после включения приложения сохранённая allowlist действует без повторной настройки.I2-05Внутренне включать package Zapret KVN для health-check, не показывая его как пользовательский выбор.
Service и libbox
I2-06Создать один foregroundVpnService,VpnControllerи закрытый набор состояний подключения.I2-07Реализовать один service-lock, generation token и идемпотентныйstop().I2-08Интегрировать libbox в том же Android process; вызватьLibbox.SetMemoryLimit(false).I2-09Настроить releaseDebug=false, managedlog.level=warn,LogMaxLines=256, без runtime-лога на диск.I2-10Реализовать минимальный platform adapter: TUN PFD,protect(fd), package owner lookup и platform callbacks.I2-11Перед запуском очищать package include/exclude только в runtime-копии JSON и применять одну глобальную allowlist.I2-12Валидировать один TUN, IPv4+IPv6 full routes,auto_route, package conflicts и запрещённые bind/mark/netns-поля.I2-13ВыполнятьCheckConfig()доVpnService.Builder.establish().I2-14Реализовать foreground notification только с состоянием и действиями открыть/остановить; без live speed.I2-15При revoke/error/stop закрывать core, PFD, callbacks, streams и foreground notification ровно один раз.I2-16Получать selector-группы/текущий server через libbox и переключать активный server вызовомCommandClient.SelectOutbound()без restart TUN/core.I2-17Перед switch собрать JSON с новымselector.default, выполнитьCheckConfig()и атомарно сохранить; при ошибке runtime switch сделать один контролируемый restart.I2-18Для managed selector включитьinterrupt_exist_connections=true; проверить, что закрываются только proxy-соединения selector, аdirectи приложения вне TUN не затрагиваются.I2-18AПоверх exact core commit применять один проверяемый Android data-plane patch: vanilla WireGuard и AmneziaWG используют раздельные pinned движки metacubex, общий protected batch-1 ClientBind, roaming lock до peers и порядокdevice → IPC → TUN Up; публиковать версии модулей и patch SHA-256, а checkout возвращать чистым. Physical A/B остаётся вP16.
Тесты и Gate 2
- Instrumented: выбранное приложение видно в TUN; контрольный невыбранный UID не виден и работает напрямую.
- Instrumented: проверены IPv4, IPv6, TCP, UDP и реальный Hysteria2/QUIC transport.
- Instrumented:
protect(fd) == false, revoke и ошибка послеestablish()приводят к полному stop. - Instrumented: ноль/два TUN, partial routes и конфликт package list отклоняются до подключения.
- Instrumented: server switch меняет внешний IP без пересоздания TUN; после restart остаётся выбранный server.
- Leak test: после 20 connect/stop нет роста PFD/threads/libbox instances; TUN, adapters и callbacks закрываются в каждом цикле.
- Gate: минимальный JSON-профиль подключается на API 26 и современной ОС, а невыбранный трафик не создаёт per-packet работу приложения.
Этап 3 — DNS, bootstrap и сетевой lifecycle
Цель: соединение либо полностью работает, либо полностью закрывается без сломанного DNS Android.
I3-01РеализоватьDefaultNetworkMonitorдля underlying non-VPN network без polling.I3-02Реализовать bootstrap resolver:DnsResolverна API 29+,Network.getAllByName()на API 26–28.I3-03Реализовать маленький LKG cache адреса proxy: fresh 24 часа, аварийный срок до 7 дней, исходное имя сохранять для TLS/Reality SNI.I3-04Детектировать Private DNS off/automatic/strict черезLinkProperties; системную настройку никогда не менять.I3-05Реализовать четыре режима GUI: Автоматически, DNS Android, Защищённый через VPN, Из JSON.I3-05AДобавить включённый по умолчанию «Только IPv4 через VPN»:ipv4_onlyприменяется к generated DNS rules proxy-доменов в Secure/DNS Android и managed-этапах Auto, не меняя первую попытку с DNS профиля, direct/LAN, TUN IPv6 и режим «Из JSON»; пользователь может вернуть dual-stack, но WireGuard требует настоящий внутренний IPv6-адрес.I3-05BДобавить одну глобальную редактируемую пару DNS override для managed-режимов, по умолчанию включённую какntc.party → 130.255.77.28:hostsи точное правило стоят послеreject, но до resolver rules; «Из JSON», routing, TLS/SNI и встроенный DoH не меняются, diagnostic не раскрывает пару.I3-05CВ Auto использовать конечную цепочкуDNS профиля → DNS Android → DoH: переходить только после typed DNS health failure, полностью закрывать предыдущие core/TUN/callbacks, не переключаться на ошибке JSON/proxy/HTTPS и не давать fallback явным режимам.I3-06СоздатьRuntimeConfigBuilder, который добавляет толькоzapret-*overlays и не меняет сохранённый JSON.I3-07В явном Secure и последнем Auto-этапе использовать реальные IP,reverse_mapping, cache 4096 и пять DoH (Quad9, Google, OpenDNS, Cloudflare, Yandex) черезfallback/parallel; exactsequentialне достигает резерва при зависшем основном DoH; FakeIP не создавать.I3-08Брать внутренний DNS черезTunOptions.GetDNSServerAddress()и передавать его вVpnService.Builder.addDnsServer().I3-09Перехватывать стандартный DNS правилом port 53 /hijack-dns; не обещать перехват DoT, встроенного DoH и mDNS.I3-10При strict Private DNS блокировать managed Auto/Secure доestablish(); DNS Android разрешать только при active+validated strict, иначе fail-close без plaintext fallback; «Из JSON» не переписывать.I3-11Реализовать последовательный health pipeline: proxy socket, DNS через TUN, HTTPS Cloudflare, затем Google и OpenDNS только после ошибок предыдущих endpoints.I3-12Показывать «Подключено» только после всех проверок; любая ошибка закрывает TUN.I3-13При смене сети или DNS/captive policy state обновлять underlying Network, сбрасывать transport и выполнять один контролируемый restart с debounce/generation token.I3-14Не добавлять периодический health-check, бесконечный retry или plaintext DNS fallback.I3-15Кнопку «Очистить DNS-кэш» реализовать контролируемым restart core, честно не обещая очистить Android resolver cache.
I3-13: baseline текущей сессии неизменяем. Transient callback A → B → A
отменяет pending restart, а итоговый policy key повторно проверяется после debounce.
Тесты и Gate 3
- Unit/core: четыре DNS fixtures проходят exact CLI; JVM-тест подтверждает managed
fallback/parallelбез FakeIP. - Unit: Auto выбирает DNS профиля, Android и DoH в фиксированном порядке, остаётся bounded и не скрывает non-DNS ошибки; явные режимы имеют ровно одну попытку.
- Core: воспроизводимый тест внутри exact pinned package проверяет success, transport error, hang с общим context и
NXDOMAIN/SERVFAIL/REFUSEDбез ошибочного fallback. - Instrumented на AVD API 28/29/36: Private DNS off/automatic/strict working/strict broken; managed Auto/Secure блокируются до TUN, а поломка strict Android DNS во время активной сессии event-driven закрывает TUN без plaintext fallback.
- Instrumented на AVD: реальные Wi-Fi, mobile, Wi-Fi ↔ mobile и IPv6; один контролируемый restart на каждую смену; captive-portal fail-close покрыт детерминированной fault injection до TUN.
- Physical lab: настоящий captive portal и IPv6-only/NAT64 на целевых устройствах/сетях.
- Instrumented: блокировка системного resolver с fresh/stale/no LKG; реальный managed DoH/proxy success, отказ всех managed DoH при недоступном proxy и полная очистка lifecycle.
- Physical lab: повторить blocked system DNS/LKG и DoH failure на реальной Wi-Fi/mobile сети, а не только через детерминированную fault injection.
- Instrumented: мёртвый внутренний DNS после TUN закрывает core/PFD; Android немедленно получает обычную non-VPN сеть и снова разрешает DNS.
- Gate: вся матрица DNS ADR проходит, активный TUN никогда не остаётся с неработающим DNS.
Physical Test 17 на Pixel 9 Pro XL (API 37) подтвердил ответ WireGuard handshake и
чистый fail-close без утечки PFD/core/callback. Он одновременно выявил три независимые
ошибки после handshake: ipv4_only не применялся к proxy-доменам режима DNS Android;
fallback/sequential не мог перейти к резервному DoH после зависания первого; одиночный
Google 204 probe давал ложный VPN-200, а IPv6-попытка WireGuard-профиля без внутреннего
IPv6 завершалась missing IPv6 local address. Test 18 подтвердил WireGuard handshake,
но реальные логи затем показали блокировку managed DoH и необходимость предпочитать
уже выбранный DNS профиля. Цепочка Auto исправлена; physical подтверждение Test 21
остаётся частью открытого Gate 3.
Этап 4 — маршрутизация и rule-set
Цель: один понятный UI управляет общей destination-policy, компилируемой в настоящий effective JSON выбранного профиля.
I4-01Реализовать экран из карточек «Область VPN», «Правило трафика» и читаемый «Итог».I4-02Реализовать presets: Всё через VPN, Обход LAN, Только выбранные сайты, Россия напрямую, Россия через VPN, Пользовательский.I4-03Реализовать действия правила: Через VPN, Напрямую, Блокировать.I4-04Соблюдать порядок exact reject → direct exceptions → proxy rules → final.I4-05Не генерировать package route rules для обычной allowlist: Android уже выполнил это отсечение.I4-06Поставлять локальные binary.srsвнутри APK с manifest, version, license и SHA-256.I4-07Реализовать согласованное domain block: DNSreject+ routereject; IP/CIDR block: routereject.I4-08Не включать global sniff, legacy GeoIP/Geosite, remote managed rule-set или Android route exclusions.I4-09Реализовать advanced include/exclude mode с явным предупреждением; пустой exclude-list блокирует запуск.I4-10Дать GUI-редактор основных domain/IP/rule-set/outbound полей, редкое оставить raw JSON.I4-11После каждой GUI-операции показывать effective summary и diff управляемыхzapret-*объектов.I4-12Добавить отдельный пустой по умолчанию blocklist приложений: в include объединять его с Android TUN boundary, в exclude запрещать пересечение с direct, а в runtime-копии ставить первые DNS/routepackage_name → reject.I4-13Сделать GUI destination-policy общей для всех профилей: однократно инициализировать её из активного профиля, хранить отдельно от credentials и компилировать поверх выбранного base JSON доCheckConfig(), не переписывая профили.
Тесты и Gate 4
- Core/fixture:
ru-rule-set.jsonиblock-rule.jsonпроходят exact CLI. - Instrumented: RU/non-RU domain и IPv4/IPv6 во всех presets.
- Instrumented: selected app получает proxy/direct/reject; unselected app всегда остаётся вне TUN.
- Instrumented: приложение со встроенным DoH демонстрирует задокументированное ограничение domain-only block.
- Performance: измерить cold start, lookup CPU/RAM и размер production
.srs. - Gate: UI summary, effective JSON и реальный сетевой путь совпадают для каждого preset.
Gate 4 закрыт автоматизированно 22 июля 2026 года на AVD API 26 и 36. Все шесть presets прошли реальный TUN-путь через локальный SOCKS5 proxy/direct/reject для RU/non-RU domain и IPv4/IPv6; отдельный UID-тест подтвердил, что невыбранное приложение остаётся вне TUN. Стандартный DNS block и HTTPS DoH→numeric path воспроизводят документированную границу domain-only block. UI summary, сохранённый JSON и фактический outbound совпали для каждого preset.
Exact pinned core загрузил два .srs за 1 114 мкс при 758 624 байтах allocations; lookup benchmark — 329 нс/op, 1 104 B/op, 2 allocs/op. Суммарный размер assets после добавления championat.com — 50 114 байт. В полном debug-прогоне API 36: extraction 2 мс, cold connect 41 мс, 40 реальных flows — 100 мс CPU и +356 КиБ PSS; API 26: 5 мс, 63 мс, 300 мс CPU и без роста PSS. Это закрывает автоматизированный performance-пункт, но не release-gate энергии на физических устройствах.
Этап 5 — ссылки, QR и подписки
Цель: основной пользователь импортирует конфигурацию без знания JSON.
I5-01Реализовать единыйImportParserбез отдельной внутренней модели маршрутизации.I5-02Поддержать обычный sing-box JSON и subscription, возвращающий JSON.I5-03Поддержать URI-листы минимум VLESS, VMess, Trojan, Shadowsocks, Hysteria2 и TUIC; каждый результат преобразовывать сразу в sing-box JSON.I5-04Поддержать plain/base64 subscription со списком известных URI; неизвестную строку не угадывать.I5-05Добавить URL import и ручное обновление subscription groups с preview до сохранения.I5-06Добавить QR scanner только по действию пользователя; камера запрашивается в момент открытия.I5-07Добавить импорт из буфера только после нажатия и импорт файла через системный picker.I5-08После успешного импорта не подключаться автоматически: предложить выбрать приложения, затем явную кнопку подключения.I5-09Перед запуском сканировать импортированный JSON наurltest, NTP, remote rule-set, external Clash controller, verbose log и explicit keepalive.I5-10Показывать одно предупреждение о фоновой/внешней активности; пользовательский JSON скрыто не очищать.I5-11Маскировать UUID, password, token, subscription query и URL credentials во всех preview/log/error.I5-12Не добавлять фоновое обновление подписок или ядра.I5-13При subscription refresh сохранять выбранный server tag, если он остался; иначе выбрать первый доступный и явно сообщить пользователю.I5-14Активный профиль после ручного refresh перезапускать только после подтверждения пользователя; не менять работающий server скрыто.I5-15Для одиночного URI предлагать «Новый профиль» или «Добавить в существующую managed-группу»; никогда не сливать профили автоматически.I5-16Раздельные профили одной подписки хранить как синхронизируемую группу: обновлять credentials с сохранением индивидуальных маршрутов, согласовывать добавления/удаления через preview и не воскрешать вручную удалённый профиль.
Отложенные форматы импорта
-
F-IMPORT-WGРеализовать строгий импорт WireGuard/AmneziaWG 2.0 native.confнапрямую в sing-boxendpoints, native/core tests и открытие через AndroidACTION_VIEW; unknown-field drop запрещён. -
F-IMPORT-01Проверить спрос и корректную семантику Clash YAML. Реальный спрос и риск silent field loss подтверждены; точный core parser не экспортирован в libbox, поэтому второй YAML-конвертер не входит в MVP. Решение и gate зафиксированы в политике форматов. -
F-IMPORT-02AСверить URI inventory точного ядра. Единственный отсутствующий URI из pinned parser — Hysteria v1; это кандидат, а не реализованная поддержка. -
F-IMPORT-02Собрать и проанализировать реальные неподдержанные ссылки: минимум 3 обезличенных Hysteria v1 URI и образцы пробелов текущих VLESS/SS/TUIC/Hysteria2 parser; затем выбрать по данным. -
F-IMPORT-03Добавлять Clash YAML только после полного gate из политики форматов; не импортировать Clash DNS/routes/groups.
Здесь [x] у исследовательского пункта означает завершённый аудит, а не
наличие формата в APK.
Тесты и Gate 5
- Golden tests для каждого URI, IPv6 host, percent/base64 encoding, transport, TLS, Reality и пустых/битых полей.
- Fuzz/property tests: parser не падает и не зависает на произвольном вводе.
- Integration: обычный и раздельный subscription update атомарен; битый ответ не меняет старые профили.
- Security: секреты отсутствуют в Logcat, crash message, notification и redacted export.
- Gate: путь URL/QR/clipboard/file → preview → JSON → выбор приложений → connect работает без raw editor.
Открытая часть Gate 5 требует реальной камеры/HTTPS subscription endpoint и физического
устройства. Redacted export из I6-15–I6-16 теперь проверен instrumented-тестом:
профиль, package list, endpoint, UUID/password/token и внешний IP в отчёт не попадают.
Полный security-gate всё ещё требует проверки Logcat/crash/notification на физическом
устройстве. Автотестами также подтверждены parser/preview, native CheckConfig,
атомарный malformed refresh и общая redaction ошибок/debug core log.
Этап 6 — продуктовый UI и диагностика
Цель: компактный Material You клиент, пригодный для ежедневного использования.
Главная и состояние
I6-01Реализовать компактную connection card: состояние, профиль, сервер, IP, ping и время.I6-01AПо нажатию на сервер открывать bottom sheet selector-группы с именем, протоколом, endpoint, отметкой текущего выбора и двумя честно подписанными измерениями: ICMP RTT до endpoint по основной сети Android и relay delay штатного sing-box URL-test через сам outbound. Одна проверка запускает оба метода без переключения selector; отсутствие ответа одного метода не скрывает результат другого.I6-02Использовать внутренний libbox traffic manager без external controller/listener.I6-03ПодписыватьCommandStatusс интервалом 1 секунда только пока главная в lifecycleSTARTED.I6-04Хранить 60 download/upload значений в кольцевом массиве текущей сессии.I6-05Никогда не включатьCommandConnections; boundedCommandLogоткрывать на время connect health-check и затем только на экране диагностики.I6-06IP запрашивать один раз после connect, ping — при connect и вручную.I6-07Уведомление не обновлять каждую секунду и не рисовать там скорость.
Главная и диагностика проверены 22 июля 2026 года: 85/85 JVM и текущие 67/67
instrumented на AVD API 36; базовая матрица 66/66 также пройдена на API 26/29.
Постоянным остаётся только event-driven CommandGroup;
CommandStatus создаётся при видимой главной, а отдельный CommandLog — на время
connect health-check и затем только при видимой диагностике. Оба непостоянных клиента
физически закрываются вне своего узкого lifecycle. CommandConnections отсутствует.
Остальные экраны
I6-08Завершить Профили: группы, source, last update, add actions и ошибки.I6-09Завершить Маршрутизацию: две карточки, rules, app picker, advanced JSON.I6-10Завершить Настройки: тема, DNS, Stable/Beta, Диагностика, Сообщество, О приложении.I6-10aДобавить выключенную по умолчанию сетевую автоматику: независимые Wi-Fi/cellular/Ethernet/other правила, bounded trusted-SSID list, fail-safe при redacted SSID, видимое состояние паузы без TUN и ручной override до смены сети.I6-10AДобавить отдельный экран «Скрытие VPN» и rootless runtime-модуль без нового process/thread/polling: localhost endpoints закрыты по умолчанию, stored JSON не меняется.I6-10BЗавершить physical gate MTU 1500 на IPv4/IPv6/NAT64/QUIC, операторах, OEM и энергии; initial device-test позволил включить default с явным откатом к profile/core.I6-10CПовторно проверить на физическом Android WireGuard/AWG после исправления: stable 0.2.3 доказал внешний TUN 9000 при endpoint 1280; stablev0.2.4задаётmin(1500, endpoint MTU), то есть 1280 для проверенного профиля. Владелец установил production APK на то же реальное устройство и подтвердил работу WireGuard и остальных использованных профилей. Полная OEM/оператор/IPv6 матрица остаётся вI6-10B/R8.I6-11Разместить Telegram-ссылки только в «Настройки → Сообщество».I6-12Добавить accessibility labels, нормальный back navigation, состояния loading/empty/error и крупные touch targets.I6-13Ограничить анимации стандартными Compose/Material; не добавлять тяжёлый dashboard.
Остальные экраны завершены 22 июля 2026 года без нового слоя навигации, DI или
зависимостей. Профили группируются только по UI metadata и показывают источник и
updated_at; base JSON остаётся источником transport/outbounds, а общая
destination-policy хранится как bounded DataStore intent. Маршрутизация имеет две
главные карточки: Android per-app scope и destination routing, а кнопка
«Исходный JSON профиля» открывает настоящий base-профиль и поясняет, что общая
destination-policy накладывается только при запуске. Выбор другого профиля при
подключённом VPN сразу заменяет работающую сессию выбранным профилем. Stable/Beta хранится как
UI-настройка и не запускает фоновые обновления; updater остаётся этапом 7.
Три Telegram URL присутствуют в production-коде только внутри экрана «Настройки → Сообщество». Подэкраны настроек, picker приложений и JSON-редактор обрабатывают системный Back; интерактивные строки имеют минимум 56–72 dp и понятные semantics. Собственных бесконечных/тяжёлых анимаций нет — используются только штатные состояния Compose/Material. Проверено в общей матрице: 85/85 JVM, 67/67 instrumented tests на API 36 и базовые 66/66 на API 26/29. Основные кнопки соединения имеют явный минимум 48 dp; этот инвариант и подписи нижней навигации проверяются smoke-тестом.
Диагностика
I6-14Показывать короткий тип ошибки и раскрываемые последние bounded log lines.I6-14AЗаписывать bounded event timeline каждой попытки подключения/restart: общая длительность, статусы этапов и самый долгий этап; только monotonic event timestamps, без ticker/polling.I6-14BАтомарно хранить только последний redacted uncaught Kotlin/Java crash вnoBackupFilesDir; runtime/core traffic logs на диск не писать. На API 30+ читать одну системную process-exit запись для native crash/ANR без копирования тяжёлого trace.I6-14CЗаменить head/tail startup-окно на priority ring: handshake/endpoint/TUN/error сохраняются, повторы схлопываются, один callback принимает не более 48 записей, а received/coalesced/dropped counters экспортируются.I6-15Создавать redacted diagnostic JSON только по действию пользователя.I6-16Экспортировать через Android Sharesheet/FileProvider и удалять временный файл при следующем запуске.I6-17Включать core/app version, Android/API, network state, Private DNS mode и effective overlay без секретов.
Диагностика не является отдельным Gradle-модулем и не добавляет постоянного фонового сборщика. CommandLog подписывается
отдельным клиентом на время connect health-check и при Activity STARTED с открытым
экраном. В памяти остаются три последние попытки, до 48 приоритетных startup core-записей
на каждую и отдельные 80 общих записей; повторы схлопываются, шум вытесняется раньше
handshake/TUN/errors, а counters честно показывают отброшенное. Клиент закрывается идемпотентно после успешной
проверки, вместе с экраном или service. Runtime-лог на диск не пишется. Effective overlay — структурная
сводка managed zapret-*: режим DNS, наличие dual-stack TUN, типы managed DNS,
количество правил/actions, локальные rule-set и bounded hardening-state без endpoint,
match values или секретов.
Вся suspend-цепочка connect/restart ограничена одним 30-секундным deadline. Timeout
завершается fail-close с VPN-120 и не создаёт периодический watchdog или retry-loop.
Отчёт версии 3 создаётся только кнопкой, содержит app/core revision+patch SHA-256, Android/API/ABI,
VPN/non-VPN network state, Private DNS, последнюю классифицированную ошибку, безопасный
overlay, bounded connection timeline, runtime resource/log counters, последний redacted app crash,
одну Android process-exit запись и bounded log lines. Он не содержит raw JSON, имя профиля, packages, внешний IP
или credentials. Единственный временный файл находится в cache/diagnostics/, доступен
через FileProvider с exported=false и read grant Sharesheet, а AppContainer удаляет
его при следующем запуске.
Полевой отчёт v1 от 22 июля 2026 года подтвердил живую сессию на Android 17/API 37:
Wi-Fi validated, managed DNS Automatic, Private DNS off, dual-stack TUN и VLESS/Reality без
зафиксированной ошибки. В захваченном steady-state трафике DNS lookup занимал 1–3 мс,
а выход VLESS на TLS/Reality — около 177–196 мс. Отчёт был создан через 83,7 с после
connected_at, а первая log line была получена через 78,1 с, поэтом v1 не доказывает
длительность первичного connect и не закрывает причину задержки. Для этого нужна одна
новая репродукция на APK с отчётом v2 и его connection timeline.
Тесты и Gate 6
- Compose UI tests для всех состояний главной и четырёх вкладок.
- Lifecycle test: после ухода с главной status ticker закрыт; после ухода из диагностики log stream закрыт.
- Rotation/process recreation не создаёт второй service/core и не теряет фактическое состояние VPN.
- Accessibility smoke test на светлой/тёмной теме и API 26/31+.
- Gate: полный путь «установил → импортировал → выбрал → подключил → диагностировал» не требует JSON.
Автоматизированный Gate 6 закрыт 22 июля 2026 года на AVD API 26 и API 36. UI-тесты
покрывают отсутствие профиля/приложений и состояния Stopped, Starting, Connected,
Stopping, Error, а также основные экраны всех четырёх вкладок. В светлой и тёмной
темах проверены accessibility labels нижней навигации и touch targets не меньше 48 dp.
Полный сценарий использует обычную Shadowsocks-ссылку из буфера: создаёт managed JSON
через preview, выбирает установленное приложение, поднимает настоящий Android TUN и
libbox, переживает rotation с тем же connectedAt и единственным core, затем открывает
диагностику. Сетевой health-result в тесте детерминирован, поэтому сценарий не зависит
от внешнего VPN-сервера, но profile parser, native CheckConfig, service, TUN и libbox
не подменяются. Raw JSON editor нигде не открывается.
Отдельный scripts/verify-process-recreation.sh проверяет hard process death. Android
в этом случае закономерно уничтожает service/TUN/core; новый процесс обязан показать
фактическое Stopped с нулевыми ресурсами, а следующее подключение — создать ровно один
service/core/TUN. Probe receiver существует только в debug source set, защищён системным
android.permission.DUMP, а CI дополнительно запрещает его присутствие в release APK.
Этап 7 — обновление APK и выпуск
Цель: безопасный Forgejo Release без динамического ядра и мусора.
I7-01Проверять Forgejo Releases один раз при запуске и вручную: Stable принимает только обычный release, Beta — любой prerelease; при обновлении показывать release notes.I7-02Скачать APK во внутренний cache, проверить опубликованный SHA-256 и передать системному installer.I7-03Проверять package name и совместимость подписи; не обещать silent install.I7-04Удалять скачанные/незавершённые APK после install handoff, отмены, ошибки и при следующем запуске; diagnostic temp-файлы — при следующем запуске.I7-05Никогда не скачивать core отдельно: libbox обновляется только вместе с APK.I7-06Настроить release workflow: exact core SHA → CLI/AAR → tests → APK → checksum → metadata.I7-07Хранить signing secrets только в локальном owner-only хранилище вне Git; сделать зашифрованную офлайн-копию ключа и инструкции восстановления.I7-08Публиковать release notes с app version, core tag, full core SHA, ABI и checksum.I7-09Проверить same-key upgrade с предыдущей версией и сохранение профилей/DataStore.I7-10Публиковать отдельныеarm64-v8a,armeabi-v7a,x86_64APK без чужих native-библиотек; updater выбирает первый совместимый ABI устройства.I7-11Вынести updater в направленныйapp-updaterlibrary-модуль. После retryable сетевой ошибки один раз повторять текущую проверку или загрузку через временный VPN runtime overlay, ограниченный package приложения и Forgejo-хостом; после операции восстанавливать предыдущее состояние VPN.
Updater проверяет выбранный канал один раз на запуск процесса и по кнопке, не имеет
scheduler/service и не выполняет периодический polling. Stable использует самый новый
не-prerelease, Beta — самый новый prerelease независимо от имени тега. Release считается
валидным при единственном поддерживаемом metadata-файле, полной матрице одно-ABI APK и отдельных
APK.sha256; metadata и checksum обязаны совпасть, а Forgejo asset после загрузки
скачивается обратно и побайтно сверяется с локальным файлом. Переходный schema-1
release-metadata.json указывает на arm64 APK для уже установленных старых клиентов. Разрешены только
HTTPS-адреса точного хоста git.zapret.moe и пути текущего репозитория с ограниченными redirect/размером/таймаутом.
app-updater не зависит от UI, профилей, libbox или VPN lifecycle. Интеграционный callback
в app включается только после сетевой ошибки/HTTP 403, 429, 451 или 5xx. Если VPN уже
работает, сервис выполняет контролируемый restart с временным package_name + domain_suffix
правилом; если VPN был выключен и consent уже выдан, временно запускается активный профиль.
После запроса исходное состояние восстанавливается в finally, включая отмену. Правило не
записывается в профиль и не влияет на Forgejo-трафик других приложений. Без consent или
активного профиля updater показывает обычную ошибку с причиной недоступности VPN-повтора.
После загрузки Android читает сам APK: package должен совпасть с установленным, versionCode
должен быть строго больше, versionName/versionCode — совпасть с metadata, minSdk — подходить,
а текущий сертификат — входить в доказанную signing history нового APK. Для multi-signer
требуется точное равенство набора. Только после этих проверок non-exported FileProvider
передаёт content URI штатному installer; silent install не используется. Частичный, битый,
отменённый или оставшийся после process restart APK удаляется из cache/updates.
Локальный stable publisher принимает строгий tag vMAJOR.MINOR.PATCH, выводит
детерминированный монотонный versionCode, собирает exact pinned core, тестирует,
подписывает отдельные arm64-v8a/armeabi-v7a/x86_64 APK постоянным owner-only ключом,
проверяет apksigner/manifest/единственность ABI и публикует APK, SHA-256, metadata и
notes один раз без замены уже опубликованных assets. После публикации он запускает
Forgejo Actions как независимую фоновую проверку и не ждёт её. Workflow не имеет
production key и ничего не публикует.
Постоянный ключ создан, проверен и хранится локально вне репозитория; публичный
fingerprint закреплён в release.properties. Локальный publisher, фоновый verification
workflow и инструкция готовы. Постоянный ключ не передаётся ни Forgejo,
ни runner-контейнеру.
Gate 7
- Неверный checksum, другая подпись, прерванная загрузка и downgrade обрабатываются безопасно.
- После update/cancel/restart cache не содержит старых APK.
- Release APK воспроизводимо содержит указанный core revision.
- Forgejo Release содержит APK, SHA-256 и достаточную информацию для независимой проверки.
Автоматизированный Gate 7 пройден на AVD API 26/36. scripts/verify-same-key-upgrade.sh
установил build 701001, записал настоящий профиль/DataStore/allowlist, обновил тем же ключом
до 701002 и подтвердил сохранность. Затем Android отклонил downgrade и переподписанный
другим ключом APK как VERSION_DOWNGRADE/UPDATE_INCOMPATIBLE, не повредив данные.
Unit/instrumented тесты отдельно проверяют rotation lineage, multi-signer, package/version,
неверный checksum, прерванный .part, installer cancel и startup cleanup. Публичные
v0.2.1-beta.30 и stable v0.2.1 содержат три ABI, отдельные SHA-256 и обе
metadata-схемы.
Этап 8 — пострелизная физическая матрица
Цель: постепенно расширять доказанную совместимость на реальных Android-устройствах и сетях. По решению владельца эта матрица выполняется пользователями после публикации MVP и не блокирует уже выпущенный stable; открытая галочка означает непроверенное сочетание, а не известную поломку.
Устройства и сеть
R8-01Реальное слабое устройство API 26/28.R8-02Android 10/API 29 AVD:DnsResolver, Private DNS boundary и 66/66 tests.R8-03Современный Android API 36 AVD: 66/66 tests; физическое устройство остаётся в energy/OEM gate.R8-04Wi-Fi, cellular, IPv4, IPv6/NAT64, captive portal и смена сети.R8-05Always-on/Lockdown читается через public API 29+, объясняется до TUN и не включается приложением.
Корректность
R8-06Повторить полную DNS matrix из DNS ADR.R8-07Повторить routing matrix из Routing ADR.R8-08Проверить каждый заявленный outbound отдельно на connect, UDP/QUIC иprotect(fd)loop.R8-09Проверить shared UID, удалённый package, пустые include/exclude и revoke.R8-10100 connect/stop и 50 Wi-Fi/mobile transitions пройдены на API 29/36 AVD без deadlock, duplicate restart и утечек.
Протокол и честная граница automated/physical прогона зафиксированы в
GATE8_RESULTS.md. R8-01, полный R8-04, R8-06…R8-09
остаются открытыми до реальных устройств/сетей/серверов.
CPU, память и батарея
R8-11Пять повторов: VPN off baseline и VPN idle с погашенным экраном. API 36 AVD 5/5 пройден; физическая energy-часть открыта.R8-12Невыбранное приложение передаёт фиксированный объём без пропорционального CPU/network роста Zapret KVN. На AVD 8 MiB дали median TUN=0; физический OEM-повтор открыт.R8-13Сравнить выбранныйdirectиproxy: CPU, throughput, RSS/PSS, GC и энергия. AVD CPU/throughput/RSS/PSS готовы; реальные proxy, GC trace и энергия открыты.R8-14Сравнить главную видимую/закрытую и diagnostics открыта/закрыта. Настоящие Compose-экраны AVD измерены; физическая энергия открыта.R8-15Измерить принятый managedparallelпротив контрольногоsequentialна уникальных именах. Test 17 уже доказал функциональную регрессиюsequentialпри hang; AVD 5×12 готов, физическая энергия/cache burst открыты.R8-16Сравнить текущийmixedstack сsystem; AVD-разница ниже 5%, default не изменён; физическая матрица открыта.R8-17Сравнить default MTU 1500 и profile/core; initial physical test дал заметный выигрыш, но IPv6/NAT64/QUIC, оператор/OEM и энергия открыты.R8-18СравнитьSetMemoryLimit(false)с экспериментальным GC=10: AVD CPU/PSS/RSS/throughput ниже порога; Go GC count/pause, OOM и физическая энергия открыты.R8-19Harness сохраняет System Trace, batterystats и 130 raw-файлов с SHA-256; manual CI загружает их artifact. Физические PowerMetric/ODPM входят в открытые R8-11…R8-18.R8-20Порог 5% применяется агрегатором и проверяется negative/positive self-test вverify-project.sh.
Точный AVD-протокол и граница оставшейся физической проверки находятся в
GATE8_RESULTS.md. Harness дополнительно прошёл сокращённую матрицу
5 × 16 на API 26; это только проверка minSdk-совместимости, не energy evidence. Ни один
production default по AVD-данным не изменён.
Security и release candidate
R8-21Secrets проверяются до export во внутреннем bounded-log, в redacted export, foreground notification и app-private temp/history после явного чтения clipboard; stale diagnostics/APK очищаются отдельными тестами. Уведомление принимает только закрытый enum состояний.R8-22Managed runtime удаляет все listener/UI/secret поляexperimental.clash_api, не меняя сохранённый JSON. Явный raw user JSON остаётся под контролем пользователя.R8-23Exact merged-manifest audit проверяет permission allowlist, все exported-компоненты, signature-permission AndroidX, FileProvider, backup/cleartext/process/VPN contract иDebug=false.R8-24Arm64 release проверяет APK/R8 mapping и exact native symbols; minified x86_64 release на API 36 прошёл 5 cold starts (median 407 ms) без process crash.R8-25Локальный minified RC прошёл clean install, same-key update, downgrade/signature rejection; production-signed stablev0.2.4установлен и запущен владельцем на реальном устройстве.
Финальный gate
- Нет failed fixture, unit или instrumented test: exact/compat fixtures 7/7, JVM 85/85, API 36 instrumented 67/67; API 26/29 baseline также пройден.
- Нет core revision/ABI/signature mismatch. Exact core и ABI проверены release-аудитом, same-key/foreign-key/downgrade tests пройдены, production-signed stable установлен на реальном arm64-устройстве.
- Нет app-owned WakeLock, alarm/job, скрытого polling или бесконечного reconnect. CI проверяет manifest и production sources; разрешён только lifecycle ticker видимой главной и одноразовый debounce смены сети.
- Managed idle не создаёт периодический сетевой трафик: 5/5 окон дали 0 UID RX+TX и 0 status/log clients; физическая energy-матрица остаётся отдельным R8 gate.
- Невыбранный трафик не проходит через TUN/libbox: 5 × 8 MiB дали 0 UID RX+TX Zapret KVN и median 0 TUN bytes (единственный интерфейсный шум — 96 bytes).
- Все известные ограничения синхронно перечислены в UI, README и генерируемых
release notes; это проверяет
scripts/verify-project.sh. - Первый production Forgejo Release опубликован; актуальный stable
v0.2.4доступен с тремя ABI, SHA-256 и updater metadata.
Публичный MVP завершён. Открытые R8-* и physical/energy gates из
GATE8_RESULTS.md являются пострелизной программой совместимости:
их закрываем только реальными пользовательскими evidence, но они не возвращают
статус уже принятого MVP в незавершённый.
После MVP — только по измерениям и запросам пользователей
Эти пункты не входят в текущий final gate и не реализуются «на всякий случай». Галочка означает не наличие кода, а прохождение указанного ниже отдельного activation gate.
F-01Whole-app block активирован 24 июля 2026 года по явному запросу пользователя: отдельный пустой по умолчанию список создаёт узкоеpackage_name → rejectвнутри TUN и не превращает Android include/exclude boundary в третий глобальный режим.F-02Clash YAML import. Требуется полный gate изIMPORT_FORMATS.md: libbox binding либо отдельное обоснованное решение о parser, минимум 10 реальных образцов и доказанное отсутствие silent field loss. DNS/routes/groups из Clash автоматически не сливать.F-03Планшетный/navigation rail layout. Активировать только после возврата планшетов в product scope и UI/accessibility matrix на compact/medium/expanded width.F-04Выборmixed/systemstack в GUI. AVD-разница ниже 5%; оставить upstreammixed, пока физические API 26/современный Android не покажут устойчивый выигрыш или несовместимость хотя бы одного режима.F-05MTU 1500 продвинут в default после initial device-test; экран сохраняет явный откат к profile/core. Полная физическая матрица IPv4, IPv6/NAT64, QUIC, PMTU, CPU и энергии остаётся вI6-10B. Это только внешний Android TUN: внутренний WireGuard/AWG endpoint без явногоmtuполучает Android-совместимое значение 1280; его physical gate отслеживается отдельно вI6-10C.F-06Patch owner/process lookup. Сначала P4 benchmark текущего core и отдельной экспериментальной сборки на реальных устройствах; требуются существенный повторяемый выигрыш, собственный patch hash и полный regression/release matrix.F-07FakeIP. Только явно экспериментальный режим с отдельной ADR: per-profile ranges/cache identity, очистка при stop/delete/network change и тесты, исключающие сохранение FakeIP вне TUN. Managed default остаётся без FakeIP.F-08Remote rule-set updater. Только после подтверждённого сценария, политики лицензий/version/SHA/rollback и явного ручного UX; не добавлять скрытый worker/polling и не объединять с APK updater.
Текущее состояние
- Архитектурный аудит завершён 22 июля 2026 года.
- Exact CLI: 7/7 fixtures.
- Compatibility CLI: 7/7 fixtures.
- Go packages compile/tests pass; дополнительный audit test временно встраивается в exact pinned fallback package и проверяет success/error/hang/RCODE.
- Markdown fences, локальные ссылки и SHA-256 fixtures проверены.
- Android/Gradle-проект создан.
- Debug и R8 release APK собраны.
- Runtime smoke пройден на эмуляторах API 26 и API 36.
- Повторный аудит I0-08–I0-14: exact origin/tag/SHA, рекурсивные fixtures, arm64 release и pinned CI actions.
- JVM unit tests: 165/165 в
:app:testDebugUnitTest; добавлены Auto DNS fallback, app-scoped health route, Forgejo updater/signing, security, Always-on/Lockdown policy и полная блокировка приложений. - Android instrumented tests: текущие 67/67 пройдены на API 36; базовые 66/66 — на API 26/29, security delta 3/3 — на API 26.
- Hard process recreation:
scripts/verify-process-recreation.shпройден на API 26/36; после смерти процесса ноль session/core/TUN/callback/client, после нового connect ровно один экземпляр. - Packaged
.srs: exact CLI RU/non-RU domain/IPv4/IPv6, manifest/license/SHA, atomic repair; 50 114 байт после добавленияchampionat.com, cold install 14 мс на AVD API 36. - Routing lookup/cold-start CPU/RAM measurements выполнены на AVD API 26/36 и exact core benchmark.
- Test 18 исторически собран локально из commit
e637391для arm64-v8a, armeabi-v7a и x86_64 и был опубликован в прежнем GitHub как отдельный prerelease с SHA-256/metadata. - Test 20 опубликован из commit
352fb31с редактируемым DNS override; Auto DNS fallback в него ещё не входил. - Test 21 собран локально из commit
d78ad72для arm64-v8a, armeabi-v7a и x86_64 и опубликован с новой цепочкой Auto DNS, SHA-256 и metadata; физическая проверка остаётся открытой. - Test 22 собран локально из commit
479a480для arm64-v8a, armeabi-v7a и x86_64 и опубликован с Markdown-оформлением release notes, SHA-256 и metadata. - По отчётам Test 21 подтверждён Android WireGuard GRO/standard-bind сбой: handshake и первый DNS проходят, затем return data зависает. Runtime-only direct detour переводит endpoint без явного
detourнаClientBind; health-route теперь обязателен во всех DNS-режимах, а профиль без DNS получает минимальный Android fallback. Exact pinned CLI и JVM regression tests проходят; физическая проверка остаётся открытой. - Beta 23 исторически опубликована из commit
6be23f4; проверка прежнего GitHub API показала, что старый updater сортировалtest.*выше более новогоbeta.*. Запись сохранена как история исправления. - Test 23 опубликован из commit
f602c19для arm64-v8a, armeabi-v7a и x86_64 с versionCode200123, тем же debug signer, SHA-256 и updater metadata. Он дополнительно сортирует prerelease поpublished_atи находится первым для Test 21/22. - Диагностика Test 23 доказала более широкий дефект pinned Android WireGuard: handshake и первый UDP DNS успешны, но TCP SYN/data-plane зависает; «Из JSON» давал ложный успех через direct IPv6. Android data-plane заменён без второго TUN на pinned vanilla/AWG движки metacubex.
- Test 24 опубликован из commit
ba395b4для arm64-v8a, armeabi-v7a и x86_64 с versionCode200124, тем же debug signer, SHA-256, updater metadata и точными версиями vanilla WireGuard/AmneziaWG data-plane модулей; физическая проверка новой архитектуры остаётся открытой. - ADB evidence Test 24 подтвердил исправный Android TUN/VPN lifecycle и самозакрытие ровно через 12 секунд в добавленном
wireguard_data_plane. Этот gate проверял доступность трёх публичных HTTPS-сайтов, а не handshake, дублировал общий health pipeline и скрывал core-log; он удалён. Общая HTTPS-проба теперь получает app-scoped TLS sniff и обязательный route в selected outbound, bounded core-log открывается до первой сетевой проверки. - Test 25 опубликован из commit
a1ee9cbдля arm64-v8a, armeabi-v7a и x86_64 с versionCode200125, прежним debug signer, новым patch SHA-256, per-APK checksums и updater metadata; Git tag указывает точно на проверенный app commit. - Evidence Test 25 исключил MTU как самостоятельную причину видеозависания: одинаковое поведение воспроизводится при внешнем TUN
1500и profile/core9000. Найден общий для raw/WireGuard/VLESS runtime-дефект: exact core безlog.levelвыбираетtrace; теперь runtime всех профилей ограниченwarn(или более строгим сохранённым уровнем),log.outputудаляется, а профиль на диске не меняется. - Test 26 опубликован из commit
9103ed1для arm64-v8a, armeabi-v7a и x86_64 с versionCode200126, прежним debug signer, per-APK checksums и updater metadata. Он изолированно проверяет новую runtime logging policy при неизменных core и WireGuard data-plane. - IPv4-only userspace WireGuard распознаётся по внутренним endpoint addresses.
Connect-time HTTPS health-check выбирает IPv4 через resolver VPN
Network, подключается к числовому адресу с исходными TLS SNI/certificate hostname/HTTP Host и возвращаетVPN-201, если требуемого семейства нет. Сохранённый JSON не переписывается; неиспользуемые DNS servers только явно предупреждаются. - Beta
v0.2.4-beta.1опубликована из commit4dff6b3с production-подписью для arm64-v8a, armeabi-v7a и x86_64. Runtime userspace WireGuard теперь задаёт внешний Android TUNmin(1500, endpoint MTU)во всех DNS-режимах; exact fixture, JVM, lint и release-candidate проверки проходят. Физическое подтверждение остаётся открытым пунктомI6-10C. - Stable
v0.2.4опубликован из commitec6aad4с versionCode200499, production-подписью и отдельными arm64-v8a, armeabi-v7a и x86_64 APK. Forgejo Release содержит SHA-256 и schema 1/2 metadata; tag указывает на exact локально проверенный source commit. - Idle CPU/battery проверены на физических устройствах; пункт пострелизный и закрывается по измерениям, а не субъективному времени работы.
Следующее действие: принимать пользовательские отчёты по stable v0.2.4,
воспроизводить только подтверждённые проблемы по ADB evidence и выпускать patch-релизы.
Постепенно закрывать физическую матрицу: captive portal, IPv6-only/NAT64,
camera/HTTPS subscription, blocked-DNS/LKG/DoH, OEM per-app/routing и энергия.
Также создать две зашифрованные офлайн-копии production signing key по SIGNING.md.