Define native Android architecture and parity contract (#88)
Dependency security audit / rustsec (push) Failing after 26s

This commit is contained in:
hugo committed 2026-10-08 09:35:03 +02:00
1 parent 71a4f289cf
commit 30d9119302
4 files changed
+316

No files matched your search

+8
View File
@@ -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.
+5
View File
@@ -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
+211
View File
@@ -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.
+92
View File
@@ -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.