Define the native Android architecture, Kotlin language, support matrix, and dependency policy #88

Closed
opened 2026-08-17 19:08:06 +00:00 by hugo · 1 comment
Owner

Goal

Record the implementation contract for a full native Android version of IronStorage before feature work begins.

Decisions to adopt

  • Use Kotlin as the Android application language and Gradle Kotlin DSL for the build. Do not use Java as the primary language and do not build the UI in Rust, Flutter, React Native, or a WebView.
  • Keep crates/storage as the sole owner of password-store, OpenPGP, Git, synchronization, conflict, entry, password-generation, OTP, QR-payload, identity, HTTPS-credential, and secure-secret policy.
  • Expose storage-owned commands and view-ready typed state to Kotlin through a thin Rust UniFFI crate. Kotlin owns Android lifecycle, controls, layout, accessibility, permissions, and operating-system API adapters only.
  • Use a single-activity, Jetpack Compose, unidirectional-data-flow architecture. Screen-level AndroidX ViewModels expose immutable StateFlow state and accept UI events; coroutines perform blocking bridge calls off the main thread. Hilt provides dependency injection.
  • Set minSdk to API 26 so the supported range has the Android Autofill Framework. Compile and target the current stable/Play-required SDK at implementation time. Release native ABIs are arm64-v8a and armeabi-v7a under the user-directed ARM support decision. All native libraries must support current Android 16 KB page-size requirements.
  • Support phones, tablets, foldables, multi-window, desktop windowing, portrait/landscape, light/dark/system themes, hardware keyboards, and process recreation. Android Auto, TV, Wear OS, and XR-specific experiences are outside scope.
  • Apple Watch selection, snapshot, connectivity, and watchOS UI are explicitly outside the Android product. The Android app must not show empty Watch controls inherited from shared mobile models.

Approved library and platform map

  • UI: Jetpack Compose BOM, Material 3, Material 3 Adaptive, Activity Compose, Navigation 3 (or the current stable AndroidX Compose navigation API if Navigation 3 is not stable when implementation starts), Lifecycle ViewModel/Runtime Compose, collectAsStateWithLifecycle.
  • Concurrency/state: Kotlin coroutines, Flow/StateFlow, and kotlinx-coroutines-test.
  • Dependency injection: Hilt/Dagger with KSP.
  • Rust bridge: UniFFI Kotlin bindings with Android mode enabled, its required JNA Android artifact, Cargo/NDK cross compilation (prefer cargo-ndk), and generated immutable records.
  • Secure secrets and authentication: Android Keystore framework APIs, AES-GCM keys with the required authentication policy, AndroidX Biometric/BiometricPrompt, and KeyguardManager. Do not use deprecated EncryptedSharedPreferences or MasterKey.
  • QR camera: CameraX Preview and ImageAnalysis. Camera frames are passed to Rust-owned decoding/envelope parsing; Kotlin and ML Kit must not interpret key or OTP payloads.
  • External credential integration: AndroidX Credentials provider APIs on Android 14+ plus Android AutofillService from API 26 for compatible apps and older releases.
  • Deferred guaranteed work: WorkManager only where an operation is explicitly safe and useful after the initiating screen/process disappears. Interactive unlock, secret display, and repository mutations stay foreground and cancellable.
  • Testing/quality: JUnit, AndroidX Test, Compose UI Test, coroutines-test, instrumented tests, Macrobenchmark and Baseline Profiles where measurements justify them, Android Lint, and a Kotlin formatter/static-analysis rule set selected and pinned in the version catalog.
  • Platform APIs: Android ClipboardManager, FLAG_SECURE, app-private files/no-backup directories, backup data-extraction rules, connectivity APIs, and system permission contracts.

Do not add Room, Retrofit/OkHttp, JGit, Bouncy Castle/OpenPGP, an OTP library, a password generator, an entry parser, or a second QR payload format for domain behavior already provided by Rust. DataStore may hold presentation-only Android state only; shared/domain preferences continue through storage-owned configuration APIs.

Deliverables

  • Add an Android architecture decision record and a feature-to-owner/library matrix.
  • Document module boundaries, allowed dependency directions, SDK/ABI policy, threat model, update/backup behavior, and the rule for selecting stable dependency versions.
  • Add a parity matrix against the current iPhone app and shared Rust mobile API, excluding only Apple Watch behavior.
  • Link every Android milestone issue to the owning layer and approved library/API.

Acceptance criteria

  • A contributor can determine the language, UI toolkit, bridge, security APIs, supported Android range, ABIs, and owner of every feature without guessing.
  • The design preserves the repository architecture boundary and contains no application subprocess path.
  • Each approved dependency has a single stated purpose, license review, maintenance check, and version-pinning/update policy.
  • The parity matrix has no unexplained mobile feature gap and marks Watch-only state as excluded rather than stubbed.
  • This issue remains documentation/planning work until implementation begins; no Android feature should choose a competing architecture without updating the decision record.

Part of #87.

Android support update (2026-10-08)

User-directed scope: Android release and CI targets are arm64-v8a and armeabi-v7a. x86_64 Android is excluded for now. Validation for every Android milestone issue uses emulator devices only; physical-device testing and physical-device checklists are excluded. Record the limits of available ARM emulator/API images without claiming physical-device coverage.

## Goal Record the implementation contract for a full native Android version of IronStorage before feature work begins. ## Decisions to adopt - Use **Kotlin** as the Android application language and Gradle Kotlin DSL for the build. Do not use Java as the primary language and do not build the UI in Rust, Flutter, React Native, or a WebView. - Keep `crates/storage` as the sole owner of password-store, OpenPGP, Git, synchronization, conflict, entry, password-generation, OTP, QR-payload, identity, HTTPS-credential, and secure-secret policy. - Expose storage-owned commands and view-ready typed state to Kotlin through a thin Rust UniFFI crate. Kotlin owns Android lifecycle, controls, layout, accessibility, permissions, and operating-system API adapters only. - Use a single-activity, Jetpack Compose, unidirectional-data-flow architecture. Screen-level AndroidX ViewModels expose immutable StateFlow state and accept UI events; coroutines perform blocking bridge calls off the main thread. Hilt provides dependency injection. - Set minSdk to API 26 so the supported range has the Android Autofill Framework. Compile and target the current stable/Play-required SDK at implementation time. Release native ABIs are arm64-v8a and armeabi-v7a under the user-directed ARM support decision. All native libraries must support current Android 16 KB page-size requirements. - Support phones, tablets, foldables, multi-window, desktop windowing, portrait/landscape, light/dark/system themes, hardware keyboards, and process recreation. Android Auto, TV, Wear OS, and XR-specific experiences are outside scope. - Apple Watch selection, snapshot, connectivity, and watchOS UI are explicitly outside the Android product. The Android app must not show empty Watch controls inherited from shared mobile models. ## Approved library and platform map - UI: Jetpack Compose BOM, Material 3, Material 3 Adaptive, Activity Compose, Navigation 3 (or the current stable AndroidX Compose navigation API if Navigation 3 is not stable when implementation starts), Lifecycle ViewModel/Runtime Compose, `collectAsStateWithLifecycle`. - Concurrency/state: Kotlin coroutines, Flow/StateFlow, and `kotlinx-coroutines-test`. - Dependency injection: Hilt/Dagger with KSP. - Rust bridge: UniFFI Kotlin bindings with Android mode enabled, its required JNA Android artifact, Cargo/NDK cross compilation (prefer `cargo-ndk`), and generated immutable records. - Secure secrets and authentication: Android Keystore framework APIs, AES-GCM keys with the required authentication policy, AndroidX Biometric/`BiometricPrompt`, and `KeyguardManager`. Do not use deprecated EncryptedSharedPreferences or MasterKey. - QR camera: CameraX Preview and ImageAnalysis. Camera frames are passed to Rust-owned decoding/envelope parsing; Kotlin and ML Kit must not interpret key or OTP payloads. - External credential integration: AndroidX Credentials provider APIs on Android 14+ plus Android `AutofillService` from API 26 for compatible apps and older releases. - Deferred guaranteed work: WorkManager only where an operation is explicitly safe and useful after the initiating screen/process disappears. Interactive unlock, secret display, and repository mutations stay foreground and cancellable. - Testing/quality: JUnit, AndroidX Test, Compose UI Test, coroutines-test, instrumented tests, Macrobenchmark and Baseline Profiles where measurements justify them, Android Lint, and a Kotlin formatter/static-analysis rule set selected and pinned in the version catalog. - Platform APIs: Android `ClipboardManager`, `FLAG_SECURE`, app-private files/no-backup directories, backup data-extraction rules, connectivity APIs, and system permission contracts. Do not add Room, Retrofit/OkHttp, JGit, Bouncy Castle/OpenPGP, an OTP library, a password generator, an entry parser, or a second QR payload format for domain behavior already provided by Rust. DataStore may hold presentation-only Android state only; shared/domain preferences continue through storage-owned configuration APIs. ## Deliverables - Add an Android architecture decision record and a feature-to-owner/library matrix. - Document module boundaries, allowed dependency directions, SDK/ABI policy, threat model, update/backup behavior, and the rule for selecting stable dependency versions. - Add a parity matrix against the current iPhone app and shared Rust mobile API, excluding only Apple Watch behavior. - Link every Android milestone issue to the owning layer and approved library/API. ## Acceptance criteria - A contributor can determine the language, UI toolkit, bridge, security APIs, supported Android range, ABIs, and owner of every feature without guessing. - The design preserves the repository architecture boundary and contains no application subprocess path. - Each approved dependency has a single stated purpose, license review, maintenance check, and version-pinning/update policy. - The parity matrix has no unexplained mobile feature gap and marks Watch-only state as excluded rather than stubbed. - This issue remains documentation/planning work until implementation begins; no Android feature should choose a competing architecture without updating the decision record. Part of #87. ## Android support update (2026-10-08) User-directed scope: Android release and CI targets are **arm64-v8a** and **armeabi-v7a**. x86_64 Android is excluded for now. Validation for every Android milestone issue uses emulator devices only; physical-device testing and physical-device checklists are excluded. Record the limits of available ARM emulator/API images without claiming physical-device coverage.
hugo added this to the 06 - Implement an Android version of the software milestone 2026-08-17 19:08:06 +00:00
hugo added the enhancement label 2026-08-17 19:08:06 +00:00
Author
Owner

Implemented in commit 30d9119 (pushed to main).

Added docs/android/architecture.md and docs/android/parity.md, linked from README.md and DEPENDENCIES.md. The contract specifies Kotlin/Compose/StateFlow/Hilt/KSP, Rust domain ownership and shared UniFFI boundaries, five Gradle modules and dependency directions, stable application identity, SDK/ABI/16 KB policy, approved dependency purposes/licenses/maintenance and version-update gates, threat model, and atomic private-state/backup/update/recovery behavior.

Reviewed the current iPhone app, AutoFill extension, Apple bridge and storage mobile APIs. The parity matrix assigns every non-Watch workflow and all 25 open Android milestone issues to its Rust owner and approved Android API, explicitly excludes Watch-only behavior, and records pending implementation without claiming shipped parity.

Acceptance review: all five #88 criteria are covered. The user explicitly confirmed that #88 should be completed as its stated documentation/planning issue; no artificial runtime Rust code was added. Subsequent functional issues still require full Rust/Kotlin implementation.

Validation: cargo fmt --all -- --check; RUSTFLAGS="-D warnings" cargo check --workspace --all-targets; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace (409 tests passed). The first sandboxed test run could not bind local SSH fixture sockets; the full rerun with local socket access passed. Live milestone links and local document links were checked. SDK policy was checked against upstream documentation and installed non-preview API 37 package metadata.

No iPhone or Android application code changed, so simulator execution is not applicable to this planning issue. No simulator/app data was modified. Future Android/physical-device acceptance remains assigned to the implementation/audit/release issues.

Implemented in commit 30d9119 (pushed to main). Added docs/android/architecture.md and docs/android/parity.md, linked from README.md and DEPENDENCIES.md. The contract specifies Kotlin/Compose/StateFlow/Hilt/KSP, Rust domain ownership and shared UniFFI boundaries, five Gradle modules and dependency directions, stable application identity, SDK/ABI/16 KB policy, approved dependency purposes/licenses/maintenance and version-update gates, threat model, and atomic private-state/backup/update/recovery behavior. Reviewed the current iPhone app, AutoFill extension, Apple bridge and storage mobile APIs. The parity matrix assigns every non-Watch workflow and all 25 open Android milestone issues to its Rust owner and approved Android API, explicitly excludes Watch-only behavior, and records pending implementation without claiming shipped parity. Acceptance review: all five #88 criteria are covered. The user explicitly confirmed that #88 should be completed as its stated documentation/planning issue; no artificial runtime Rust code was added. Subsequent functional issues still require full Rust/Kotlin implementation. Validation: cargo fmt --all -- --check; RUSTFLAGS="-D warnings" cargo check --workspace --all-targets; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace (409 tests passed). The first sandboxed test run could not bind local SSH fixture sockets; the full rerun with local socket access passed. Live milestone links and local document links were checked. SDK policy was checked against upstream documentation and installed non-preview API 37 package metadata. No iPhone or Android application code changed, so simulator execution is not applicable to this planning issue. No simulator/app data was modified. Future Android/physical-device acceptance remains assigned to the implementation/audit/release issues.
hugo closed this issue 2026-10-08 07:35:39 +00:00
Sign in to join this conversation.