Share the platform-neutral Rust mobile contract (#91)
Dependency security audit / rustsec (push) Failing after 22s
Dependency security audit / rustsec (push) Failing after 22s
This commit is contained in:
1 parent
fcb36a0684
commit
180851087b
52 files changed
+8932
-4263
No files matched your search
@@ -25,10 +25,10 @@ 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
|
||||
view-ready records and typed errors. #91's `crates/mobile-bridge` removes SF Symbol
|
||||
and Watch assumptions from shared phone contracts and generates Swift and Kotlin
|
||||
from one source. `crates/apple` opts into Watch; `crates/android` packages the
|
||||
phone surface through #92's native build gates. 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.
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# Shared mobile contract (#91)
|
||||
|
||||
`crates/mobile-bridge` is the single UniFFI source for phone records, enums,
|
||||
conversions, objects, operations and OS callbacks. `crates/android` packages it
|
||||
without features. `crates/apple` packages it with the explicit `watch` feature.
|
||||
Neither packaging crate copies converters. The component namespace is
|
||||
`ironstorage_mobile`; the native library names remain `ironstorage_android` and
|
||||
`ironstorage_apple`. Swift source/header names follow the shared namespace.
|
||||
|
||||
## Public workflow inventory
|
||||
|
||||
| Storage owner | Shared phone exports | Identity / concurrency boundary |
|
||||
| --- | --- | --- |
|
||||
| mobile | shell, fixtures, tab selection | typed tabs/states/semantic icons |
|
||||
| mobile_home / git | Home page, commits/changes/actions, controlled operations/progress | typed actions, repository operation serialization and cancellation |
|
||||
| mobile_passwords | browse, search, folder/entry rows | storage entry paths and row IDs |
|
||||
| mobile_authentication / authentication | unlock, reveal/copy, preferences, identity/appearance/timeout, lifecycle | generation-scoped authentication handles, monotonic expiry |
|
||||
| mobile_entry / document / generate | structured fields, editor sessions, OTP image import, generation, save/discard | session u64 and field IDs; original ciphertext/conflict checks |
|
||||
| mobile_mutation / mutation | preparation, request, commit, outcome | opaque revision token validated at execution; explicit overwrite decisions |
|
||||
| mobile_totp / otp | discovery/progress/cancel, cached search, detail/code/copy | storage-owned catalog, Unix-time inputs and expiry |
|
||||
| mobile_onboarding / config | server discovery, clone and local setup, token replacement | typed transport/phase errors, controlled cancellation |
|
||||
| mobile_key_transfer / presentation | export/import, frames, progress, key selection | storage-owned QR envelopes and bounded decoding |
|
||||
| mobile_watch | **Apple opt-in only:** preferences, selection, snapshots/status/receipts | no Watch enum, field, method or control in the Android contract |
|
||||
|
||||
`MobileIcon` expresses intent, never an asset name. UIKit maps it to SF Symbols
|
||||
in `MobileIcons.swift`. Android's core maps it exhaustively to its presentation
|
||||
enum; Compose maps navigation intents to vector drawables. Asset names and
|
||||
selected variants belong to the native UI. Actions already use typed enums.
|
||||
|
||||
## OS boundaries
|
||||
|
||||
- `MobilePlatformPaths` accepts absolute private data and cache roots, rejects
|
||||
traversal and overlapping roots, and lets storage derive config/vault/key
|
||||
paths. Validation does not create or migrate files. #90 owns initialization,
|
||||
permissions, relocation, backup and cache policy.
|
||||
- `MobileSecretBackend` performs create/retrieve/replace/delete/lock/unlock on
|
||||
a typed locator and protection supplied by storage. Values are opaque encoded
|
||||
storage records. OS errors are enums. It cannot select accounts, protection,
|
||||
enrollment recovery, cache lifetime or authentication policy. Its adapter is
|
||||
usable by `mobileAuthenticationWithPlatform`; #93 implements Android Keystore.
|
||||
Preferences use that same injected backend, never a hidden native fallback.
|
||||
- `MobileClock` returns elapsed monotonic milliseconds from a stable origin.
|
||||
It cannot mint or refresh leases. Rust uses the existing authentication clock
|
||||
and generation checks. Unix timestamps required by TOTP/Git remain explicit
|
||||
inputs, not values parsed from rendered countdowns.
|
||||
- `MobileLifecycleEvent` reports foreground, background, device lock, real user
|
||||
activity, or cancellation. Storage revokes leases/editors on background/lock,
|
||||
checks expiry on foreground, and extends activity only for an active lease.
|
||||
- `MobileClipboard` reads/writes/clears OS text and waits for a requested timeout
|
||||
or cancellation. Rust's existing manager restores the prior text or clears
|
||||
only if the copied value is still current; newer clipboard content survives.
|
||||
Call off the UI thread; the facade serializes access to one OS clipboard.
|
||||
- `MobileCameraFrame` supplies luminance bytes, dimensions and row/pixel stride.
|
||||
Storage validates bounds (4096 pixels per dimension, 32 MiB plane, pixel
|
||||
stride 1–4) and decodes QR payloads. Native UI never interprets envelopes.
|
||||
- `MobileCredentialRequest` carries an opaque request ID, OS-asserted package,
|
||||
signing-certificate SHA-256 and web origins. Rust validates/canonicalizes the
|
||||
facts. A validated request is **not** an authorization grant. #109 implements
|
||||
matching, selected-candidate authentication and scoped grants in storage.
|
||||
|
||||
## Ownership, cancellation and diagnostics
|
||||
|
||||
UniFFI objects are reference-counted Rust owners. Operation objects retain their
|
||||
authentication owner while work runs. Kotlin `close()` / Swift deinitialization
|
||||
release native handles; the final storage authentication owner destroys the
|
||||
lease and locks its store. Frontends cancel the exact operation on navigation
|
||||
or coroutine cancellation before releasing it. `MobileAuthentication.cancel`
|
||||
revokes its generation and discards editor state; it is not interchangeable
|
||||
with cancelling an individual discovery/Git operation. Late results are
|
||||
discarded by facade revision IDs. Polling, repaint and progress never count as
|
||||
user activity. Editor IDs and field IDs are stable within their owning session;
|
||||
revision tokens are opaque and must be returned unchanged to storage.
|
||||
|
||||
Secrets and payloads are owned values, not persisted view state. Rust secret
|
||||
buffers zeroize on drop. Swift/Kotlin cannot promise complete managed-memory
|
||||
erasure, so callers release revealed/copy/OTP/frame values when their operation
|
||||
ends and never log or save them. Generated records, associated-value enums and
|
||||
errors have redacted diagnostic rendering; typed fields remain available for
|
||||
deliberate UI presentation. Unexpected native/callback errors redact their
|
||||
payloads. Mechanical generator hardening is version-checked and preserves wire
|
||||
layouts and every UniFFI contract/API checksum.
|
||||
|
||||
## Contract gates
|
||||
|
||||
Every Android binding generation generates both Swift and Kotlin from the
|
||||
compiled phone component. The build tool verifies all records/fields, types,
|
||||
functions and operation checksums against metadata, writes `inventory.tsv`, and
|
||||
rejects any Watch item in the phone surface. The Apple opt-in build generates
|
||||
both languages too. `compare-contract` compares complete shared signatures;
|
||||
only Watch items and the opt-in Watch inputs to `preferences` differ.
|
||||
|
||||
```sh
|
||||
cargo build -p ironstorage-apple --lib
|
||||
cargo run -p ironstorage-android-build -- contract-bindings \
|
||||
target/debug/libironstorage_apple.dylib android/uniffi.toml /tmp/ironstorage-watch-contract watch
|
||||
cargo run -p ironstorage-android-build -- compare-contract \
|
||||
android/rust-bridge/build/generated/bindings-raw/debug/inventory.tsv \
|
||||
/tmp/ironstorage-watch-contract/inventory.tsv
|
||||
```
|
||||
|
||||
On non-macOS build hosts, use the host shared-library extension. Apple generation
|
||||
copies the verified/hardened Swift source and header into `apple/Generated`.
|
||||
Contract tests exercise typed errors, checksum failure, callback ownership,
|
||||
cancellation/concurrency, camera bounds, large foreign payloads and redaction.
|
||||
All app validation uses persistent emulator devices; no physical-device gate is
|
||||
part of this milestone's testing scope.
|
||||
|
||||
The native Swift probe in `tools/android-build/tests/mobile_contract.swift` runs
|
||||
against the generated source and the host Apple library:
|
||||
|
||||
```sh
|
||||
xcrun swiftc -swift-version 5 -module-cache-path /tmp/ironstorage-swift-cache \
|
||||
-I apple/Generated \
|
||||
-Xcc -fmodule-map-file=apple/Generated/ironstorage_mobileFFI.modulemap \
|
||||
-L target/debug -lironstorage_apple \
|
||||
-Xlinker -rpath -Xlinker "$PWD/target/debug" \
|
||||
apple/Generated/ironstorage_mobile.swift \
|
||||
tools/android-build/tests/mobile_contract.swift -o /tmp/ironstorage-swift-contract
|
||||
/tmp/ironstorage-swift-contract
|
||||
```
|
||||
|
||||
The Android native probe is `MobileContractTest`: run it with the existing
|
||||
`NativeRuntimeTest` on persistent 4 KB and strict 16 KB ARM64 emulators. UI and
|
||||
native instrumentation suites run sequentially to preserve input focus. The
|
||||
storage integration test exercises a real encrypted fixture with the injected
|
||||
secret backend/clock, activity renewal, background revocation, editor invalidation
|
||||
and final-owner cleanup without global configuration.
|
||||
@@ -36,7 +36,7 @@ each row. Every issue linked here belongs to milestone 23.
|
||||
| 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 |
|
||||
| Watch pairing/install state in Settings; TOTP Watch selection, snapshots, receipts, transfer and watchOS detail | mobile_watch; shared bridge's explicit `watch` feature enabled only by crates/apple | Absent from Android records and UI (#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
|
||||
|
||||
Reference in new issue
Block a user