UI/UX Part 08 — Contacts, Requests, Verification & Identity UX Architecture

Reusable P2P Communication Platform

Status: UI/UX architecture specification
UI Series: Part 08
Desktop UI: Dioxus
Android UI: Kotlin + Jetpack Compose
Core runtime: Rust
Primary purpose: define the complete contacts, message requests, profile, identity verification, trust, blocking, reporting, nearby discovery, QR/SAS verification, own-device linking differentiation, and identity-change UX across desktop and Android.


1. Purpose

A communication product must clearly separate:

someone I know
someone I accepted
someone I verified
someone nearby
one of my own devices
someone blocked
an unknown requester

If these concepts are blurred, users can make dangerous assumptions.

The governing principle is:

Contact familiarity, conversation acceptance, cryptographic verification, and device trust are distinct concepts and must remain visually and semantically distinct.


2. Architectural Position

Rust Identity / Trust Core
        │
        ├── Account identity
        ├── Device identity
        ├── Contact metadata
        ├── Verification state
        ├── Requests
        ├── Block state
        └── Revocation state
        │
        ▼
Identity Presentation Service
        │
   ┌────┴─────┐
   │          │
Dioxus     Compose
Desktop    Android

3. Primary Identity Concepts

#![allow(unused)]
fn main() {
pub enum ContactRelationship {
    Unknown,
    Requested,
    Accepted,
    SavedContact,
    Verified,
    Blocked,
}
}

Device trust remains separate.


4. Do Not Collapse Trust States

Incorrect:

saved contact = verified

Correct:

saved contact
+
verification state

5. Core Contact Summary

#![allow(unused)]
fn main() {
pub struct ContactSummary {
    pub account: AccountId,
    pub display_name: String,
    pub avatar: AvatarRef,
    pub relationship: ContactRelationship,
    pub verification: VerificationState,
    pub presence: Option<PresenceSummary>,
    pub unread_requests: u32,
}
}

6. Verification States

#![allow(unused)]
fn main() {
pub enum VerificationState {
    Unverified,
    Verified,
    IdentityChanged,
    Revoked,
    Unknown,
}
}

7. Contact List Destinations

Recommended:

Contacts
Requests
Nearby
Blocked

Optional:

Organizations
Favorites

later.


8. Desktop Contacts Layout

Recommended:

Left sidebar:
    Contacts
    Requests
    Nearby
    Blocked

Main list/detail:
    contact list
    profile detail

9. Android Contacts Layout

Phone:

TopAppBar
Search
Contacts list
FAB / Add Contact

Secondary:

Requests
Nearby
Blocked

via tabs/chips/menu depending density.


10. Tablet/Foldable Contacts

Use:

NavigationRail
Contact List
Contact Detail

11. Contact Row Anatomy

Recommended:

avatar
display name
verification badge if meaningful
presence optionally
subtitle/status

Avoid too many trust icons.


12. Verification Badge

Show:

Verified

as explicit semantic state.

Do not rely on checkmark color alone.


13. Identity Changed Warning

This is high priority.

Show:

Identity changed
Review

not subtle icon only.


14. Presence

Optional small status.

Do not imply trust.


15. Add Contact Entry Points

Potential:

Search by known identifier
Scan QR
Nearby
Shared invite
Manual code

16. Contact Search

If global directory does not exist:

search local contacts only

Do not imply cloud username discovery unless backend supports it.


17. Add via QR

Flow:

Scan QR
→ Parse invite
→ Authenticate payload
→ Show identity preview
→ Verify/Accept
→ Save contact

18. Android QR UX

Compose launches camera/scanner.

Rust validates payload.


19. Desktop QR UX

Potential:

webcam scan
paste code
open image

Initial desktop v1 may prioritize:

paste code / show own QR

if camera scanning is less important.


20. Own Contact QR

User can show:

My contact QR

for another person.


21. Own-Device QR

Must look different from contact QR.

Label clearly:

Link another device

not:

Add contact

22. Device Link vs Contact Link

Hard UX distinction.

Own-device flow may grant:

account authorization
history sync
device trust

which is much more sensitive than contact addition.


23. Contact Invite Preview

Before acceptance show:

display name
avatar
identity fingerprint short form
invite expiry
source

24. Fingerprint

Full cryptographic fingerprint belongs in verification/details screen.

Not normal list row.


25. Short Fingerprint

Can show grouped code:

A7F2 19CC 8E42 ...

26. Safety Number / SAS

Verification flow may compare:

short numeric/string code

27. Verification Methods

#![allow(unused)]
fn main() {
pub enum VerificationMethod {
    QrScan,
    SasCompare,
    FingerprintCompare,
    TrustedDeviceIntroduction,
}
}

28. Verification Flow

Recommended:

Open Contact
→ Verify Identity
→ choose method
→ compare/scan
→ Rust validates transcript
→ confirmation
→ Verified

29. Verification Is Explicit

Do not automatically mark verified because:

invite was accepted
message delivered
peer is nearby

30. QR Verification

If both users are physically together:

scan each other's authenticated QR

best UX.


31. SAS Compare

Show:

same code on both devices?

Actions:

Matches
Doesn't match

32. Do Not Use Ambiguous Confirmation

Bad:

OK

Good:

Codes match
Codes do not match

33. Verification Success

Show:

Identity verified

with date/device context optionally.


34. Verification History

Contact details can show:

Verified on date
Verification method

without exposing unnecessary secrets.


35. Identity Change

If account/device root identity changes unexpectedly:

strong warning

36. Identity Change Screen

Show:

Previous identity no longer matches
Messages/calls may be blocked by security policy
Verify again before continuing

Actions:

Verify again
Trust new identity
Block
View details

37. Trust New Identity

High-risk action.

Require:

explicit confirmation

Possibly device authentication in high-security mode.


38. Identity Change Cannot Be Silently Cleared

Hard rule.


39. Contact Profile Screen

Recommended sections:

Identity
Message
Audio Call
Video Call
Shared Media
Verification
Devices summary if appropriate
Notifications
Block
Delete Contact

40. Profile Header

Shows:

avatar
display name
relationship
verification
presence

41. Contact Alias

User may assign local display name.

This does not change cryptographic identity.


42. Alias Editing

Action:

Edit contact name

Stored locally/account preference.


43. Claimed vs Local Name

If needed in security details:

Local name: Alice
Claimed account name: alice@example

44. Profile Status Text

Optional user status/about.

Treat as untrusted display content.


45. Shared Media

Link to Part 10 files/media view scoped to contact/conversation.


46. Notification Settings

Per-contact/conversation mute settings.


47. Block

Blocking should:

stop new messages
stop calls
stop presence sharing
stop typing/read receipts

according to backend policy.


48. Block UX

Action in contact details and request sheet.

Confirmation:

Block Alice?

Explain consequences briefly.


49. Blocked State

Profile shows:

Blocked
Unblock

Composer/call actions disabled.


50. Unblock

Explicit action.

Does not automatically:

verify
accept request
restore deleted history

51. Delete Contact

Deleting contact metadata is distinct from:

blocking
deleting conversation
revoking identity

52. Delete Contact UX

Explain:

removes saved contact information
does not block them

53. Requests

Unknown users/messages go to request area.


54. Request Summary

#![allow(unused)]
fn main() {
pub struct ContactRequestView {
    pub request_id: RequestId,
    pub account: AccountId,
    pub display_name: String,
    pub avatar: AvatarRef,
    pub preview: RequestPreview,
    pub received_at: Timestamp,
    pub trust_hint: RequestTrustHint,
}
}

55. Request Preview

Privacy-conscious default:

New message request

or limited excerpt.


56. Request Detail

Show:

sender identity preview
first message
mutual context if available
verification status

57. Request Actions

Accept
Delete
Block
Report

58. Accept

Creates accepted conversation/contact relationship.

Still:

Unverified

until verification.


59. Delete Request

Removes request locally according to backend semantics.


60. Block Requester

Blocks identity and removes request.


61. Report

Only if abuse/reporting system exists.


62. Reporting Privacy

User chooses what evidence is shared.

Do not auto-upload full conversation.


63. Report Flow

Potential:

Reason
Optional selected messages
Submit

64. Request Flood UX

Do not show hundreds of push-like cards.

Use:

request count
paged list
rate-limited notifications

65. Unknown Request Read Receipts

Default:

do not send

until accepted.


66. Unknown Request Typing

Default:

do not send

67. Unknown Request Presence

Default:

do not share

68. Nearby Discovery

Part 14 proximity.

Nearby list is:

discovery observation

not trusted identity.


69. Nearby Row

Show:

Nearby device/person
signal proximity hint
verification state

Avoid exact distance unless reliable.


70. RSSI

Do not show raw RSSI to normal users.

Advanced diagnostics only.


71. Nearby Discovery States

#![allow(unused)]
fn main() {
pub enum NearbyPeerState {
    Discovered,
    Connecting,
    Authenticating,
    Verified,
    Failed,
}
}

72. Nearby Identity Safety

Before authentication:

Unverified nearby device

73. Nearby Add Flow

Select
→ authenticate
→ compare/confirm identity
→ save contact

74. Nearby Does Not Mean Trusted

Hard rule.


75. Bluetooth/Wi-Fi Details

Normal UI should say:

Nearby

not:

BLE transport
Wi-Fi Aware

Diagnostics can expose path.


76. Own Device Nearby

If platform detects own-device link candidate:

Link your device

not normal contact flow.


77. Device Linking Entry

Dedicated:

Devices
→ Link new device

78. Own Device Verification

Can use:

QR
SAS
existing trusted device approval

79. Device Link Warning

Explain:

This device may access your messages and account

80. Device Authorization

Requires explicit approval.


81. Device Link Success

Show:

Device linked

and route to Devices screen.


82. Device Link Failure

Examples:

Code expired
Already used
Identity mismatch
Unsupported version

83. Contact vs Device Visual Language

Use different icons/labels.

Do not reuse identical "verified" badge semantics blindly.


84. Contact Verification

Means:

peer/account identity verified

85. Device Trust

Means:

this device is authorized under my account

86. Group Member Identity

Group member list should show:

display name
role
verification state if relevant

87. Group Verification Density

Do not show 100 verification icons by default.

Could show warning only when:

identity changed
unknown member

88. Member Detail

Tap member:

profile
message
call
verify
block

depending policy.


89. Organization Identity

Future managed deployments may have:

Organization verified
Managed account

separate from personal verification.


90. Directory Results

If organization directory exists:

Directory contact

does not necessarily mean verified E2EE identity.


91. Search Contacts

Local search:

name
alias
organization

92. Contact List Sorting

Recommended:

Favorites/Recent? optional
Alphabetical

93. Recommendation

Default alphabetical list.

Search handles large sets.


94. Favorites

Optional later.

Avoid excessive inbox-like ranking.


95. Recent Contacts

Calls/chats already provide recency.

No need separate unless useful.


96. Contact Import

Future:

Android contacts
CSV
vCard

must not automatically create verified identities.


97. Android Contacts Permission

Avoid requesting full system contacts unless feature actually uses it.


98. Contact Matching Privacy

If later matching phone/email contacts:

privacy-sensitive

requires separate backend/privacy architecture.


99. Manual Identifier Entry

Potential:

account code
public key/fingerprint
invite code

100. Identifier Validation

Rust authoritative.


101. Invalid Identifier

Show inline:

This code is not valid

102. Expired Invite

Show:

This invite has expired
Request a new one

103. Already Added

Show:

Already in contacts
Open contact

104. Self Contact

If user scans own contact code:

This is your identity

offer:

Link device

only if appropriate.


105. Duplicate Contact

Same AccountId must not create duplicate logical contact.

Aliases may merge.


106. Contact Merge

Future for imported address book.

Not needed for crypto identity core.


107. Identity Fingerprint Screen

Show:

full fingerprint
QR
copy
share
verification state

108. Fingerprint Formatting

Group for readability.


109. Copy Fingerprint

Explicit action.


110. Share Fingerprint

Can use OS share sheet.


111. QR Content

Should encode authenticated verification payload, not only pretty text.


112. QR Expiry

Verification/contact invite may expire.

UI displays expiry if relevant.


113. Screenshot of QR

May remain usable until expiry.

Security model must assume QR can be copied.


114. Single-Use Invite

UI should say:

This code can be used once

if applicable.


115. Verification Confirmation

After QR/SAS:

Verified

only after Rust confirms complete transcript.


116. Partial Verification

If flow interrupted:

remain Unverified

117. Verification Cancellation

No trust state change.


118. Verification Error

Examples:

Codes do not match
Session expired
Identity changed during verification

119. Verification Failure Severity

Mismatch should be strong:

Do not continue

with block/retry options.


120. Identity Revoked

If contact identity/device is revoked:

Revoked

and communication policy determines continuation.


121. Device Revocation Visibility

Contact details may show:

one of Alice's devices was removed

only if product exposes device-level details.

Default:

account-level trust only

122. Multi-Device Contact

Do not show every peer device in normal profile.

Advanced security details may.


123. Verified Account with New Device

Part 28 decides whether:

new device needs verification

UI shows security warning only if policy requires.


124. Verification Badge Scope

Must be clear whether verification applies to:

account root
specific device
current session

Recommendation UI:

account-level verified

with device details hidden unless needed.


125. Safety Number Change

If root identity changes:

verified badge removed
Identity changed warning

126. Contact Details Actions

Recommended:

Message
Audio Call
Video Call
Search in Conversation
Shared Media
Verify Identity
Mute
Block
Delete Contact

127. Destructive Ordering

Place:

Block
Delete Contact

in lower destructive section.


128. Desktop Profile Layout

Wide:

header
actions row
details sections
shared media preview
security section

129. Android Profile Layout

Scrollable:

large avatar/header
action buttons
sections/cards
destructive actions

130. Adaptive Tablet Profile

List/detail.


131. Call Availability

Disable call buttons if:

blocked
calls disabled
unsupported

Rust capability.


132. Message Availability

Composer/open chat action similarly capability-driven.


133. Contact Capability DTO

#![allow(unused)]
fn main() {
pub struct ContactCapabilities {
    pub can_message: bool,
    pub can_audio_call: bool,
    pub can_video_call: bool,
    pub can_verify: bool,
    pub can_block: bool,
    pub can_report: bool,
}
}

134. Android Long Press Contact

Potential menu:

Message
Call
Mute
Block

but primary tap should open profile/conversation depending product choice.


135. Recommendation

Tap contact row:

open contact profile

from Contacts area.

Tap conversation row:

open conversation

Keeps mental model clear.


136. Desktop Double Click

Optional:

open conversation

while single click opens profile/detail.

Not required.


137. Requests Desktop

Use list + detail.


138. Requests Android

Full-screen list/detail or bottom sheet.


139. Nearby Android

Can include scanning animation/status.

Respect reduced motion.


140. Nearby Desktop

Simple list with:

Searching nearby…

141. Discovery Toggle

User can start/stop nearby discovery.

Do not scan continuously without reason.


142. Battery Awareness

Android discovery UI may show:

Searching…

only while active.

Rust handles scheduling.


143. Location Permission

If Android requires location-related permission for certain nearby APIs, Kotlin handles platform explanation.

Do not claim location is being shared if only permission is technical requirement.


144. Privacy Explanation

Explain accurately:

Nearby permission is used to discover devices

not vague.


145. NFC Pairing

Android:

Tap phones

if supported.

Rust validates payload/transcript.


146. NFC Fallback

Offer:

Scan QR

147. Contact Request Notifications

Part 31 can show:

New message request

with generic privacy.


148. Request Notification Tap

Opens request detail.


149. Blocked Contact Notifications

None.


150. Verification Notification

Potential:

Identity changed

security alert.


151. Security Center Integration

Identity warnings link to Part 15 UI/UX security center later.


152. Device Screen Integration

Own-device linking routes to Devices.


153. Backup Integration

Verified contact state may be included in backup per Part 33.


154. Search Integration

Part 32 can search contacts.


155. Contact Search Result

Shows:

name
verification
relationship

156. Presence Privacy

If user hides presence from peer:

contact UI may still show peer presence if peer shares theirs

directionality matters.


157. Read Receipt Privacy

Per-contact setting may exist.


158. Typing Privacy

Per-contact or global.

Do not clutter profile initially unless advanced privacy page.


159. Notification Mute

Contact/profile may link to conversation notification settings.


160. Block Effects Explanation

Brief:

They will no longer be able to message or call you through this account.

Exact semantics must match backend.


161. Report vs Block

Report can imply:

share evidence

Block is local trust/action policy.

Do not merge.


162. Abuse Report Consent

Show exactly what content will be included.


163. Identity Details Privacy

Do not expose:

peer IP
device hardware identifiers
transport addresses

in contact profile.


164. Device Names

If showing peer devices in advanced view:

Alice's phone
Alice's desktop

only if peer/device metadata sharing policy allows.


165. Contact Notes

Future optional private notes.

If added:

local/private

not sent to peer.


166. Custom Nickname

Local only.


167. Avatar Change

Updates profile/list.

Does not affect verification.


168. Profile Name Change

Likewise.


169. Spoofing Risk

Verified state should remain tied to cryptographic identity, not display name/avatar.


170. Accessibility — Contact Row

Screen reader:

Alice, verified, online

or:

Bob, unverified

171. Request Accessibility

Announce:

Message request from Alice, unverified

172. Verification Accessibility

SAS code should be readable digit/group by digit/group.


173. QR Accessibility

Always provide alternate method:

Compare code manually

174. Color Independence

Verified/warning/blocked states require icon/text, not color alone.


175. Large Font

Fingerprint/SAS screens must wrap cleanly.


176. RTL

Contact/profile layouts mirror correctly.

Fingerprint itself should preserve canonical order.


177. Reduced Motion

Nearby scanning animation can simplify.


178. Keyboard Desktop

Shortcuts:

Ctrl/Cmd+N → Add/New contact or conversation depending context
Enter → open selected contact
Shift+F10 → menu

179. Desktop Focus

When contact list updates:

preserve focus by AccountId

180. Android TalkBack

Contact actions expose:

Message
Call
Verify
More

through accessible controls.


181. Screen Reader Verification

Announce state changes:

Identity verified
Verification failed

182. Empty Contacts

No contacts yet

Actions:

Scan QR
Add Contact
Find Nearby

183. Empty Requests

No message requests

184. Empty Nearby

No nearby devices found

with:

Try again

185. Empty Blocked

No blocked contacts

186. Loading Contacts

Local contacts load immediately.

Nearby discovery is separate active loading state.


187. Offline Contacts

Local contacts/profile remain fully usable.

Verification/contact invite may require direct/local connectivity depending method.


188. Offline QR Verification

Should work if two devices can establish local authenticated exchange.


189. Request Acceptance Offline

If request already local:

accept locally

and sync/send acknowledgement later if protocol supports.


190. Contact Alias Offline

Always local-first.


191. Verification Persistence

Verified state must survive app restart.


192. Identity Warning Persistence

Must remain until resolved.


193. Process Death Android

Compose reloads contact/trust snapshot from Rust.

No UI-local trust state.


194. Desktop Restart

Same.


195. Multi-Device Trust Sync

If verification is account-wide:

verified contact state syncs

to user's devices securely.


196. Device-Local Verification

If architecture chooses per-device verification:

UI must say so

Recommendation:

account-level verification where cryptographic model permits

197. Block Sync

Recommendation:

account-wide

across user's devices.


198. Alias Sync

Product choice.

Could be account-wide.


199. Contact Deletion Sync

Likewise.


200. Request Sync

Accepted/rejected state should synchronize to avoid duplicate requests.


201. Answered Elsewhere Equivalent

If request accepted on one device:

other devices update

202. Rust Presentation API

#![allow(unused)]
fn main() {
pub trait ContactPresentation {
    async fn list(
        &self,
        filter: ContactFilter,
    ) -> Result<ContactPage, UiError>;

    async fn profile(
        &self,
        account: AccountId,
    ) -> Result<ContactProfileView, UiError>;

    async fn accept_request(
        &self,
        request: RequestId,
    ) -> Result<(), UiError>;

    async fn block(
        &self,
        account: AccountId,
    ) -> Result<(), UiError>;

    async fn unblock(
        &self,
        account: AccountId,
    ) -> Result<(), UiError>;

    async fn delete_contact(
        &self,
        account: AccountId,
    ) -> Result<(), UiError>;
}
}

203. Verification API

#![allow(unused)]
fn main() {
pub trait VerificationPresentation {
    async fn begin(
        &self,
        account: AccountId,
        method: VerificationMethod,
    ) -> Result<VerificationSessionView, UiError>;

    async fn confirm_sas(
        &self,
        session: VerificationSessionId,
        matches: bool,
    ) -> Result<VerificationResultView, UiError>;

    async fn process_qr(
        &self,
        payload: VerificationQrPayload,
    ) -> Result<VerificationResultView, UiError>;
}
}

204. Nearby API

#![allow(unused)]
fn main() {
pub trait NearbyPresentation {
    async fn start_discovery(&self) -> Result<(), UiError>;
    async fn stop_discovery(&self) -> Result<(), UiError>;
    async fn connect(&self, candidate: NearbyCandidateId) -> Result<(), UiError>;
}
}

205. Device Link API

Keep separate:

#![allow(unused)]
fn main() {
pub trait DeviceLinkPresentation {
    async fn create_link_invite(&self) -> Result<DeviceLinkInviteView, UiError>;
    async fn accept_link_invite(&self, payload: DeviceLinkPayload) -> Result<(), UiError>;
}
}

206. Separate APIs Matter

This reinforces:

Contact verification ≠ own-device authorization

207. Contact Events

#![allow(unused)]
fn main() {
pub enum ContactUiEvent {
    ContactAdded(ContactSummary),
    ContactUpdated(ContactSummary),
    ContactRemoved(AccountId),
    RequestAdded(ContactRequestView),
    RequestRemoved(RequestId),
    VerificationChanged {
        account: AccountId,
        state: VerificationState,
    },
    BlockChanged {
        account: AccountId,
        blocked: bool,
    },
}
}

208. Android ViewModel

Owns:

current tab/filter
search query
sheet/dialog state
permission effects
QR scanner effect

Rust owns trust/contact truth.


209. Dioxus Presenter

Owns:

selection
sidebar filter
dialog/window state
focus

210. No Trust State in UI

Hard rule.


211. Security Error Mapping

Examples:

VerificationMismatch
InviteExpired
IdentityChanged
AlreadyBlocked
UnauthorizedDeviceLink

212. Error UX

Mismatch:

strong security warning

Expired:

simple retry/new code

Permission:

contextual platform request

213. Testing Matrix

Required:

contact list
search
profile
request
accept
delete request
block
unblock
verify QR
verify SAS
mismatch
identity change
nearby discovery
own-device link distinction
offline
multi-device sync

214. Android Tests

Include:

QR camera permission
NFC
nearby permission
TalkBack
large font
process death
deep link/invite

215. Desktop Tests

Include:

keyboard
context menu
QR display/paste
multi-pane profile
focus preservation

216. Security Tests

Verify UI cannot mark contact verified without Rust confirmation.


217. Request Flood Test

Large unknown-request set remains bounded/paged.


218. Identity Change Test

Warning persists across restart and blocks/limits actions according to policy.


219. Device Link Confusion Test

Own-device flow never uses ordinary contact acceptance wording.


220. Verification Mismatch Test

UI never displays success after mismatch.


221. Block Test

After block:

message/call controls disabled
presence/typing sharing stops according to backend

222. Multi-Device Block Test

Block on desktop updates phone if account-wide.


223. Accessibility Test

Full contact verification flow usable without:

camera
color
gesture

through manual SAS/fingerprint path.


224. Initial Production Recommendation

For v1, ship:

contact list
contact profile
message requests
accept/delete/block
QR contact invites
SAS/fingerprint verification
identity-change warning
blocked list
nearby discovery
own-device linking as separate flow

Defer:

phone-number contact matching
cloud username directory
social graph
mutual contacts
complex organization directory
contact notes
favorites

225. Definition of Done

UI/UX Part 08 is complete when:

  • contact familiarity, request acceptance, verification, blocking, and device trust are distinct
  • verified state can only come from Rust security/identity subsystem
  • request acceptance never implies verification
  • own-device linking uses a separate flow/API from contact addition
  • identity-change warnings are persistent and high-priority
  • QR/SAS/fingerprint verification flows are defined
  • nearby discovery is clearly marked unverified until authentication
  • blocked contacts cannot message/call/share presence according to policy
  • desktop Dioxus and Android Compose layouts are platform-native
  • contact profile, request detail, blocked list, and nearby screens are defined
  • multi-device trust/block/request synchronization behavior is explicit
  • accessibility, manual verification fallback, RTL, large font, and keyboard/TalkBack behavior are defined
  • contact, verification, nearby, and device-link Rust presentation APIs are separate and typed
  • request flood, mismatch, identity change, block, offline, and multi-device cases are tested

226. Final Architecture

                   RUST IDENTITY / TRUST CORE
                             │
        ┌────────────────────┼────────────────────┐
        │                    │                    │
     Contacts             Requests           Verification
        │                    │                    │
        └────────────────────┼────────────────────┘
                             │
                 Identity Presentation
                      ┌──────┴──────┐
                      │             │
                   Dioxus        Compose
                      │             │
               Desktop UX      Android UX

Separate sensitive path:

Own Device Linking
      │
      ▼
DeviceLinkPresentation
      │
      ▼
Device Authorization

not:

Contact Accept

227. Final Principle

The identity UX should help users understand exactly what they know and what they do not know.

The correct mental model is:

I know this contact
≠
I accepted this request
≠
I verified this identity
≠
I authorized this device

By preserving those distinctions, the product can remain friendly while still communicating real cryptographic trust correctly across Dioxus desktop and Android Compose.