Prepare Android release artifacts and listing (#112)
Android ARM verification / rust (push) Failing after 49m41s
Android ARM verification / android (push) Failing after 8s
Dependency security audit / rustsec (push) Failing after 22s

Add Rust distribution auditing, pinned post-build AAB signing, complete offline third-party notices, an Android privacy policy and reviewable Play listing assets. Preserve the application ID and audit both ARM ABIs, native symbols, baseline profiles and R8 mapping.

Fix dialog focus ownership, navigation-triggered cleanup loops and stale authentication errors after first-run setup, with native and real-activity regressions.

Google Play publication is deferred by the user. Issue #112 remains open for production signing, Console declarations and actual Play install/update verification.
This commit is contained in:
hugo committed 2026-10-15 16:16:01 +02:00
1 parent b54636b7bc
commit bf83a0ba41
51 files changed
+1493 -41

No files matched your search

+243
View File
@@ -0,0 +1,243 @@
# Android Play distribution — issue #112
Status: local release preparation. The user explicitly deferred Google Play
publication on 15 October 2026. #111 was audited and delivered in `b54636b`.
No Play release, accepted AAB, production signing lineage, or production install
has been established. Keep #112 open until its acceptance criteria are evidenced.
## Release identity and artifact gate
The application ID remains `de.rfc1437.ironstorage`. `android/release.properties`
is the single Android version source. Code 2 updates earlier code-1 builds when
signed with the same certificate; changing signing certificates cannot be fixed
by uninstalling a retained test app. The name remains 1.0.1. Before publishing,
check the highest code already allocated in Play and advance it if necessary.
The SDK catalog retains min 26 and target/compile 37. The only release ABIs are
`arm64-v8a` and `armeabi-v7a`.
The Rust `audit-distribution` command checks the decoded bundle manifest against
that version source and the SDK catalog. It rejects debug/test/profile flags,
audits every selected native ELF for 16 KB alignment, compares retained FULL
symbols with the bundle's native build IDs, requires both baseline-profile
files, and compares the retained R8 mapping with the bundled mapping byte for
byte. It writes deterministic SHA-256 provenance for the audited artifacts.
The existing APK manifest gate separately audits permissions, exported
components and all backup/transfer exclusions. These are artifact checks;
Google Play processing and installed signing identity require separate evidence.
## Signing and trust boundary
Upload signing and Play app signing are different keys. The upload certificate
only authorizes submission. Google Play signs delivered APKs with the app-signing
key selected in Play Console. Preserve that lineage for all production updates.
Never replace the retained emulator's debug-signed app with a differently signed
upload or Play APK by uninstalling it. Use a separate named AVD for Play installs.
The distribution command requires an external keystore and these process
variables: `IRONSTORAGE_UPLOAD_STORE_FILE`, `IRONSTORAGE_UPLOAD_STORE_PASSWORD`,
`IRONSTORAGE_UPLOAD_KEY_ALIAS`, `IRONSTORAGE_UPLOAD_KEY_PASSWORD`, and
`IRONSTORAGE_UPLOAD_CERT_SHA256` (64 hexadecimal digits, independently pinned).
Do not put passwords in shell arguments, command history, Gradle properties,
repository files, CI output, or artifacts. Disable shell tracing. Retain a secure
offline upload-key backup. The repository ignores `.jks` and `.keystore`; that is
not permission to place other private-key formats in the checkout.
Use the pinned JDK, SDK, Cargo and NDK from `android/README.md`. Run
`python3 .gitea/scripts/android_ci.py distribution`. It builds all ordinary
Android verification gates without signing variables in the build processes,
verifies the upload certificate, signs and validates
the AAB, and writes only release artifacts to `android/build/distribution`:
AAB, R8 mapping, selected native symbols, CycloneDX inventory, decoded manifest,
third-party notices, certificate fingerprint and checksummed provenance. Retained
notices must match the bundle asset byte for byte. It refuses to overwrite an
existing distribution directory. Archive each successful directory by commit
and version in access-controlled release storage before cleanup.
This checkout has no protected main-branch rule (read-only Gitea check on
2026-10-15). No production signing key, Play service account or signing secret
has been installed. Do not mistake a Gitea `environment` field or a branch
condition in an editable workflow for an approval boundary. Enable production
signing only on a trusted release runner after repository/runner access and
approval rules are configured. Keep signing credentials away from pull-request
jobs and untrusted workflow revisions. Keep any Play publisher credential scoped
to this app and the required testing/release track. Signing can be prepared
without granting the build system permission to publish.
## Console, policy and listing
Read-only browser checks on 2026-10-15 reached developer-account registration for
the two available consumer Google accounts. The project-domain Workspace account
cannot use Play Console under its current admin policy. No account was created,
terms accepted, identity information submitted, payment made or bundle uploaded.
An enrolled developer account with access to this app is required to continue
actual Play processing.
Current primary-source review (recheck when the release is submitted):
- [Target API policy](https://support.google.com/googleplay/android-developer/answer/11926878):
new phone apps and updates require API 36+ from 31 August 2026; target 37 meets
that numeric requirement, subject to Console accepting the installed SDK.
- [Play Console registration](https://support.google.com/googleplay/android-developer/answer/6112435/):
account enrollment includes a distribution agreement, registration payment and
identity verification. New personal accounts also have testing and device
verification requirements. Those account requirements are distinct from this
milestone's emulator-only product testing; do not claim an emulator satisfies
Google's account verification requirements.
- [App signing](https://developer.android.com/studio/publish/app-signing):
enroll in Play App Signing and record both app-signing and upload certificate
fingerprints before testing updates. No production key has been selected yet.
- [Developer verification](https://developer.android.com/developer-verification):
verify the publisher identity/package registration for applicable distribution
regions. Do not invent an organization, legal identity or verified status.
- [Data safety](https://support.google.com/googleplay/android-developer/answer/10787469):
off-device transmission is collection even when sent to a user-selected server;
locally processed data and qualifying end-to-end encrypted payloads have
exemptions. Do not declare all Git traffic end-to-end encrypted: commit
identity, paths and HTTPS authentication are visible to the selected server.
- [Broad package visibility](https://support.google.com/googleplay/android-developer/answer/10158779):
`QUERY_ALL_PACKAGES` requires a permitted-use declaration/review. It verifies
OS-mediated Autofill/Credential Manager caller certificates, including clients
without discoverable intent filters. It does not enumerate an installed-app
inventory or transmit one. See `credentials.md` for the security rationale.
Approval is pending; no policy exemption is assumed.
`android/play/listing.toml` contains the reviewable English listing draft.
The existing adaptive and monochrome launcher icons remain. `android/play/`
also contains a 512px Play icon, a 1024×500 feature graphic and four genuine
1080×1920 phone screenshots, with editable SVGs and unmodified capture sources.
Their PNG formats, dimensions, sizes and listing text limits were checked.
Tablet screenshots, the IARC-generated rating, final publisher contact details
and Console form submissions remain pending. Use actual app
screenshots with public demonstration data; never publish fixture private keys,
passphrases, tokens, repository locations, or a support diagnostic transcript.
The in-app offline privacy policy is the canonical Android policy and includes
support, third-party server traffic, local storage, camera, provider access,
clipboard and uninstall/backup behavior. Its source URL becomes public only
when the corresponding commit is pushed. Verify anonymous access before using
it in Console. The old Apple-only website policy must not be represented as an
Android policy. The dependency inventory is supplemented by packaged third-party
copyright and license notices, displayed offline in Preferences. The build checks
Rust notice coverage against both ARM normal dependency graphs and fails for
unreviewed Android license exceptions; see `android/licenses/README.md`.
Draft declarations for review against the final Console questionnaire:
- No ads, analytics, developer-side app account, tracking or crash collection.
- Optional HTTPS sends user-provided Git name/email, authentication identity and
unencrypted repository metadata to the configured host. Declare the applicable
optional personal-information/user-content categories; collection serves app
functionality. The encrypted GPG entry payload is unreadable to intermediaries
without recipient keys. Do not apply that exemption to plaintext Git metadata.
- Camera frames, biometric results and requesting-app certificates stay local.
User-confirmed provider/clipboard/QR actions transfer the selected content as
the user requests; review the user-initiated sharing exception accurately.
- HTTPS uses TLS and rejects cleartext/unsupported remotes. No developer data
deletion endpoint is needed for a nonexistent developer account/database;
local and server-side deletion limits are stated in the privacy policy.
- The app contains OpenPGP encryption and TLS. Complete the actual encryption/
export questionnaire using the publisher's jurisdiction and applicable
classification. No exemption or export classification has been filed.
## Install, update and failure evidence still required
Start on the internal track. Record Play processing/version, signing
fingerprints, ABI/SDK/permission inspection and pre-launch reports. Complete the
closed-testing requirements that the enrolled account actually imposes. A
production rollout must follow successful testing and the publisher's chosen
release regions; do not substitute a locally signed APK for Play acceptance.
On a dedicated ARM64 Play-capable emulator, install from Play into a new app
container through the normal setup UI. Create/import keys and a compatible
local store through supported flows, then exercise optional HTTPS clone/sync,
browsing/editing, TOTP, QR permission/transfer, biometrics and enabled Android
credential providers. Keep review secrets out of public listing material.
For update validation, retain that same AVD, package and signing lineage.
Record repository commits and entry counts, configuration, a usable protected
record and a populated TOTP cache before installing the next higher Play code.
Update through Play without clearing data. Verify each retained item and an
actual authorized decrypt/provider operation after update. Repeat on available
ARM64 API/page-size classes; ARMv7 gets package/native audit coverage, not an
invented runtime result. Physical devices and x86 are outside this milestone.
The app's existing Rust gates cover atomic private-storage writes, cancelled
operations, recovery of corrupt metadata without discarding repository data,
and current-enrollment Keystore invalidation. A failed install must leave the
old package/state usable; a failed operation must retain a recoverable prior
state and display its typed error. APK downgrade is not a recovery mechanism:
ship a corrected build with a higher code and a schema-compatible recovery path.
Do not use `adb install -d`, erase a container, restore opaque app-data packages,
or weaken authentication to force a test through. Uninstall is destructive and
not a migration test. State on the user's configured Git server is not removed
by uninstalling; ordinary entry deletion also leaves Git history.
## Validation record
Local preparation was validated on 15 October 2026. The implementation commit
is recorded on #112. These checks do not establish a delivered Play release.
- Required workspace formatting, warnings-as-errors check, Clippy and tests
passed: 526 Rust tests, 0 failures, 0 ignored across 41 suites. No Apple app or
project source changed; no iPhone simulator update was required.
- CI orchestration self-checks passed (14 tests). Android verification completed
with 838 actionable tasks, including unit tests, lint, KtLint, architecture,
instrumentation compilation, release R8/baseline packaging and native gates.
- The post-build signing command passed using an external **disposable validation
key**. Strict JAR verification against the pinned upload certificate,
bundletool 1.18.3 validation, decoded bundle identity/SDK/permission checks,
both ARM ABI/16 KiB audits, matching FULL symbols/R8 mapping/baseline profiles
and exact retained notice bytes all passed. This is not the production key
and was not uploaded. The generated notice asset covers 114 Android release
components and 423 Rust normal dependencies. AAR-embedded JAR notices were
inspected; JNA's embedded notices are covered by its retained release notice
and the complete Apache text, with its bundled libffi notice also retained.
- `SettingsScreenTest` passed all three methods on each ARM64 image: API 26 /
4 KiB, API 37 / 4 KiB, API 37 / strict 16 KiB. A real-activity regression first
reproduced document dialogs being dismissed by the background-lock cleanup.
Owned-window registration and protected dialog surfaces fixed the problem.
Tests verify normal dismissal preserves search and real pause/resume clears
the presentation. The earlier fixture-only settings test did not cover the
activity's real presentation object; it is supplemented, not counted as proof
of this path.
- Release QA also reproduced an endless protected-presentation rebuild when
navigating from Passwords to TOTP. Navigation-entry stop events were revoking
the root while that root was being rebuilt. Passwords and TOTP now observe the
protected activity's lifecycle for background cleanup. The navigation test
first failed with a recomposition timeout, then passed with the correction.
It requires a normally configured store and asserts actual Passwords content;
a separate test checks immediate masking on stop and clean reveal on resume.
- A normal first-run setup on the API 26 optimized app found a stale
authentication configuration error after successful store creation. Setup
completion now refreshes Rust-provided authentication state after an existing
request settles. The isolated native entry integration test first failed
without the refresh, then passed with it, including encrypted entry operations
and confirmation that setup does not grant an unlock lease. The API 26 demo
store was created through the app's normal key-generation/setup UI.
- The final focused UI checks passed on configured ARM64 stores at API 26 /
4 KiB, API 37 / 4 KiB and API 37 / strict 16 KiB: 14 executed tests per image,
with two opt-in interactive inspection fixtures excluded per image. On the
main image, the two real-store regressions were explicitly rerun with zero
skips after correcting their precondition to accept Rust's ready/locked shell
state as well as completed onboarding state. The final native setup/entry
regression also passed. Actual optimized-app computer use verified
Passwords-to-TOTP navigation on the main and older images and the strict
16 KiB image, retaining the existing stores and cached TOTP navigation.
- Negative checks against the real candidate reject mismatched retained notices
and R8 mapping, and emit no successful provenance for either mismatch.
- The optimized APK generated from the final AAB passed APK alignment/strip/
matching-symbol audits and installed with `adb install -r` using the retained
emulator's existing debug signing certificate. Computer use confirmed the
unchanged three-commit, clean local repository, version 1.0.1, loaded privacy
policy including deletion/backup text, and responsive offline notices. Both
documents remained open and closed normally. Cold launch also succeeded.
This is a local release-equivalent update, not a Play install or production
signing-lineage test.
- No app uninstall, container/key reset, opaque app-data restore or ordinary
biometric re-enrollment was used. The three unused AVDs were shut down
gracefully after validation; the visible retained API 37 AVD remains running.
The actual Play acceptance, enrolled developer identity, production signing
configuration/automation, live listing/assets/forms and clean/update smoke tests
from Play remain outstanding. #112 cannot be closed on local packaging or
emulator checks alone. Keep the working changes for this issue's eventual single
commit; do not present the disposable-key candidate as a production release.