ZapretKVN-android/docs/SIGNING.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

8.5 KiB
Raw Permalink Blame History

Подпись Zapret KVN Android

Release APK всегда должен быть подписан одним постоянным ключом. Stable собирается и публикуется локально владельцем; production JKS и пароли не передаются в Forgejo Actions и не добавляются в Git.

Однократное создание ключа офлайн

На отключённой от сети машине с JDK 17:

keytool -genkeypair \
  -keystore zapret-kvn-release.jks \
  -alias zapret-kvn-release \
  -keyalg RSA -keysize 4096 -validity 10000
sha256sum zapret-kvn-release.jks > zapret-kvn-release.jks.sha256
gpg --symmetric --cipher-algo AES256 zapret-kvn-release.jks
keytool -exportcert -rfc -keystore zapret-kvn-release.jks -alias zapret-kvn-release \
  | openssl x509 -outform DER \
  | sha256sum

Сделать минимум две офлайн-копии zapret-kvn-release.jks.gpg и checksum на разных носителях. Проверить восстановление на третьем временном носителе командами gpg --decrypt и sha256sum -c, затем безопасно удалить расшифрованную проверочную копию. Потеря ключа означает невозможность обновить уже установленное приложение.

Локальное приватное хранилище

По умолчанию локальный publisher читает каталог ~/.zapret-kvn-signing с правами 700. Файлы внутри должны иметь права 600:

  • zapret-kvn-release.jks — постоянный production key;
  • certificate-sha256.txt — SHA-256 сертификата;
  • signing-secrets.env — owner-only env-файл с ANDROID_SIGNING_STORE_PASSWORD, ANDROID_SIGNING_KEY_ALIAS, ANDROID_SIGNING_KEY_PASSWORD и ANDROID_SIGNING_CERT_SHA256.

Для совместимости publisher также понимает историческое имя github-secrets.env, но файл по-прежнему хранится только локально и не передаётся Forgejo.

Другой каталог можно явно задать через ZAPRET_SIGNING_DIR. Публичный fingerprint закреплён в release.properties; publisher до сборки требует точного совпадения JKS, приватного metadata-файла, env-файла и публичного значения. scripts/ci-build.sh автоматически отключает Gradle configuration cache, когда переданы ZAPRET_SIGNING_*, чтобы пароли не попали в повторно используемый Gradle state.

Финальный approval, версии и публикация

Перед проверками и финальными коммитами согласованного Windows/Android выпуска из checkout Windows выполняется python3 scripts/prepare_core_release.py --windows-version X.Y.Z --android-tag vA.B.C --android-root /path/to/ZapretKVN. Скрипт проверяет официальные теги Amnezia и создаёт одинаковый core-release-freeze.json в обоих репозиториях. Повтор той же пары версий лишь проверяет фиксацию, без нового выбора upstream. Android publisher требует, чтобы tag и закреплённые исходники соответствовали этой фиксации; изменившийся API или несовместимый UDP-патч нужно исправить и проверить до публикации.

После закрытия всех обязательных host-пунктов финального gate stable выпускается из чистого main, который точно совпадает с origin/main. Проверка production-signed ABI-specific APK на физическом Android-устройстве даёт дополнительное подтверждение, но не является обязательным условием публикации; её фактический статус всегда указывается в release notes:

git tag -a vMAJOR.MINOR.PATCH -m "Zapret KVN MAJOR.MINOR.PATCH"
scripts/publish-local-stable.sh vMAJOR.MINOR.PATCH --final-gate-approved

Скрипт повторно fetch-ит origin/main, при необходимости отправляет только уже созданный tag, запускает локальные тесты и release-аудиты, собирает и подписывает три ABI, проверяет bundle и создаёт приватный draft Forgejo Release. Восемь assets загружаются по одному с ограничением времени и повторами; после каждой загрузки файл скачивается обратно из Forgejo и его размер и SHA-256 сравниваются с локальным файлом. Release становится публичным только после точного совпадения всего набора. Существующий опубликованный Release или несовпадающий tag никогда не заменяется.

  • stable tag: vMAJOR.MINOR.PATCH;
  • beta tag: vMAJOR.MINOR.PATCH-beta.N, где N от 1 до 98;
  • versionCode вычисляется детерминированно; stable получает slot 99 и поэтому новее beta той же версии;
  • опубликованные assets не заменяются: исправление выпускается только новым tag.

Лимит APK проверяется по фактическому ABI: 96 МиБ для arm64-v8a и armeabi-v7a, 112 МиБ для x86_64 (согласован владельцем после встраивания официального Xray). Native-библиотеки остаются несжатыми для прямой загрузки.

Локальный publisher публикует подписанные arm64-v8a, armeabi-v7a, x86_64 APK, отдельные .sha256 и release-metadata-v2.json с package, version, точным commit ядра, signing fingerprint и размером APK.

После публикации он запускает release-verify.yml и сразу завершается, не ожидая workflow. Forgejo Actions ничего не публикует и не имеет production key: он собирает исходники с одноразовым тестовым ключом, скачивает опубликованные локально assets и независимо проверяет metadata, checksum, ABI, версии и production fingerprint. ADB-проверки на эмуляторе и реальном устройстве необязательны и не отмечаются как пройденные, если фактически не запускались; instrumentation APK при этом должен успешно собираться.

Возобновление прерванной публикации

Не удалять build/local-release/TAG, draft, remote assets или tag и не использовать --clobber. Повторный запуск той же команды сначала полностью проверит сохранённый bundle, затем продолжит только draft с тем же tag: совпавшие по размеру и фактическому SHA-256 скачанных из Forgejo файлов будут пропущены, отсутствующие — загружены по одному.

Если remote asset существует, но отличается размером или содержимым, publisher остановится. Такой asset не удаляется и не заменяется; исправление выпускается новым tag. Именно пакетная загрузка восьми файлов одной операцией зависла при публикации v0.2.12, тогда как отдельные загрузки APK завершились за несколько секунд, поэтому batch-upload для stable запрещён.