From 30d91193028066b9a4e96a2d1f43facd7c22b04b Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Thu, 8 Oct 2026 09:35:03 +0200 Subject: [PATCH] Define native Android architecture and parity contract (#88) --- DEPENDENCIES.md | 8 ++ README.md | 5 + docs/android/architecture.md | 211 +++++++++++++++++++++++++++++++++++ docs/android/parity.md | 92 +++++++++++++++ 4 files changed, 316 insertions(+) create mode 100644 docs/android/architecture.md create mode 100644 docs/android/parity.md diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 897984f..128ec6a 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -100,3 +100,11 @@ The checked-in compatibility suite completes the earlier OpenPGP backend gate: `pgp` imports protected armored and binary exports, decrypts every GnuPG-audited fixture, emits independently decryptable single- and multi-recipient messages, and verifies the detached recipient signatures. + +## Android dependency policy + +The approved Android library/API purposes, license review, maintenance checks, +version pinning and update gates are recorded in +[`docs/android/architecture.md`](docs/android/architecture.md#approved-dependencies-and-review-policy). +The Gradle catalog and resolved dependency/license records must identify actual +shipped versions before release; this approval matrix does not replace that audit. diff --git a/README.md b/README.md index 2e87325..a95ad5e 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,11 @@ Rollback-safe remove, move, and copy transactions, destination rules, and selective subtree reencryption are documented in [`docs/tree-mutations.md`](docs/tree-mutations.md). +The native Android architecture, support policy and complete mobile parity/issue +map are recorded in [`docs/android/architecture.md`](docs/android/architecture.md) +and [`docs/android/parity.md`](docs/android/parity.md). Android implementation +belongs to milestone 06; the decision record does not claim a shipped client. + ## Project layout ```text diff --git a/docs/android/architecture.md b/docs/android/architecture.md new file mode 100644 index 0000000..5c57eef --- /dev/null +++ b/docs/android/architecture.md @@ -0,0 +1,211 @@ +# Android architecture decision + +Accepted implementation contract for [#88](https://git.rfc1437.de/hugo/IronStorage/issues/88) +in milestone 23, **06 - Implement an Android version of the software**. +Reviewed against the checkout's iPhone app, AutoFill extension, and shared Rust +mobile APIs on 2026-10-08. The milestone goal is full native Android feature +parity. This decision records ownership and release requirements; it does not +claim that the Android implementation or device audit already exists. + +## Language and presentation + +Use Kotlin, Gradle Kotlin DSL, one activity, Jetpack Compose, Material 3 and +Material 3 Adaptive. Use Navigation 3: its [stable release is 1.2.0](https://developer.android.com/jetpack/androidx/releases/navigation3). +Screen ViewModels expose immutable StateFlow state and accept typed events. +Compose observes with collectAsStateWithLifecycle. Hilt supplies dependencies; +KSP runs its processors. Blocking UniFFI calls run on a bounded background +coroutine dispatcher. Cancellation cancels the Rust operation as well as the +coroutine; a late result must not restore a revoked lease or stale screen. + +Rust owns repository layout, config, keys, OpenPGP, Git, conflicts, documents, +generation, OTP, QR payloads, identities, external access permissions and +secure-secret orchestration. Kotlin collects input, renders Rust state, and +adapts OS mechanics. No app component launches a subprocess. No Kotlin feature +reads repository files, parses entries/URIs or error labels, generates passwords +or codes, or independently decides credential matches or authentication policy. + +Use typed semantic icons/actions, stable IDs, generation/revision tokens, +view-ready records and typed errors. #91 removes SF Symbol and Watch assumptions +from shared phone contracts. Keep Swift compatibility through mechanical +adapters. #92 generates Kotlin and Swift from one shared UniFFI surface rather +than copying crates/apple conversions. A thin Android bridge may adapt platform +callbacks; it must contain no password-store decisions or handwritten unsafe +Rust. Generated/dependency FFI stays isolated from project-authored safe Rust. + +## Modules and allowed dependencies + +| Module | Responsibility | Allowed project dependencies | +| --- | --- | --- | +| android/app | Activity, composition root, navigation, manifest and service registration | features, core, platform | +| android/features | Related Compose workflows, screen ViewModels, accessibility | core only | +| android/core | Android-facing facade, immutable presentation contracts, coroutine scheduling, bridge mapping | rust-bridge | +| android/platform | Keystore, BiometricPrompt, clipboard, camera, paths and OS request translation | core contracts | +| android/rust-bridge | Generated UniFFI Kotlin and ABI native packaging | shared Rust bridge via build tasks | +| Shared Rust bridge | Mechanical records, lifetime/callback/error conversion | crates/storage | +| crates/storage | All domain behavior and trust-boundary validation | audited Rust dependencies | + +Start with these five Gradle modules; split feature groups only when useful. +Platform implementations are injected at app composition. Feature code cannot +import generated UniFFI/JNA types or depend directly on platform/bridge modules. +OS services delegate to core; exported service input is untrusted. Only Rust +holds repository capabilities. Gradle dependency checks, source-boundary checks, +and release manifest inspection are gates in #89/#94/#111, not substitutes for +review. Generated bindings and native/build outputs are not checked in. + +## Support and build policy + +Production package, namespace and application ID: **de.rfc1437.ironstorage**. +Updates retain this ID and signing lineage. Tests use derived IDs; a development +variant may not masquerade as the production signed installation. + +Minimum SDK: **26** (Android 8, including Autofill). Compile/target SDK: **37** +(Android 17) at this decision, following the [SDK instructions](https://developer.android.com/about/versions/17/setup-sdk). +Recheck stable SDK and [Play requirements](https://support.google.com/googleplay/android-developer/answer/11926878) +at implementation and release. Never select preview APIs to satisfy a release +policy. The installed android-37.0 SDK reports an empty codename and +PreviewSdkInt=0 (revision 2); verify the same package metadata in CI before +pinning the build, rather than relying on preview wording in older instructions. + +Release ABIs: **arm64-v8a**, **armeabi-v7a**, **x86_64**, corresponding to +**aarch64-linux-android**, **armv7-linux-androideabi**, +**x86_64-linux-android**. An ABI may be removed only with a dependency audit and +an amendment to this decision; emulator success alone proves no arm64 hardware +coverage. All shipped native dependencies, including JNA, must pass ELF LOAD +alignment, APK ZIP alignment and actual 16 KB runtime checks. Do not depend on +compatibility mode. [Android's page-size guidance](https://developer.android.com/guide/practices/page-sizes) +requires auditing dependencies as well as the app library. + +At #89 pin a mutually supported stable AGP/Gradle/Kotlin/KSP/Hilt set, JDK, +SDK build tools and NDK, plus wrapper checksum. The current [AGP 9.4 contract](https://developer.android.com/build/releases/agp-9-4-0-release-notes) +requires Gradle 9.6.0 and JDK 17 and supports API 37. This is a candidate build +baseline, subject to actual artifact availability and plugin compatibility. +No dynamic versions, snapshots or automatic toolchain substitutions. #92 pins +cargo-ndk, Rust targets/toolchain and the workspace UniFFI version and builds all +ABIs from the locked Cargo graph. Retain separate native symbols and R8 mappings. + +Support phones, tablets, foldables, resizing/multi-window, desktop windowing, +portrait/landscape, system/light/dark themes, hardware keyboard, large fonts, +TalkBack and process recreation. Preserve non-secret navigation/filter state; +never serialize decrypted fields, passphrases, tokens, QR key payloads or a lease +into SavedStateHandle, bundles, preferences, crash reports or test artifacts. +Rotation/process death must not replay a mutation. In-flight mutations resolve +through Rust revision/transaction state before a retry. + +Apple Watch, watchOS and Watch Connectivity are excluded. Android Auto, TV, +Wear OS and XR-specific experiences are outside the product; Wear is not a +substitute for Watch. Do not show disabled Watch controls on Android. + +## Approved dependencies and review policy + +These are approved purposes, not permission to add every library immediately. +#89 pins only scaffold dependencies; later owners add their required artifacts. +Exact versions live in one version catalog, Cargo.lock and reviewed toolchain +pins. Keep Gradle dependency verification metadata and locks where supported; +review checksums independently before accepting a new artifact. For each addition +and upgrade, record exact coordinates/version, upstream release/maintenance +check, SPDX license and bundled notices, transitives, security advisories and +compatibility results. Approval of a library family does not approve every +transitive version. #94 builds the resolved SBOM/license gate; #112 bundles +attributions and available source obligations. + +| Library/API | Single purpose | License review and maintenance source | +| --- | --- | --- | +| Kotlin and Compose compiler plugin | Kotlin compilation | Apache-2.0, [JetBrains license](https://github.com/JetBrains/kotlin/blob/master/license/LICENSE.txt); check matching stable compiler/plugin releases | +| Gradle, AGP and Android Lint | Reproducible packaging and Android static checks | Apache-2.0 tool distribution notices; [AGP compatibility/release notes](https://developer.android.com/build/releases/about-agp) | +| Compose BOM, Material 3, Adaptive, Activity Compose | Native controls/layout and activity hosting | Apache-2.0, [AndroidX license](https://github.com/androidx/androidx/blob/androidx-main/LICENSE.txt); check each resolved artifact's stable AndroidX release | +| Navigation 3 | Typed adaptive navigation/back stack | AndroidX Apache-2.0; stable release source above | +| Lifecycle ViewModel/Runtime Compose | Lifecycle-aware screen state collection | AndroidX Apache-2.0; stable AndroidX release notes | +| kotlinx.coroutines and coroutines-test | Bounded async calls, Flow and deterministic scheduling tests | Apache-2.0, [upstream](https://github.com/Kotlin/kotlinx.coroutines); check Kotlin compatibility and tagged release | +| Hilt/Dagger | Dependency injection | Apache-2.0, [upstream license](https://github.com/google/dagger/blob/master/LICENSE.txt); verify KSP/AGP support | +| KSP | Hilt code generation | Apache-2.0, [upstream license](https://github.com/google/ksp/blob/main/LICENSE); verify compiler/AGP support | +| UniFFI | Shared typed Swift/Kotlin bridge generation | MPL-2.0, [Mozilla license](https://github.com/mozilla/uniffi-rs/blob/main/LICENSE); pin workspace tool/library together | +| JNA Android AAR | UniFFI native loading/calls | Choose Apache-2.0 under [JNA dual license](https://github.com/java-native-access/jna/blob/master/LICENSE); verify Android ABI/16 KB binaries per version | +| cargo-ndk and NDK | ABI cross compilation/packaging | MIT OR Apache-2.0 for [cargo-ndk](https://github.com/bbqsrc/cargo-ndk); retain NDK component notices and review stable toolchain | +| AndroidX Biometric | OS authentication prompt | AndroidX Apache-2.0; choose stable API supporting CryptoObject and required auth policy | +| CameraX Preview/ImageAnalysis | Camera capture and frame delivery | AndroidX Apache-2.0; check stable artifacts and device support; Rust decodes/parses frames | +| AndroidX Credentials provider | Android 14+ password-provider integration | AndroidX Apache-2.0; verify provider support and stable artifact API | +| AndroidX Test and Compose UI Test | Instrumented/system and Compose behavior checks | AndroidX Apache-2.0; align with Compose BOM and test runner | +| JUnit | JVM unit tests | EPL-1.0 for [JUnit 4](https://github.com/junit-team/junit4); test-only, pin maintained 4.13.x patch | +| ktlint | Kotlin formatting/static style | MIT, [upstream](https://github.com/pinterest/ktlint); pin CLI/rules together and review Kotlin compatibility | +| Macrobenchmark/Baseline Profiles | Measure startup and optimize measured bottlenecks | AndroidX Apache-2.0; add only with reproducible measurements in #105 | +| WorkManager | Explicitly approved durable noninteractive work | AndroidX Apache-2.0; add only when an operation survives process loss safely | +| Android framework | Keystore, KeyguardManager, AutofillService, clipboard, FLAG_SECURE, private paths, backup rules, connectivity and permissions | OS APIs; no extra bundled crypto/storage implementation; behavior audited on supported APIs/OEMs | + +DataStore is allowed only for presentation-only preferences if necessary. +Shared preferences use Rust configuration. Room, Retrofit/OkHttp, JGit, +Bouncy Castle/OpenPGP, Kotlin OTP/password/parser libraries, ML Kit payload +interpretation and a competing QR format are prohibited. Reuse existing Rust +crypto/Git/QR code. Do not schedule interactive unlock, secret display or repository +mutations as background WorkManager tasks. + +## Threat model and state lifetime + +Protect the pass repository/key material, passphrases, HTTPS application tokens, +OTP secrets/codes, decrypted fields, credential selections and identity grants. +Untrusted inputs include remote endpoints/data, repository entries and policies, +QR/images, OS provider callers, restored files, and concurrent/stale UI requests. +Rust validates transports before any repository mutation or connection and uses +repository remotes as authoritative. Android starts HTTPS-only with SSH disabled; +SSH endpoints still parse to typed unsupported errors. Adding SSH requires an +explicit audited support decision while preserving storage-owned transport policy. + +The sandbox and Keystore protect against ordinary cross-app access and accidental +backup, not a fully compromised/rooted device. Debuggable builds, logs, screenshots, +recents thumbnails, clipboard history, accessibility, keyboard suggestions and +crash telemetry are disclosure paths. #100 uses FLAG_SECURE, redaction, background +relock, bounded clipboard ownership/expiry and memory cleanup; never claim complete +Kotlin/JVM memory erasure. Clear platform references and revoke Rust generations. + +Keystore holds non-exportable AES-GCM wrapping keys. Only sealed envelopes reach +private storage. Rust decides secret type, identity, enrollment/invalidation, +authentication policy, lease and recovery. Kotlin translates OS error facts. +BiometricPrompt authorizes the required CryptoObject; device credential fallback +must match the Rust policy/API level and must not silently weaken it. Changed +biometrics, lock-screen removal, restored-without-key data, cancellation and a +stale callback fail closed and require explicit recovery. Provider candidates +are non-secret until authorized user/system selection plus a valid lease. + +## Paths, backup, update and recovery + +#90 receives canonical filesDir, noBackupFilesDir and cacheDir from Android once; +Rust creates scoped capabilities and owns all contents. Never infer HOME or use +public/shared storage. Place repository, keys, config, TOTP discovery catalog, +Keystore envelopes and transactional durable scratch under noBackupFilesDir. +Disposable camera/image buffers may use cacheDir only when cleanup is safe; +never use the cache for the only copy of a repository or credential. + +Set allowBackup=false and explicit legacy backup and Android 12+ data-extraction +rules excluding all sensitive roots from cloud backup and device transfer. Test +OEM transfer behavior; no claim that one manifest flag defeats every OEM tool. +No automatic key/repository transfer. The TOTP catalog contains only canonical +path, ciphertext SHA-256 and is_totp; treat even that metadata as private. + +An APK/Play update installs over the same ID/signing lineage and retains valid +private state and Keystore aliases. Migrations are Rust-owned, versioned and +atomic; migration failure retains prior recoverable data. Process death revokes +in-memory authentication, preserves valid completed cache checkpoints and leaves +atomic transactions recoverable. Low storage/read-only/corruption produce typed +errors without deleting unrelated data. Uninstall/reset deletes app data and can +make keys unrecoverable; reinstall/restore is not an update. Recovery needs the +user's externally retained encrypted key material/passphrase and, when configured, +a remote clone. Do not seed app data or commit credentials to prove recovery. + +## Verification and acceptance + +The [parity matrix](parity.md) assigns every current non-Watch mobile capability +and every Android issue to a Rust owner and Android API. Its pending rows are +implementation obligations, not accepted gaps. #111 must replace pending status +with evidence and have no unexplained gap before #112 distribution. + +Every issue runs the AGENTS.md Rust gates before commit. Android changes also +run build/lint/unit/instrumented checks appropriate to the issue and computer-use +validation on the existing emulator. Update by installation over the existing +app, never uninstall/reset. If unlocking needs an unavailable user, label the +issue untested and document exactly what could not be checked. Emulator evidence +cannot satisfy physical biometric/camera/Keystore/autofill or Play acceptance. +Apple changes require XcodeGen and affected simulator builds and UI checks; +never re-enroll an already enrolled iPhone simulator in ordinary validation. + +#94 owns reproducible CI and test boundaries, #105 accessibility/performance, +#111 the complete architecture/security/parity audit, and #112 physical production +install/update/Play release evidence. No release approval is implied by this ADR. diff --git a/docs/android/parity.md b/docs/android/parity.md new file mode 100644 index 0000000..7754b13 --- /dev/null +++ b/docs/android/parity.md @@ -0,0 +1,92 @@ +# Android parity and issue ownership + +Contract for [#88](https://git.rfc1437.de/hugo/IronStorage/issues/88), reviewed +2026-10-08 against apple/Sources/App/IronStorageApp.swift, +apple/Sources/AutoFill/CredentialProviderViewController.swift, crates/apple/src/lib.rs, +and crates/storage/src/mobile*.rs. See [architecture.md](architecture.md) for +support, dependency, security and update policy. + +**Status:** all Android implementation rows below are pending. No Android app +exists at this decision. The table is a complete assignment of work, not a +claim of completed parity. #111 must attach screen/component/test evidence to +each row. Every issue linked here belongs to milestone 23. + +## Current iPhone and shared-mobile capabilities + +| Existing capability / source | Owning Rust API/module | Android presentation / approved API | Implementation and required evidence | +| --- | --- | --- | --- | +| Five destinations: Home, Passwords, Search, TOTP, Preferences; loading/empty/ready/locked/error fixtures (AppContext, ShellViewController) | mobile::MobileShell, MobileTab, MobileShellState; config selected tab | Single Activity, Compose Material 3 Adaptive, Navigation 3, ViewModel/StateFlow | #89/#91/#102; shell fixtures, startup, back/navigation and recreation | +| Local-first setup, generated/imported key, passphrase confirmation, staged QR import (SetupChoiceViewController, LocalSetupViewController) | mobile_onboarding::MobileOnboardingOperation, crypto; mobile_key_transfer import | Compose forms and core facade; OS input/camera adapters | #95/#106; real local init, key export/import, canceled/failed setup rollback | +| Optional HTTPS clone, server/account/token, repository/branch discovery; attach remote to local store (OnboardingViewController) | mobile_onboarding::{MobileOnboardingRequest, MobileOnboardingOperation}; git/config/secret_store | Forms, typed incremental progress and cancel | #95; TLS/auth/branch/cancel/offline errors; repository remote authority | +| Home history, ahead/behind, local/remote change status, commit activity (ShellViewController, MobileCommitActivityViewController) | mobile_home::MobileHomePage/Operation and git | Compose Home/list/detail | #97; summaries and stale/error state, Git identity, author/history | +| Commit, Fetch, Pull, Push and pull-then-push Sync | mobile_home::MobileHomeOperation; git sync/merge/conflicts/signing | Typed actions, progress/cancel, conflict presentation | #97; pull failure prevents push, conflict/data-loss cases and signing | +| Directory hierarchy, breadcrumbs, folder and entry rows (PasswordDirectoryViewController) | mobile_passwords::MobilePasswordPage/Row/RowKind; repository/read | Adaptive list/detail with stable IDs | #96; deep tree, empty/error, keyboard and size changes | +| Password-store name search (PasswordSearchViewController) | mobile_passwords::MobilePasswordPage::search | Search ViewModel, Flow latest-query-wins | #101; large-store responsiveness and canceled/stale results | +| Unlock entry, explicit user activity, timeout, manual/background lock; shared entry/TOTP/editor lease (LockedPasswordViewController, AppContext) | mobile_authentication::MobileAuthentication; authentication generation/leases | AndroidX BiometricPrompt/CryptoObject, KeyguardManager, lifecycle | #93/#98/#100; wrong passphrase, cancel, expiry, generation race, changed enrollment and process death | +| Structured fields/sections, masked secrets, explicit reveal/select/copy; OTP field diagnostics (MobileEntryFieldCell) | mobile_entry::MobileEntryPage/Field; mobile_authentication::{entry_page,reveal_entry_field,copy_entry_field} | Compose viewer, sensitive controls and accessibility semantics | #99/#100; no pre-unlock values, relock clears all views, copy expiry/ownership | +| Create entry/folder, structured multiline editing, add/remove/reorder fields, dirty discard confirmation (MobileEntryEditorViewController) | mobile_entry::MobileEntryDraft; mobile_authentication editor session/create/save/discard; write | Compose editor/forms, stable field IDs | #103; actual encrypted saves, conflict tokens, interrupted saves and dirty navigation | +| Password length/generation and replacement | generate; mobile_entry::MobileEntryDraft::generate_password | Generator controls call Rust | #103; Rust validation and unbiased generated value; no Kotlin generator | +| OTP URI input, camera/image import, review before Save (OtpImportViewController; TOTP Add action) | mobile_entry::MobileEntryDraft::add_otp_image; document/otp/presentation | CameraX frames or Android photo picker bytes; Rust decode and field construction | #103/#106/#107; URI/image/scan parity, invalid/oversized input, cancel; no payload parsing in Kotlin | +| Move/copy/delete entries and folders, destination browse, overwrite and dirty-editor handling (PasswordMutationCoordinator) | mobile_mutation::MobileMutationService/Plan/Request; mutation | Compose dialogs/destination picker | #108; revisions, destination conflicts, recipient reencryption and rollback | +| TOTP discovery catalog, incremental progress, warm cache, refresh, cancel/resume, cached search (TotpListViewController) | mobile_totp discovery/catalog/operation; mobile_authentication cached/search APIs | Compose list/progress/search; private no-backup catalog | #90/#110; cold/warm/change/add/remove/corrupt/wrong-store/concurrent-change tests | +| TOTP rows, large detail code, countdown/expiry, copying (TotpDetailViewController, TotpCodeView) | mobile_totp::MobileTotpPage/Detail; otp; mobile_authentication::{totp_detail,copy_totp_code} | Compose code/detail; OS clock facts, Rust timing | #107/#100; RFC vectors, boundary countdown, stale callback/relock and clipboard | +| Repository/server identity, local-only/add-remote Settings, token replace/remove (PreferencesViewController, TokenUpdateViewController) | mobile_authentication::MobilePreferences; mobile_onboarding::replace_configured_application_token; config/secret_store | Compose Settings/facade | #104/#95; no token display; protected replacement/removal and offline operation | +| Key identity/fingerprint, GPG recovery import and public/private QR export (KeyImportScannerViewController, KeyExportListViewController, KeyQrExportViewController) | mobile_key_transfer::{MobileKeyTransferService,MobileKeyTransferImport}; crypto/presentation | CameraX Preview/ImageAnalysis; animated Rust-provided matrices | #106/#104; multi-frame order/duplicates/interruption, bounded frames, private export authorization and camera denial | +| Inactivity timeout, biometric enable/disable, Lock Now, appearance and Git commit name/email | mobile_authentication preference setters, git_identity; config | Compose Settings, BiometricPrompt, system/light/dark theme | #98/#104; persistent settings, validation, Keystore enrollment/recovery and lease revocation | +| iPhone AutoFill credential selection (CredentialProviderViewController) and shared identity/access domain | desktop/read/document/secret_store today; storage-owned provider contract to add | AndroidX CredentialProviderService (API 34+) and framework AutofillService (API 26+) | #109; typed caller/site identity, non-secret candidates, explicit selection/auth, forged/stale requests, permissions and cross-process lifetime | +| Camera permissions, private paths, secret-store mechanics, clipboard, scene/process events, screen privacy | Rust repository/config/secret_store/authentication/presentation; platform callbacks contain facts only | Android framework adapters, FLAG_SECURE, backup rules, ClipboardManager | #90/#93/#98/#100; backup inspection, sandbox paths, pressure/update, screen/clipboard/lifecycle tests | +| Accessible labels/actions, large text, keyboard, responsive layout and localization | Typed Rust state/actions; UI accessibility is Kotlin | Compose semantics, TalkBack, adaptive layout, resources | #105; measured keyboard/TalkBack/large-font/multi-window/RTL/rotation coverage | +| Watch pairing/install state in Settings; TOTP Watch selection, snapshots, receipts, transfer and watchOS detail | mobile_watch; Watch fields currently in mobile_totp/mobile_authentication and Apple bridge | Excluded from Android records and UI by #91 | Deliberate Watch-only exclusion; no unsupported placeholder controls or Wear implementation | + +Name search matches current mobile behavior; CLI/TUI decrypted grep is not an +iPhone parity claim. Android has no separate filesystem-based search. The +shared core's recipient/signing rules, pass/pass-otp format, HOTP counter safety, +Git failure semantics and secret-redaction requirements remain binding wherever +an Android workflow invokes them; platform presentation may not replace them. + +Current iPhone camera adapters pass OS-decoded QR bytes. Android #106/#91 must +also supply bounded image/frame facts for Rust decoding as required by the +Android contract; that additional adapter is pending, not an existing API. +The external-credential matching/grant API required by #109 must be implemented +in storage, rather than copying frontend selection assumptions from AutoFill. + +## Complete milestone issue-to-layer map + +Links identify the live issues by number and a concise description. Ordering follows the user's label +priority, then numeric order; do not substitute a dependency from another +milestone. This list gives ownership, not permission to skip acceptance criteria. + +| Issue | Owning layer | Approved implementation/API and completion gate | +| --- | --- | --- | +| [#88 Architecture/support/dependency policy](https://git.rfc1437.de/hugo/IronStorage/issues/88) | Repository architecture decision | This ADR and parity/owner matrix; review against current sources | +| [#89 Gradle scaffold/module boundaries](https://git.rfc1437.de/hugo/IronStorage/issues/89) | Android build/app/core/platform/bridge modules | Kotlin DSL/catalog/wrapper, Compose/Hilt/KSP, source/dependency gates, all shell fixtures; clean debug APK | +| [#90 Private paths/config/cache/backup](https://git.rfc1437.de/hugo/IronStorage/issues/90) | crates/storage plus Android path adapter | Context roots/capabilities, atomic layout/migrations, backup rules; Rust and sandbox/update tests | +| [#91 Platform-neutral mobile contract](https://git.rfc1437.de/hugo/IronStorage/issues/91) | crates/storage and shared UniFFI bridge | Typed semantic actions/icons, opt-in Watch data, platform callbacks, shared Swift/Kotlin inventory tests | +| [#92 Native Rust/UniFFI packaging](https://git.rfc1437.de/hugo/IronStorage/issues/92) | Shared/Android Rust bridge and Gradle rust-bridge | cargo-ndk, generated immutable Kotlin/JNA Android, three ABIs, checksums/16 KB/loading/debug symbols | +| [#93 Keystore adapter](https://git.rfc1437.de/hugo/IronStorage/issues/93) | crates/storage secure-secret orchestration and platform adapter | Framework Keystore AES-GCM; callback facts, sealed records, invalidation/recovery tests | +| [#94 CI/static analysis/reproducibility](https://git.rfc1437.de/hugo/IronStorage/issues/94) | Build/CI/test infrastructure | Cargo gates, Gradle build/Lint/ktlint, JVM/Compose/instrumented tests, verification/locks/SBOM/ABI checks | +| [#95 Local-first/HTTPS onboarding](https://git.rfc1437.de/hugo/IronStorage/issues/95) | storage mobile_onboarding plus Compose features | Existing Rust initialization/discovery/clone/credentials; no Kotlin transport | +| [#96 Hierarchical navigation](https://git.rfc1437.de/hugo/IronStorage/issues/96) | storage mobile_passwords plus Compose features | Material 3 Adaptive list/detail, typed rows/stable IDs | +| [#97 Home/Git](https://git.rfc1437.de/hugo/IronStorage/issues/97) | storage mobile_home/git plus Compose features | Rust actions/history/progress/conflicts; Compose Home and activity | +| [#98 Biometric authentication lease](https://git.rfc1437.de/hugo/IronStorage/issues/98) | storage authentication/mobile_authentication plus platform adapter | AndroidX BiometricPrompt/CryptoObject, KeyguardManager; Rust generations/policy | +| [#99 Structured viewer](https://git.rfc1437.de/hugo/IronStorage/issues/99) | storage mobile_entry/mobile_authentication plus Compose features | Typed fields/sections/reveal/copy, Rust OTP and diagnostics | +| [#100 Privacy/lifecycle/plaintext](https://git.rfc1437.de/hugo/IronStorage/issues/100) | storage lease/presentation plus platform/app | Lifecycle, FLAG_SECURE, ClipboardManager, no saved plaintext/logs, race tests | +| [#101 Name search](https://git.rfc1437.de/hugo/IronStorage/issues/101) | storage mobile_passwords plus Compose features | Rust cached/name search, Flow cancellation/latest query wins | +| [#102 Adaptive shell](https://git.rfc1437.de/hugo/IronStorage/issues/102) | storage mobile shell contract plus Android app/features | Navigation 3/Material 3 Adaptive, StateFlow, restoration without secrets | +| [#103 Entry/folder/editor/generation](https://git.rfc1437.de/hugo/IronStorage/issues/103) | storage mobile_entry/write/generate plus Compose features | Rust editor sessions/drafts/atomic saves, structured multiline controls and OTP import | +| [#104 Settings/recovery](https://git.rfc1437.de/hugo/IronStorage/issues/104) | storage config/mobile_authentication/key transfer plus Compose features | Shared setters, token/key recovery, Biometric/appearance/identity controls without Watch | +| [#105 Accessibility/adaptive/localization/performance](https://git.rfc1437.de/hugo/IronStorage/issues/105) | Android features/resources plus storage cancellation | Compose semantics/TalkBack/resources, measured Macrobenchmark/profiles if justified | +| [#106 CameraX GPG QR import/export](https://git.rfc1437.de/hugo/IronStorage/issues/106) | storage QR/key transfer plus platform camera/Compose | CameraX frames, Rust decode/envelope/matrices, permission and bounded payload checks | +| [#107 TOTP list/detail](https://git.rfc1437.de/hugo/IronStorage/issues/107) | storage mobile_totp/otp plus Compose features | Rust codes/timing, large display/copy/refresh, OTP creation/import navigation | +| [#108 Move/copy/delete](https://git.rfc1437.de/hugo/IronStorage/issues/108) | storage mobile_mutation/mutation plus Compose features | Rust plans/tokens/overwrite/reencryption, destination and confirmation UI | +| [#109 Credential Manager/Autofill](https://git.rfc1437.de/hugo/IronStorage/issues/109) | storage identity/permission/credential contract plus OS services | AndroidX provider and framework AutofillService, explicit selection/lease before secrets | +| [#110 TOTP catalog/discovery/search](https://git.rfc1437.de/hugo/IronStorage/issues/110) | storage mobile_totp plus Compose features | Existing hash/path/is_totp catalog, Rust progress/checkpoints/filter, no Watch | +| [#111 Parity/security/reliability audit](https://git.rfc1437.de/hugo/IronStorage/issues/111) | All above plus independent acceptance review | Completed matrix evidence, Rust/Android release gates and physical-device threat tests | +| [#112 Google Play distribution](https://git.rfc1437.de/hugo/IronStorage/issues/112) | Release tooling/listing and production app | Protected signing/Play AAB, audited manifest/licenses, physical clean install and update-preservation tests | + +#87 is the closed umbrella issue, not an open implementation candidate. Verify +live issues after each closure; newly created bug/critical issues may change the +order. #91 and #90 currently name later packaging/contract dependencies; their +implementations may establish the necessary narrow Rust APIs before consuming +full Android packaging, but they must meet all stated acceptance tests before +closure. Do not close a partly integrated issue just because its unit tests pass.