Cache TOTP discovery and report progress #74

Closed
opened 2026-08-11 20:06:21 +00:00 by hugo · 2 comments
Owner

Goal

Make the iPhone TOTP pane open immediately from a small local discovery index and make explicit discovery visibly progress instead of repeatedly decrypting the entire password store.

Required behavior

  • crates/storage owns discovery, cache semantics, invalidation, progress, and cancellation. Apple code only invokes Rust and presents Rust-provided state.
  • Store the cache outside the password-store repository with private file permissions.
  • Each cache record contains exactly the canonical entry filename/path, a SHA-256 hash of the undecrypted ciphertext, and an is_totp boolean. A version and canonical store identity may be stored at the catalog level.
  • Never cache decrypted entry text, OTP URIs, secrets, issuer/account metadata, generated codes, passwords, passphrases, or key material.
  • Normal TOTP-pane population reads the cache without decrypting entries.
  • Pull-to-refresh rebuilds/updates the cache. If no usable cache exists, discovery starts automatically after authentication.
  • During update, hash current ciphertext and decrypt only entries missing from the cache or whose ciphertext hash changed. Reuse unchanged is_totp flags without decryption.
  • Atomically checkpoint every newly inspected or changed record as discovery proceeds. If discovery is cancelled or the process is suspended, completed records remain available and the next refresh resumes from their hashes. Keep older records until a complete pass so interrupted discovery cannot incorrectly prune unseen files.
  • After a complete stable pass, remove records for entries no longer in the filesystem.
  • Publish typed, cancellable, monotonic progress including phase, total, inspected, cache hits, matches, and unavailable entries. Use a determinate progress bar once the total is known and keep cancellation accessible.
  • Do not publish a complete result if the repository changes during discovery. Serialize concurrent discovery updates.
  • Treat malformed, outdated, wrong-store, or unreadable cache data as unusable and rebuild safely without changing password-store contents.

Acceptance criteria

  • A cold scan shows progress and produces the exact path/hash/flag cache.
  • Opening with a valid cache builds the TOTP list without decrypting entries.
  • A warm refresh performs zero entry decryptions when ciphertext is unchanged.
  • Changing one ciphertext decrypts only that entry; adding/removing entries adds/removes exactly those cache records after a complete pass.
  • Cancelling after partial progress leaves completed records in a valid cache and a later refresh reuses them.
  • Cancellation, corruption recovery, store isolation, concurrent repository changes, and secret non-persistence have local regression coverage.
  • Required Rust gates, Xcode generation, warnings-as-errors simulator build, Analyze, and simulator visual/accessibility checks pass. Physical-device checks may be marked untested while interactive unlocking is unavailable.
## Goal Make the iPhone TOTP pane open immediately from a small local discovery index and make explicit discovery visibly progress instead of repeatedly decrypting the entire password store. ## Required behavior - `crates/storage` owns discovery, cache semantics, invalidation, progress, and cancellation. Apple code only invokes Rust and presents Rust-provided state. - Store the cache outside the password-store repository with private file permissions. - Each cache record contains exactly the canonical entry filename/path, a SHA-256 hash of the undecrypted ciphertext, and an `is_totp` boolean. A version and canonical store identity may be stored at the catalog level. - Never cache decrypted entry text, OTP URIs, secrets, issuer/account metadata, generated codes, passwords, passphrases, or key material. - Normal TOTP-pane population reads the cache without decrypting entries. - Pull-to-refresh rebuilds/updates the cache. If no usable cache exists, discovery starts automatically after authentication. - During update, hash current ciphertext and decrypt only entries missing from the cache or whose ciphertext hash changed. Reuse unchanged `is_totp` flags without decryption. - Atomically checkpoint every newly inspected or changed record as discovery proceeds. If discovery is cancelled or the process is suspended, completed records remain available and the next refresh resumes from their hashes. Keep older records until a complete pass so interrupted discovery cannot incorrectly prune unseen files. - After a complete stable pass, remove records for entries no longer in the filesystem. - Publish typed, cancellable, monotonic progress including phase, total, inspected, cache hits, matches, and unavailable entries. Use a determinate progress bar once the total is known and keep cancellation accessible. - Do not publish a complete result if the repository changes during discovery. Serialize concurrent discovery updates. - Treat malformed, outdated, wrong-store, or unreadable cache data as unusable and rebuild safely without changing password-store contents. ## Acceptance criteria - A cold scan shows progress and produces the exact path/hash/flag cache. - Opening with a valid cache builds the TOTP list without decrypting entries. - A warm refresh performs zero entry decryptions when ciphertext is unchanged. - Changing one ciphertext decrypts only that entry; adding/removing entries adds/removes exactly those cache records after a complete pass. - Cancelling after partial progress leaves completed records in a valid cache and a later refresh reuses them. - Cancellation, corruption recovery, store isolation, concurrent repository changes, and secret non-persistence have local regression coverage. - Required Rust gates, Xcode generation, warnings-as-errors simulator build, Analyze, and simulator visual/accessibility checks pass. Physical-device checks may be marked `untested` while interactive unlocking is unavailable.
hugo added this to the 04 - iPhone and Apple Watch apps milestone 2026-08-11 20:06:21 +00:00
hugo added the enhancementcritical labels 2026-08-11 20:06:21 +00:00
hugo added the untested label 2026-08-11 21:15:48 +00:00
Author
Owner

Implemented in 9759162.

Implementation:

  • Added a private, versioned, store-isolated TOTP catalog containing only canonical entry paths, SHA-256 ciphertext hashes, and is_totp flags.
  • Added atomic per-record checkpoints, warm hash reuse, changed-entry-only decryption, stable-pass deletion pruning, corruption/wrong-store fallback, serialized discovery, cancellation, and concurrent-repository-change rejection.
  • Exposed typed monotonic progress and cancellation through the Rust/UniFFI boundary. The iPhone pane now opens from cache without decryption, auto-discovers only when no cache exists, and updates through pull-to-refresh with determinate progress and Cancel.
  • Interrupted discovery now immediately reloads its partial cache. Cached-row opening unlocks only the selected entry and resumes directly to its detail. Cleared the stale unlock overlay exposed after returning from detail.
  • Corrected upstream pass hierarchy handling so foo.gpg and foo/ can coexist; non-trailing paths resolve the entry and trailing slash resolves the directory.

Verification:

  • cargo fmt --all -- --check
  • RUSTFLAGS="-D warnings" cargo check --workspace --all-targets
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace
  • xcodegen generate
  • Signed Debug iPhone 17 Pro simulator build with Swift/Clang warnings as errors
  • Xcode Analyze with warnings as errors
  • Live simulator checks: cold progress, accessible Cancel/progress state, incremental checkpoint retention, partial-cache recovery after cancel, next-launch cached list while locked, direct cached-row detail opening, and clean Back navigation without a spinner.

Deferred under the untested label: Release build/install and interaction verification on the paired physical iPhone.

Implemented in 9759162. Implementation: - Added a private, versioned, store-isolated TOTP catalog containing only canonical entry paths, SHA-256 ciphertext hashes, and is_totp flags. - Added atomic per-record checkpoints, warm hash reuse, changed-entry-only decryption, stable-pass deletion pruning, corruption/wrong-store fallback, serialized discovery, cancellation, and concurrent-repository-change rejection. - Exposed typed monotonic progress and cancellation through the Rust/UniFFI boundary. The iPhone pane now opens from cache without decryption, auto-discovers only when no cache exists, and updates through pull-to-refresh with determinate progress and Cancel. - Interrupted discovery now immediately reloads its partial cache. Cached-row opening unlocks only the selected entry and resumes directly to its detail. Cleared the stale unlock overlay exposed after returning from detail. - Corrected upstream pass hierarchy handling so foo.gpg and foo/ can coexist; non-trailing paths resolve the entry and trailing slash resolves the directory. Verification: - cargo fmt --all -- --check - RUSTFLAGS="-D warnings" cargo check --workspace --all-targets - cargo clippy --workspace --all-targets -- -D warnings - cargo test --workspace - xcodegen generate - Signed Debug iPhone 17 Pro simulator build with Swift/Clang warnings as errors - Xcode Analyze with warnings as errors - Live simulator checks: cold progress, accessible Cancel/progress state, incremental checkpoint retention, partial-cache recovery after cancel, next-launch cached list while locked, direct cached-row detail opening, and clean Back navigation without a spinner. Deferred under the untested label: Release build/install and interaction verification on the paired physical iPhone.
hugo closed this issue 2026-08-11 21:16:18 +00:00
hugo reopened this issue 2026-08-11 21:17:35 +00:00
Author
Owner

Physical-device verification completed on the paired iPhone: the signed Release build was installed and launched successfully; TOTP discovery could be unlocked and interrupted with retained results; cached entries reopened without a full rescan or lingering spinner; and the cache was reused after returning to and relaunching the app. Issue #74 is complete.

Physical-device verification completed on the paired iPhone: the signed Release build was installed and launched successfully; TOTP discovery could be unlocked and interrupted with retained results; cached entries reopened without a full rescan or lingering spinner; and the cache was reused after returning to and relaunching the app. Issue #74 is complete.
hugo removed the untested label 2026-08-11 21:30:46 +00:00
hugo closed this issue 2026-08-11 21:30:47 +00:00
Sign in to join this conversation.