Skip to content

fix(swift-sdk): isLocal = mine-or-tracked; promote wallet identities, fix observed-entry mislinking - #4375

Merged
QuantumExplorer merged 1 commit into
v4.2-devfrom
fix/persistent-identity-islocal
Aug 12, 2026
Merged

fix(swift-sdk): isLocal = mine-or-tracked; promote wallet identities, fix observed-entry mislinking#4375
QuantumExplorer merged 1 commit into
v4.2-devfrom
fix/persistent-identity-islocal

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Aug 11, 2026

Copy link
Copy Markdown
Member

Semantics (owner-decided)

PersistentIdentity.isLocal means this identity is yours or deliberately tracked on this device:

  • wallet-derived identities are ALWAYS local — the persister promotes the flag when it attaches the wallet relationship;
  • manual adds are local — LoadIdentityView's by-id/by-name flows mark their own rows (true is also the initializer default: a directly-constructed row is a manual add);
  • false only for incidental rows — observed foreign identities materialized by sync that nobody asked to track.

Promote-only: no sync path ever writes false over true. The flag makes no claim about signing capability — consumers that need capability compute it live; wallet-owned filtering has walletOwnedIdentitiesPredicate. Reviews proposing capability/evidence gating of this flag are out of scope by owner decision.

Issue being fixed

The persister wrote a constant isLocal: false for every row it created and nothing promoted, so the wallet's own identity (correct wallet relationship, identityIndex 0) showed as not-local on a real mainnet device — hiding the identity-key refresh affordances in dashwallet-ios (see dashwallet-ios #981). LoadIdentityView additionally clobbered its own manual adds with false.

What was done

One commit, no schema change:

  • persistIdentities promotes isLocal on wallet linkage; observed rows stay false; existing rows are never demoted.
  • Wallet-relationship hygiene (same audit, real bugs): the scope-wallet fallback is gated on identity_index != nil so observed entries are never mislinked to the changeset's scope wallet; a fabricated scope link clears when its identity re-emits observed; a link matching a declared-but-unresolvable owner survives while one contradicting the declaration clears; another wallet's valid relationship always survives a foreign manager's observed emission.
  • loadWalletList() runs a one-shot promote-only heal for constant-false-era stores.
  • LoadIdentityView marks manual adds true.
  • Example app: the dead "Local Only / On Network" badge reading is retired (incidental rows show "Observed"); the !isLocal network-feature gates are removed (they'd hide DPNS/tokens/profile/refresh for every wallet identity under the real semantics); wallet-signing actions gate on hasLoadedWallet.

History note

An extensive capability-oriented hardening series (evidence-gated promotion, seed-binding markers, tri-state keychain probes) was built here across 12 review rounds and then superseded by the owner's semantics decision — archived unmerged on archive/islocal-review-hardening. Genuinely pre-existing defects discovered during that series (forget-key dual-scheme deletion, KeyDetail import dropping its identifier, the out-of-wallet restore channel reserved by IdentityRestoreEntryFFI's doc, FFI error taxonomy for credential failures) should be tracked as separate issues, not re-litigated on this PR.

How Has This Been Tested

IdentityIsLocalPersistenceTests (in-memory store through the real persister bridge): promotion (direct + index-fallback), observed rows stay incidental and unlinked, sync never demotes manual adds, declared-owner keep/clear, cross-wallet preservation, startup heal. 335 tests pass on simulator; the two IdentityResolverSignIntegrationTests keychain-entitlement failures pre-exist on the baseline. SwiftExampleApp builds clean under warnings-as-errors.

Companion dashwallet-ios PR #983 aligns the app's badge copy and balance-refresh loop.

🤖 Generated with Claude Code

Loading
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