ZapretKVN-android/docs/IMPLEMENTATION_PLAN.md
loop-uh 4c57fe8b49
Some checks failed
Android CI / build (push) Failing after 15m33s
feat: unify official WG/AWG engine and preserve full UDP datagrams
2026-09-06 00:14:22 +03:00

79 KiB
Raw Permalink Blame History

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 2630.
  • 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 Первоначально выбрать для MVP arm64-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 Вызывать libbox CheckConfig() до сохранения изменённого профиля и показывать понятную ошибку.
  • 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 и selector zapret-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-группы без скрытой нормализации; managed zapret-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 Создать один foreground VpnService, VpnController и закрытый набор состояний подключения.
  • I2-07 Реализовать один service-lock, generation token и идемпотентный stop().
  • I2-08 Интегрировать libbox в том же Android process; вызвать Libbox.SetMemoryLimit(false).
  • I2-09 Настроить release Debug=false, managed log.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 2628.
  • 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; exact sequential не достигает резерва при зависшем основном 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: DNS reject + route reject; IP/CIDR block: route reject.
  • 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/route package_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 за 1114 мкс при 758624 байтах allocations; lookup benchmark — 329 нс/op, 1104 B/op, 2 allocs/op. Суммарный размер assets после добавления championat.com — 50114 байт. В полном 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-box endpoints, native/core tests и открытие через Android ACTION_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-15I6-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 секунда только пока главная в lifecycle STARTED.
  • I6-04 Хранить 60 download/upload значений в кольцевом массиве текущей сессии.
  • I6-05 Никогда не включать CommandConnections; bounded CommandLog открывать на время connect health-check и затем только на экране диагностики.
  • I6-06 IP запрашивать один раз после 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; stable v0.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; интерактивные строки имеют минимум 5672 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 занимал 13 мс, а выход VLESS на TLS/Reality — около 177196 мс. Отчёт был создан через 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_64 APK без чужих native-библиотек; updater выбирает первый совместимый ABI устройства.
  • I7-11 Вынести updater в направленный app-updater library-модуль. После 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-02 Android 10/API 29 AVD: DnsResolver, Private DNS boundary и 66/66 tests.
  • R8-03 Современный Android API 36 AVD: 66/66 tests; физическое устройство остаётся в energy/OEM gate.
  • R8-04 Wi-Fi, cellular, IPv4, IPv6/NAT64, captive portal и смена сети.
  • R8-05 Always-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-10 100 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 Измерить принятый managed parallel против контрольного sequential на уникальных именах. Test 17 уже доказал функциональную регрессию sequential при hang; AVD 5×12 готов, физическая энергия/cache burst открыты.
  • R8-16 Сравнить текущий mixed stack с 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-19 Harness сохраняет 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-21 Secrets проверяются до export во внутреннем bounded-log, в redacted export, foreground notification и app-private temp/history после явного чтения clipboard; stale diagnostics/APK очищаются отдельными тестами. Уведомление принимает только закрытый enum состояний.
  • R8-22 Managed runtime удаляет все listener/UI/secret поля experimental.clash_api, не меняя сохранённый JSON. Явный raw user JSON остаётся под контролем пользователя.
  • R8-23 Exact merged-manifest audit проверяет permission allowlist, все exported-компоненты, signature-permission AndroidX, FileProvider, backup/cleartext/process/VPN contract и Debug=false.
  • R8-24 Arm64 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 stable v0.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-01 Whole-app block активирован 24 июля 2026 года по явному запросу пользователя: отдельный пустой по умолчанию список создаёт узкое package_name → reject внутри TUN и не превращает Android include/exclude boundary в третий глобальный режим.
  • F-02 Clash 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/system stack в GUI. AVD-разница ниже 5%; оставить upstream mixed, пока физические API 26/современный Android не покажут устойчивый выигрыш или несовместимость хотя бы одного режима.
  • F-05 MTU 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-06 Patch owner/process lookup. Сначала P4 benchmark текущего core и отдельной экспериментальной сборки на реальных устройствах; требуются существенный повторяемый выигрыш, собственный patch hash и полный regression/release matrix.
  • F-07 FakeIP. Только явно экспериментальный режим с отдельной ADR: per-profile ranges/cache identity, очистка при stop/delete/network change и тесты, исключающие сохранение FakeIP вне TUN. Managed default остаётся без FakeIP.
  • F-08 Remote 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-08I0-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; 50114 байт после добавления 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 с versionCode 200123, тем же 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 с versionCode 200124, тем же 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 с versionCode 200125, прежним debug signer, новым patch SHA-256, per-APK checksums и updater metadata; Git tag указывает точно на проверенный app commit.
  • Evidence Test 25 исключил MTU как самостоятельную причину видеозависания: одинаковое поведение воспроизводится при внешнем TUN 1500 и profile/core 9000. Найден общий для raw/WireGuard/VLESS runtime-дефект: exact core без log.level выбирает trace; теперь runtime всех профилей ограничен warn (или более строгим сохранённым уровнем), log.output удаляется, а профиль на диске не меняется.
  • Test 26 опубликован из commit 9103ed1 для arm64-v8a, armeabi-v7a и x86_64 с versionCode 200126, прежним 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 опубликована из commit 4dff6b3 с production-подписью для arm64-v8a, armeabi-v7a и x86_64. Runtime userspace WireGuard теперь задаёт внешний Android TUN min(1500, endpoint MTU) во всех DNS-режимах; exact fixture, JVM, lint и release-candidate проверки проходят. Физическое подтверждение остаётся открытым пунктом I6-10C.
  • Stable v0.2.4 опубликован из commit ec6aad4 с versionCode 200499, 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.