Make the shared Rust mobile contract platform-neutral for Android #91

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

Goal

Turn the current iPhone-oriented mobile surface into a shared mobile application contract that Android can consume without inheriting Apple or Watch assumptions.

Scope

  • Inventory crates/storage::mobile* and crates/apple exports against the Android parity matrix.
  • Move reusable UniFFI records/conversions into a shared bridge or generate equivalent bindings from one source of truth; prevent Swift and Kotlin APIs from drifting.
  • Replace Apple-specific presentation values such as SF Symbol names with typed semantic icon/action identifiers that each UI maps to native assets.
  • Separate Watch-only selection/snapshot/status fields from phone TOTP records so Android does not derive state from “unavailable” Watch display strings or expose dead controls.
  • Add typed contracts for platform paths, secure-secret operations, clipboard presentation, clock/lifecycle events, camera frames, and any Android-only credential-provider request that cannot be implemented directly in Rust.
  • Keep repository paths, configuration, parsing, mutation decisions, Git progress, authentication leases, TOTP timing, QR envelopes, and external-credential matching in crates/storage.
  • Define stable IDs, revision/conflict tokens, cancellation ownership, object lifetime, and secret-redaction behavior across UniFFI.
  • Add contract tests that generate both Swift and Kotlin bindings and compare the public feature inventory.

Acceptance criteria

  • The Android bridge presents every non-Watch current mobile workflow without copying Apple-specific conversion code.
  • Apple builds continue to work, while Watch-only data is explicitly opt-in and absent from Android records.
  • UI-visible actions and states are enums/records, not values recovered from labels, symbol names, or error text.
  • Platform callback interfaces contain OS mechanics only and cannot make storage-domain decisions.
  • Binding compatibility, cancellation, concurrency, lifetime, large payload, and redaction tests pass.

Depends on #88 and the Rust/UniFFI packaging issue. 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 Turn the current iPhone-oriented mobile surface into a shared mobile application contract that Android can consume without inheriting Apple or Watch assumptions. ## Scope - Inventory `crates/storage::mobile*` and `crates/apple` exports against the Android parity matrix. - Move reusable UniFFI records/conversions into a shared bridge or generate equivalent bindings from one source of truth; prevent Swift and Kotlin APIs from drifting. - Replace Apple-specific presentation values such as SF Symbol names with typed semantic icon/action identifiers that each UI maps to native assets. - Separate Watch-only selection/snapshot/status fields from phone TOTP records so Android does not derive state from “unavailable” Watch display strings or expose dead controls. - Add typed contracts for platform paths, secure-secret operations, clipboard presentation, clock/lifecycle events, camera frames, and any Android-only credential-provider request that cannot be implemented directly in Rust. - Keep repository paths, configuration, parsing, mutation decisions, Git progress, authentication leases, TOTP timing, QR envelopes, and external-credential matching in `crates/storage`. - Define stable IDs, revision/conflict tokens, cancellation ownership, object lifetime, and secret-redaction behavior across UniFFI. - Add contract tests that generate both Swift and Kotlin bindings and compare the public feature inventory. ## Acceptance criteria - The Android bridge presents every non-Watch current mobile workflow without copying Apple-specific conversion code. - Apple builds continue to work, while Watch-only data is explicitly opt-in and absent from Android records. - UI-visible actions and states are enums/records, not values recovered from labels, symbol names, or error text. - Platform callback interfaces contain OS mechanics only and cannot make storage-domain decisions. - Binding compatibility, cancellation, concurrency, lifetime, large payload, and redaction tests pass. Depends on #88 and the Rust/UniFFI packaging issue. 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:10:20 +00:00
hugo added the enhancement label 2026-08-17 19:10:20 +00:00
hugo added the untested label 2026-10-08 11:25:20 +00:00
Author
Owner

Commit: 1808510.

Implemented #91 in the shared mobile Rust contract.

  • Moved the reusable UniFFI exports/conversions into crates/mobile-bridge; Android and Apple are thin packaging crates. Android exposes every current phone workflow. Apple explicitly opts into Watch, whose types, fields and methods are absent from Android metadata.
  • Replaced storage-owned SF Symbol strings with typed semantic icons, mapped to native Swift assets and Android vectors. Storage still owns actions, parsing, repository/Git decisions, revisions, authentication and OTP state.
  • Added working typed OS contracts for private roots, secure-secret backend, monotonic clock/lifecycle, clipboard, bounded strided camera frames and credential-provider caller facts. Injected authentication uses the supplied backend for preferences as well as leases. Rust decodes camera QR payloads, validates paths/origins, and applies existing clipboard restore/cancellation rules.
  • Added generation/inventory/checksum checks for both languages, shared-signature comparison, Swift/Kotlin diagnostic redaction, and documented IDs, ownership, cancellation and secret lifetimes in docs/android/mobile-contract.md. Fixed the Gradle Rust compiler selection and serialized emulator instrumentation suites.

Validation: all required Rust gates passed (422 workspace tests); Android unit/lint/ktlint/debug/release/APK/AAB gates passed; all three packaged ABIs passed metadata/alignment audits. Seven native instrumentation tests and the 25-state/tab UI test passed independently on persistent ARM64 4 KB and strict 16 KB emulators. Optimized release Rust loading and navigation icons were also verified on both. Swift native contract probe passed callback, >1 MiB payload, redaction, cancellation and concurrency checks. The inventories contain 538 identical shared entries. xcodegen and iPhone/bundled Watch/AutoFill simulator builds passed.

Review: checked the complete change against the issue and architecture boundary. Android secure storage, initialization, onboarding UI and provider matching remain their own milestone issues; these callbacks provide their functional Rust contract rather than implementing UI features outside #91.

Remaining manual check (untested): iPhone browsing/preferences/locked-entry rendering worked with existing data, but biometric recovery required the user's GPG passphrase. The Mac subsequently locked, preventing further computer use. No Face ID re-enrollment, app uninstall, container/keychain reset or physical-device test was performed. Automated encrypted-fixture authentication and final-owner cleanup tests passed. The final app was installed over the same simulator/bundle identifier, preserving state.

Commit: [1808510](https://git.rfc1437.de/hugo/IronStorage/commit/1808510). Implemented #91 in the shared mobile Rust contract. - Moved the reusable UniFFI exports/conversions into `crates/mobile-bridge`; Android and Apple are thin packaging crates. Android exposes every current phone workflow. Apple explicitly opts into Watch, whose types, fields and methods are absent from Android metadata. - Replaced storage-owned SF Symbol strings with typed semantic icons, mapped to native Swift assets and Android vectors. Storage still owns actions, parsing, repository/Git decisions, revisions, authentication and OTP state. - Added working typed OS contracts for private roots, secure-secret backend, monotonic clock/lifecycle, clipboard, bounded strided camera frames and credential-provider caller facts. Injected authentication uses the supplied backend for preferences as well as leases. Rust decodes camera QR payloads, validates paths/origins, and applies existing clipboard restore/cancellation rules. - Added generation/inventory/checksum checks for both languages, shared-signature comparison, Swift/Kotlin diagnostic redaction, and documented IDs, ownership, cancellation and secret lifetimes in `docs/android/mobile-contract.md`. Fixed the Gradle Rust compiler selection and serialized emulator instrumentation suites. Validation: all required Rust gates passed (422 workspace tests); Android unit/lint/ktlint/debug/release/APK/AAB gates passed; all three packaged ABIs passed metadata/alignment audits. Seven native instrumentation tests and the 25-state/tab UI test passed independently on persistent ARM64 4 KB and strict 16 KB emulators. Optimized release Rust loading and navigation icons were also verified on both. Swift native contract probe passed callback, >1 MiB payload, redaction, cancellation and concurrency checks. The inventories contain 538 identical shared entries. xcodegen and iPhone/bundled Watch/AutoFill simulator builds passed. Review: checked the complete change against the issue and architecture boundary. Android secure storage, initialization, onboarding UI and provider matching remain their own milestone issues; these callbacks provide their functional Rust contract rather than implementing UI features outside #91. Remaining manual check (`untested`): iPhone browsing/preferences/locked-entry rendering worked with existing data, but biometric recovery required the user's GPG passphrase. The Mac subsequently locked, preventing further computer use. No Face ID re-enrollment, app uninstall, container/keychain reset or physical-device test was performed. Automated encrypted-fixture authentication and final-owner cleanup tests passed. The final app was installed over the same simulator/bundle identifier, preserving state.
hugo closed this issue 2026-10-08 11:25:20 +00:00
Sign in to join this conversation.