# Apple mobile integration audit This is the source-of-truth coverage and release-test checklist for the iPhone and Apple Watch milestone. Automated evidence is required on every change; a manual result is evidence only when the named device or simulator flow was actually exercised. ## Coverage matrix | Area | Implemented boundary | Automated evidence | Manual release evidence | | --- | --- | --- | --- | | Home tab, remote activity, pull-to-refresh | UIKit renders Rust `MobileHomePage` and typed Commit/Fetch/Pull/Push actions | `mobile_home` unit tests, `git_embedded`, and `crates/apple` bridge tests | Exercise refresh and all four actions against a disposable HTTPS remote, including expired application-token recovery | | Passwords tab, navigation, view, and edit | Rust supplies typed paths, fields, drafts, revisions, and mutations; UIKit presents them | `mobile_passwords`, `entry_documents`, `mobile_mutation`, and `mobile_authentication` tests | Open nested entries, edit/save/cancel, and confirm stale-edit recovery | | Swipe move/copy/delete | Rust plans destinations, collisions, dirty-editor handling, and commits; UIKit also exposes accessibility actions | `plans_destinations_collisions_hidden_paths_and_stale_revisions` and authentication serialization tests | Exercise swipe and VoiceOver alternatives; confirm destructive prompts | | TOTP tab and sharing | Rust discovers, caches, parses, selects, and generates OTP data | `mobile_totp`, `otp`, and `mobile_watch` integration tests | Discover entries, read a code, change Watch selection, and verify countdown | | Preferences and biometric unlock | Rust owns preference/authentication state; LocalAuthentication and Keychain remain isolated Apple adapters | `authentication_leases`, `secret_store`, and mobile authentication tests | Enroll once, then test match, cancel, rejection, and changed enrollment using the documented sequence below | | GPG key QR transfer | Rust frames, validates, reconstructs, authenticates, and atomically stores keys; UIKit only scans/displays frames | `mobile_key_transfer` unit tests and `crypto_compatibility` | Test public/private transfer, interruption, duplicates, and capture shielding | | Watch synchronization | Rust owns selected snapshots, pairing identity, revisions, replay rejection, receipts, and revocation | `replacement_snapshots_reject_replays_conflicts_and_pairing_changes` | On a paired device, verify background delivery, replacement, replay rejection, revoke, and pairing change | | Watch display | Rust owns OTP calculation, timing, and ready/empty/syncing/stale/locked/error presentation; SwiftUI renders it | `watch_runtime_generates_view_ready_totp_and_clears_secrets_when_locked` plus ARM64 Watch target build | Test offline and stale-snapshot behavior, wrist-down privacy, VoiceOver, Dynamic Type, and supported Watch sizes | | Lifecycle and disclosure controls | Rust drops protected state; UIKit/SwiftUI clear views, time-limit pasteboard values, and shield private QR/captured content | authentication, presentation, TOTP, and Watch lifecycle tests; source audit in `apple_mobile_audit` | Background/foreground, lock, screenshot/capture, pasteboard expiry, and protected-data loss | | iPhone layouts and input | Native UIKit navigation, tables, alerts, refresh controls, semantic text styles, and hardware-keyboard behavior | iPhone simulator compile for the supported target | Exercise supported iPhone sizes, largest Dynamic Type, VoiceOver, contrast, and hardware keyboard | The following are intentionally outside this milestone and must not be stubbed into the Apple apps: full CLI/TUI parity, command mode or command palette, SSH Git, non-TOTP Watch features, and production AutoFill behavior. ## Security boundary audit - `crates/storage` owns repository access, parsing, Git, OTP, authentication, snapshot transfer, conflict handling, and secret policy. - The Apple Rust crates are mechanical UniFFI projections. Swift may collect input and call those APIs; it does not open a password-store repository, calculate OTP, parse snapshots, or launch processes. - Git URLs are rejected unless they use HTTPS before transport work. The Watch stores only the selected opaque snapshot in a passcode-protected, device-only Keychain item. - Apple production sources contain no logging calls. Secret values use typed redaction/zeroization in Rust; pasteboard values are removed after their Rust deadline; private QR and authenticated content are hidden during capture or lifecycle loss. - The iPhone and AutoFill privacy manifests declare in-container file metadata access (`C617.1`) and elapsed-time/timer use (`35F9.1`). The Watch manifest declares no required-reason API because its minimal Rust core does not open files or use a monotonic timer. No target declares tracking or developer data collection. - Face ID and camera access have purpose strings in `project.yml`. No custom entitlement is currently required: default signing supplies each target's application identifier and Keychain group, WatchConnectivity needs no added capability, and no App Group is used. Alternative-distribution entitlements belong to the final distribution issue. `cargo test -p ironstorage --test apple_mobile_audit` enforces the source and privacy-manifest parts of this review so those boundaries cannot silently regress. ## Required automated gates From the repository root: ```sh cargo fmt --all -- --check RUSTFLAGS="-D warnings" cargo check --workspace --all-targets cargo clippy --workspace --all-targets -- -D warnings cargo test --workspace ``` For Apple changes, regenerate the project and compile the exact affected iPhone and ARM64 Watch simulator targets. Confirm each built app bundle contains its own `PrivacyInfo.xcprivacy`. ## Manual biometric sequence Keep the existing booted simulator, app container, bundle identifier, and Face ID enrollment. For first setup only, choose **Features > Face ID > Enrolled**, enable Biometric Unlock in IronStorage, unlock one entry, and enter the GPG passphrase once. Do not toggle enrollment again during normal validation. For each normal check, tap the entry's **Unlock** control first and then choose **Features > Face ID > Matching Face**. Verify cancellation and rejection without changing enrollment. A changed-enrollment check is destructive because `biometryCurrentSet` invalidates the protected Keychain item; run it only when the tester is ready to enter the passphrase again. ## Manual evidence still required Record the device model/OS, build commit, and pass/fail result for each manual row above. Physical paired-Watch deployment and WatchConnectivity testing are currently skipped by project direction, so issues closed without that evidence must carry the `untested` label. Do not describe a simulator build or an opened Xcode project as device validation.