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

Open
opened 2026-08-17 19:08:06 +00:00 by hugo · 0 comments
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, armeabi-v7a, and x86_64 unless a documented dependency audit proves one infeasible. 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.

## 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, armeabi-v7a, and x86_64 unless a documented dependency audit proves one infeasible. 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.
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
Sign in to join this conversation.