Skip to content

fix(platform): derive identity-rescan keys through the wallet signer#3860

Merged
QuantumExplorer merged 3 commits into
v3.1-devfrom
claude/jolly-turing-d159ec
Jun 12, 2026
Merged

fix(platform): derive identity-rescan keys through the wallet signer#3860
QuantumExplorer merged 3 commits into
v3.1-devfrom
claude/jolly-turing-d159ec

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jun 11, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

SwiftExampleApp → Identities → Add → "Re-scan for Identities" → pick a mnemonic-restored wallet → "Re-scan Wallet" failed immediately with:

Couldn't derive identity-authentication keys: Invalid identity data: Failed to derive authentication key: Invalid parameter: External signable wallet has no private key

and discovered 0 identities.

App wallets are loaded into the in-process WalletManager as key_wallet::WalletType::ExternalSignable — the seed lives in iOS Keychain, not in process. Both the discovery scan (IdentityWallet::discover) and its key preview (platform_wallet_preview_identity_registration_keys) derived DIP-9 identity-auth keys directly from that in-memory wallet, which holds no private key, so they bottomed out in root_extended_priv_key() returning External signable wallet has no private key.

Identity registration on the same wallets already works because its key derivation never touches the in-memory wallet — it resolves the mnemonic on demand through a Swift-owned MnemonicResolver (Keychain-backed), builds a master ExtendedPrivKey, and derives from it. This PR routes the rescan / preview through that same mechanism.

What was done?

packages/rs-platform-wallet

  • identity_handle.rs: added derive_identity_auth_key_hash_from_master, a master-xpriv sibling of derive_identity_auth_key_hash that goes through derive_ecdsa_identity_auth_keypair_from_master + ripemd160_sha256, so the rescan and registration derivations can never drift.
  • discovery.rs: factored the gap-limit scan into one shared discover_inner with a pluggable KeyHashSource (resident wallet vs. supplied master) and added the public discover_from_master. discover() behavior for key-resident wallets is unchanged; the per-index read lock is now only taken on the wallet-internal derive path. load_identity_by_index (same latent flaw) was intentionally left untouched.

packages/rs-platform-wallet-ffi (signatures changed in place — pre-release)

  • platform_wallet_discover_identities and platform_wallet_preview_identity_registration_keys now take a MnemonicResolverHandle. When non-null they resolve the mnemonic keyed by the wallet handle's own wallet_id (self-pinned, same rationale as dash_sdk_derive_identity_key_at_slot_with_resolver), build the master, and derive via discover_from_master / derive_ecdsa_identity_auth_keypair_from_master; when null they keep the resident-wallet behavior for key-resident consumers.
  • Resolver failures map to distinct NOT_FOUND / BUFFER_TOO_SMALL / other messages. The resolve→parse→seed→master sequence is shared in a new resolve_master_from_resolver helper so both entry points stay in lockstep. Mnemonic/seed live in Zeroizing buffers; the master's scalar is non_secure_erased before return (ExtendedPrivKey has no Drop).

packages/swift-sdk

  • ManagedPlatformWallet.discoverIdentities / previewIdentityRegistrationKeys construct a MnemonicResolver (default WalletStorage, overridable for tests) and pass its handle to the updated FFI. discoverIdentities runs the FFI inside Task.detached, so the resolver is captured in the closure and the FFI call is wrapped in withExtendedLifetime to keep ARC holding it for the call's duration. Public signatures stay source-compatible (defaulted params only) — the example app's SearchWalletsForIdentitiesView compiles unchanged. Swift stays a thin bridge; no derivation/policy moved into Swift (per swift-sdk/CLAUDE.md).

How Has This Been Tested?

  • cargo fmt --all; cargo clippy clean for both platform-wallet and platform-wallet-ffi.
  • cargo test -p platform-wallet --all-features: 223 passed, 0 failed (220 prior + 3 new). New identity_handle::tests pin the fix for the "abandon … about" vector:
    • master_hash_matches_resident_wallet_hash — master-based hash equals the resident-wallet derive_identity_auth_key_hash on a Mnemonic wallet across slots/networks.
    • external_signable_errors_but_master_succeeds — the resident derive errors on an ExternalSignable wallet while the master derive succeeds and reproduces the key-resident hash.
    • master_hash_matches_registration_keypair_pubkey_hash — parity with registration's in-creation key #0.
  • cargo test -p platform-wallet-ffi --all-features: all passing (100 + 26 + 5/9, 4 ignored).
  • cargo check --workspace --all-targets: clean.
  • cd packages/swift-sdk && ./build_ios.sh --target sim --profile release: succeeded; the regenerated platform-wallet-ffi.h shows the new MnemonicResolverHandle *mnemonic_resolver_handle parameter on both functions.
  • xcodebuild -project SwiftExampleApp/SwiftExampleApp.xcodeproj -scheme SwiftExampleApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 16': BUILD SUCCEEDED with SearchWalletsForIdentitiesView unchanged.

Breaking Changes

None that warrant a ! type. The two FFI symbols gained a resolver parameter (pre-release SDK/FFI churn, not consensus-breaking); the only callers are the two Swift SDK methods, whose public signatures remain source-compatible via defaulted parameters.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Identity discovery and registration key preview now support external-signable and watch-only wallets in addition to resident-key wallets.
    • Methods for identity operations now accept wallet storage parameters for enhanced configuration flexibility.
  • Tests

    • Added validation tests for identity key derivation consistency across wallet types and networks.

App wallets load into the in-process WalletManager as
key_wallet::WalletType::ExternalSignable (the seed lives in iOS Keychain,
not in process). The "Re-scan for Identities" flow derived DIP-9
identity-auth keys directly from that in-memory wallet, which has no
private key, so the scan and its key preview both failed immediately with
"External signable wallet has no private key" and discovered nothing.

Mirror the mechanism identity registration already uses: resolve the
wallet's mnemonic on demand through a Swift-owned MnemonicResolver
(Keychain-backed), build a master ExtendedPrivKey, and derive the scan /
preview keys from that master.

platform-wallet:
- identity_handle.rs: add derive_identity_auth_key_hash_from_master, a
  master-xpriv sibling of derive_identity_auth_key_hash that goes through
  derive_ecdsa_identity_auth_keypair_from_master + ripemd160_sha256, so
  the rescan and registration derivations can never drift.
- discovery.rs: factor the gap-limit scan into a shared discover_inner
  with a pluggable KeyHashSource (resident wallet vs. supplied master)
  and add the public discover_from_master; discover() behavior for
  key-resident wallets is unchanged. The per-index read lock is now only
  taken on the wallet-internal derive path.
- unit tests pin the fix: master-based hash equals the resident-wallet
  hash on a Mnemonic wallet, the resident derive errors on an
  ExternalSignable wallet while the master derive succeeds, and the
  master hash matches the registration keypair's pubkey hash.

platform-wallet-ffi:
- platform_wallet_discover_identities and
  platform_wallet_preview_identity_registration_keys take a
  MnemonicResolverHandle. When non-null they resolve the mnemonic keyed
  by the wallet handle's own wallet_id, build the master, and derive via
  discover_from_master / derive_ecdsa_identity_auth_keypair_from_master;
  when null they keep the resident-wallet behavior. Resolver failures map
  to distinct NOT_FOUND / BUFFER_TOO_SMALL / other messages, and the
  resolve→master sequence is shared in resolve_master_from_resolver so
  both entry points stay in lockstep. Mnemonic/seed live in Zeroizing
  buffers; the master's scalar is non_secure_erase'd before return.

swift-sdk:
- ManagedPlatformWallet.discoverIdentities / previewIdentityRegistrationKeys
  construct a MnemonicResolver (default WalletStorage, overridable for
  tests) and pass its handle to the updated FFI. discoverIdentities runs
  the FFI inside Task.detached, so the resolver is captured and wrapped in
  withExtendedLifetime to keep ARC holding it for the call. Public
  signatures stay source-compatible (defaulted params only); the example
  app's SearchWalletsForIdentitiesView compiles unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@QuantumExplorer, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 3 minutes and 15 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 390fdb1f-1be2-492a-a124-25acafcaf0ab

📥 Commits

Reviewing files that changed from the base of the PR and between 2a7d096 and 74eeb25.

📒 Files selected for processing (1)
  • packages/rs-platform-wallet-ffi/src/identity_key_preview.rs
📝 Walkthrough

Walkthrough

This PR enables identity discovery and registration-key preview for watch-only and external-signable wallets by adding a mnemonic-resolver-based master-key derivation path that branches at the FFI boundary and core wallet layer, supporting both resident-key and resolver-backed wallet capabilities.

Changes

Identity Discovery and Key Preview for External-Signable Wallets

Layer / File(s) Summary
Mnemonic resolver master-key derivation helper
packages/rs-platform-wallet-ffi/src/identity_keys_from_mnemonic.rs
Added resolve_master_from_resolver to invoke a Swift-owned mnemonic resolver, validate UTF-8, parse BIP-39, derive a zeroized seed, and return an ExtendedPrivKey master key with detailed error mapping.
Core wallet identity discovery with master-xpriv support
packages/rs-platform-wallet/src/wallet/identity/network/discovery.rs
Introduced KeyHashSource enum to parameterize discovery source, refactored discover to delegate to discover_inner, added discover_from_master entry point that derives probe key hashes from a caller-supplied master ExtendedPrivKey.
Master-based identity-auth key hash derivation
packages/rs-platform-wallet/src/wallet/identity/network/identity_handle.rs
Added derive_identity_auth_key_hash_from_master for lock-free RIPEMD160(SHA256) hashing of identity-auth slots from a master key, with tests verifying equivalence to resident-wallet hashes and external-signable wallet compatibility.
FFI identity discovery with resolver handle routing
packages/rs-platform-wallet-ffi/src/identity_discovery.rs
Updated platform_wallet_discover_identities to accept a mnemonic resolver handle, branch on wallet capability via a short read lock, route to resident-wallet discover or discover_from_master after resolver invocation, and explicitly scrub the resolved master post-scan.
FFI identity key preview with resolver handle routing
packages/rs-platform-wallet-ffi/src/identity_key_preview.rs
Updated platform_wallet_preview_identity_registration_keys to accept a mnemonic resolver handle, introduced DeriveSource to branch derivation source, derives all preview rows with per-row FFI allocation/cleanup, and explicitly scrubs the resolved master scalar before return.
Swift SDK support for resolver-based mnemonic access
packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift
Updated previewIdentityRegistrationKeys and discoverIdentities to accept optional storage: WalletStorage, create a MnemonicResolver, pin its lifetime, and pass resolver.handle to updated FFI functions.
Module re-export formatting
packages/rs-platform-wallet/src/wallet/identity/network/mod.rs
Re-formatted pub use identity_handle::{ ... } re-export block with adjusted line breaks; exported identifiers unchanged.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related Issues

Suggested Labels

ready for final review

Suggested Reviewers

  • shumkov
  • thepastaclaw

Poem

🐰 A wallet with no keys, yet still may roam,
Through mnemonic paths to find its home,
The master guides each probe with care,
From Keychain's vault through zeroized air,
Two paths converge—one swift, one strong—
Identity blooms where wallets belong! 🗝️✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title refers to a real part of the change (identity-rescan key derivation through wallet signer), but misses the main point: enabling discovery/preview to work with external-signable (ExternalSignable/watch-only) wallets via mnemonic resolution, which is the primary issue being fixed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/jolly-turing-d159ec

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@thepastaclaw

thepastaclaw commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

✅ Review complete (commit 74eeb25)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

PR is well-scoped: it routes identity rescan/preview key derivation through the existing Swift-owned mnemonic resolver so iOS Keychain-backed ExternalSignable wallets can derive DIP-9 identity-auth keys without an in-process seed. One blocking issue: the new synchronous preview Swift wrapper does not extend the MnemonicResolver's lifetime across the FFI call (the discovery path does), creating a real use-after-free window because the Rust ctx is passUnretained. Two convergent suggestions worth addressing: the preview's resident-wallet branch now takes the wallet_manager read lock per row instead of once, and the Swift wrapper unconditionally forces every wallet through the resolver — a regression for resident-Mnemonic wallets created with createWallet(seed:) or without a Keychain-stored mnemonic.

🔴 1 blocking | 🟡 2 suggestion(s)

1 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift`:
- [BLOCKING] packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift:791-798: Keep mnemonic resolver alive across the preview FFI call
  `MnemonicResolver` builds its Rust callback context with `Unmanaged.passUnretained(self)` and frees the underlying handle in `deinit` — Rust only ever sees a raw pointer back to the Swift object, so ARC must keep the resolver alive for the whole synchronous FFI call. In this new preview path, the last Swift-level use of `resolver` is just evaluating `resolver.handle` as a parameter, after which ARC is free to end the object's lifetime. If the resolver is released before or during the call, Rust dereferences a freed `MnemonicResolverHandle` (or invokes the destroyed callback context inside `resolve_master_from_resolver`), causing FFI use-after-free / crash / potential memory corruption on a user-triggered preview. The async `discoverIdentities` path in this same PR already wraps the equivalent call in `withExtendedLifetime(resolver)` for exactly this reason; the synchronous preview needs the same guard.
- [SUGGESTION] packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/ManagedPlatformWallet.swift:1199-1210: Swift wrapper forces every wallet through the resolver, regressing resident-Mnemonic flows
  The Rust FFI deliberately keeps `mnemonic_resolver_handle` nullable so key-resident wallets keep the historical resident-wallet derivation. Both Swift wrappers (`discoverIdentities` and `previewIdentityRegistrationKeys`) unconditionally construct a `MnemonicResolver` and pass `resolver.handle`, so the null-handle fallback is unreachable from Swift. Concrete regressions: `PlatformWalletManager.createWallet(seed:)` (no BIP-39 mnemonic exists to resolve) and `createWallet(mnemonic:)` callers that do not separately persist the mnemonic via `WalletStorage` — discovery used to derive from the in-process wallet and now fails with resolver `NOT_FOUND`. If the design intent really is "iOS Keychain-backed only," surface that intent by either documenting the new precondition, falling back to a null resolver when no mnemonic is stored, or offering an explicit resident-wallet variant. Otherwise the public API silently loses a previously-working path.

In `packages/rs-platform-wallet-ffi/src/identity_key_preview.rs`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/src/identity_key_preview.rs:288-312: Resident-wallet preview path acquires WalletManager read lock per row
  Before this PR, `platform_wallet_preview_identity_registration_keys` took `wallet.wallet_manager().blocking_read()` and resolved `key_wallet` once at the top of the closure, then reused them for every preview row. After the refactor, the resident-wallet branch of `derive_material` runs per iteration and re-acquires `blocking_read()` plus re-looks-up `get_wallet(&wallet.wallet_id())` on every row. The resolver path correctly hoists the master xpriv once outside the loop, so the resident path is now strictly worse than the original with no upside on the resolver side. Hoist the resident-path lock acquisition (and the `get_wallet` lookup) before the loop — matching how `master_opt` is resolved once — and let `derive_material` consume a borrowed `&Wallet`/`KeyHashSource`-shaped enum per row. Bonus: keeps the two paths symmetric in shape.

Comment thread packages/rs-platform-wallet-ffi/src/identity_key_preview.rs Outdated
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.73%. Comparing base (93b657d) to head (74eeb25).
⚠️ Report is 15 commits behind head on v3.1-dev.

❗ There is a different number of reports uploaded between BASE (93b657d) and HEAD (74eeb25). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (93b657d) HEAD (74eeb25)
rust 2 1
Additional details and impacted files
@@              Coverage Diff              @@
##           v3.1-dev    #3860       +/-   ##
=============================================
- Coverage     87.15%   70.73%   -16.43%     
=============================================
  Files          2641       20     -2621     
  Lines        327793     2788   -325005     
=============================================
- Hits         285701     1972   -283729     
+ Misses        42092      816    -41276     
Components Coverage Δ
dpp ∅ <ø> (∅)
drive ∅ <ø> (∅)
drive-abci ∅ <ø> (∅)
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value ∅ <ø> (∅)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Follow-up review fixes for the identity rescan/preview DIP-9 key
derivation in PR #3860:

- Swift preview wrapper: pin the MnemonicResolver across the synchronous
  FFI call with withExtendedLifetime. Its Rust ctx is passUnretained and
  the handle is freed in deinit, so without pinning ARC could deallocate
  it at the resolver.handle argument (last Swift use) while Rust is
  mid-call — a use-after-free. Mirrors discoverIdentities.

- Preview FFI: hoist the resident-path wallet-manager read guard and
  get_wallet lookup out of the per-row loop (they were re-acquired every
  row). The guard is now taken once and held across the pure-compute
  loop, matching pre-PR behavior; the per-row derive consumes a borrowed
  key source so the resolver and resident paths are symmetric in shape.

- Both FFI entry points: select the derivation source by wallet
  capability instead of forcing the resolver. If the in-process wallet
  holds resident private keys (not external-signable, not watch-only),
  use the historical in-process derive and never touch the resolver
  (also skipping a pointless iOS Keychain read); otherwise consult the
  resolver, returning an enriched error when it's null for an
  external-signable/watch-only wallet. This restores raw-seed /
  createWallet(seed:) wallets, whose mnemonic may never have been
  persisted to WalletStorage. Module/function and Swift doc comments
  updated to describe the capability-based selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

All three prior findings (preview FFI resolver lifetime, per-row WalletManager lock in resident preview, Swift forcing the resolver path on resident wallets) are FIXED on head 2a7d096. The capability-based dispatch in Rust (is_external_signable || is_watch_only) is the correct invariant, and resident Mnemonic/Seed/ExtendedPrivKey wallets now skip the resolver entirely. One new minor finding: the preview path holds the wallet-manager read guard across the Swift mnemonic-resolver callback (and the full row loop), unlike the discovery path which deliberately scopes the guard to the capability check.

🟡 1 suggestion(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet-ffi/src/identity_key_preview.rs`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/src/identity_key_preview.rs:353-447: Preview path holds WalletManager read guard across the Swift resolver callback
  The preview closure acquires `wm = wallet.wallet_manager().blocking_read()` at line 353 and only releases it at the closure's end (line 446). For the external-signable branch this means the guard is held across `resolve_master_from_resolver(...)` at line 391 — a synchronous re-entry into Swift that performs an iOS Keychain read (and can stall on biometric unlock) — and across the entire per-row derive loop. The sibling discovery path in `identity_discovery.rs:165-178` deliberately scopes the read guard to a tight block that only inspects the wallet's shape (`is_external_signable` / `is_watch_only`), drops it before calling the resolver, and never crosses `block_on_worker`. Only the resident branch genuinely needs the guard for the loop's duration (it borrows `&Wallet`); the master branch does not need the guard past the capability check and the `wallet_id` / `network` reads. Today no deadlock occurs because the Swift resolver does not touch the wallet manager, but the wider hold window blocks concurrent wallet-manager writers behind a synchronous Keychain round-trip and introduces a non-obvious cross-FFI reentrancy invariant that the discovery path already avoids. Mirror the discovery shape: capture `wallet_has_resident_keys`, `network`, and `wallet_id` under the short guard, drop `wm`, then resolve the master and either re-acquire the guard for the resident branch's loop or hand the master to the loop directly.

Comment thread packages/rs-platform-wallet-ffi/src/identity_key_preview.rs Outdated
…heck

`platform_wallet_preview_identity_registration_keys` held the
wallet-manager read guard across the synchronous Swift resolver
callback (`resolve_master_from_resolver`, an iOS Keychain read that can
stall on biometric unlock) and the whole derive loop, because the
`DeriveSource::Resident` borrow kept it alive.

Restructure to two-phase locking, mirroring the discovery path: a short
guard scoped to the capability check is dropped before any resolver
interaction; only the resident branch re-acquires the guard, and only
for the loop's duration. The master branch holds no guard past the
capability check. Zeroization is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Incremental + cumulative verification at head 74eeb25. The prior locking concern is FIXED: the preview path now implements two-phase locking — a short capability-check read guard (lines 370-383) is dropped before the Swift mnemonic resolver call (lines 408-410), and the read guard is only re-acquired for the resident-derive loop (lines 425-439), matching the discovery-path invariant. No new in-scope defects identified across the FFI, Rust quality, security, and FFI-engineer perspectives. The codex-security-auditor finding about an empty SetPrices schedule panic touches an unmodified file (rs-drive token direct-purchase transformer) and is unrelated to this PR's identity-rescan/preview scope, so it is dropped.

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

✅ DashSDKFFI.xcframework built for this PR.

SwiftPM (host the zip at a stable URL, then use):

.binaryTarget(
  name: "DashSDKFFI",
  url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
  checksum: "4f0099d7859e85299c34d6292310c4075a2087392e23d9ae19e31f7f9105a48c"
)

Xcode manual integration:

  • Download 'DashSDKFFI.xcframework' artifact from the run link above.
  • Drag it into your app target (Frameworks, Libraries & Embedded Content) and set Embed & Sign.
  • If using the Swift wrapper package, point its binaryTarget to the xcframework location or add the package and place the xcframework at the expected path.

@QuantumExplorer
QuantumExplorer merged commit 1718563 into v3.1-dev Jun 12, 2026
15 of 16 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/jolly-turing-d159ec branch June 12, 2026 04:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants