Data Flow & Privacy Map

myowjaYOY/social-app

Generated by DocAgent — automated codebase documentation analysis. Based on analysis of 3 screens. Subject matter expert review is recommended before distribution.

April 19, 2026

Data Flow & Privacy Map (Mobile)

Application: social-app (Bluesky / AT Protocol) Document Title: Data Flow & Privacy Map (Mobile) Date: April 2026 Applicable Regulations: GDPR (EU) 2016/679, CCPA/CPRA (California), Apple App Store Privacy Nutrition Label requirements, Google Play Data Safety requirements Platform Scope: iOS and Android (React Native / Expo); web platform noted where relevant Document Status: Working Document — DPO Review Required Before Distribution


Executive Summary

Generated by DocAgent — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.

This Data Flow & Privacy Map covers ten screens of the social-app (Bluesky) React Native mobile application, spanning the authentication flow (login, password reset, account selection) and the direct messaging feature (conversation view, inbox, chat list, and messaging settings). The overall privacy posture of the documented screens is assessed as Moderate. The application demonstrates several privacy-positive practices — notably the use of in-memory-only handling for passwords and reset tokens, server-side moderation enforcement, and email verification gating for messaging features. However, several areas require investigation and remediation before the application can be considered fully compliant with GDPR Article 32 and MASVS-STOR-1 requirements.

Across the ten documented screens, twelve data categories were identified: account credentials (username/handle, password), email address, authentication tokens (access JWT), two-factor authentication codes, reset tokens, user profile data (display name, handle, DID), direct message content, conversation metadata, messaging preferences, notification preferences, and analytics/behavioral data. The most privacy-sensitive categories are authentication tokens and direct message content. On-device storage mechanisms documented include the session state layer (which abstracts Keychain/Keystore access) and a custom native module for notification preferences (backed by UserDefaults on iOS / SharedPreferences on Android). No direct AsyncStorage or MMKV usage for sensitive data was documented within the ten screens reviewed; however, the session state layer's underlying storage implementation is not documented at the screen level and requires investigation by the iOS/Android leads. One device permission data flow was identified: push notification permission requested post-login.

The top three highest-risk privacy concerns are: (1) The session state layer's on-device storage mechanism for authentication tokens (accessJwt) is not documented at the screen level — if tokens are stored in AsyncStorage rather than Keychain/Keystore, this constitutes a Critical MASVS-STOR-1 and GDPR Article 32 violation; (2) Direct message content is processed in real-time via ConvoProvider and its transport mechanism (WebSocket or polling) is not documented, creating uncertainty about whether message content is transmitted over TLS and whether it is stored on-device between sessions; (3) The analytics abstraction (ax.metric) fires behavioral events (login success, password reset outcomes, chat open) whose destination service, data minimization posture, and GDPR legal basis are not documented.

Assessment limitations: This document is based exclusively on per-screen technical documentation review. It does not reflect live data flow tracing, network traffic analysis, database inspection, or device forensics. All findings marked "(documented behavior — verify with data flow analysis)" or "(inferred from [source] — verify against [check])" require live verification before regulatory reliance.


Methodology & Scope

Regulatory Framework

Framework Application
GDPR (EU) 2016/679 Articles 5, 6, 9, 13, 17, 25, 30, 32, 35 — lawfulness, data minimization, right to erasure, privacy by design, ROPA, security, DPIA trigger assessment
CCPA/CPRA (California) Consumer rights mapping, sale/sharing of personal information assessment, sensitive personal information categories
Apple App Store Privacy Nutrition Labels App Privacy section — data types collected, data linked to user, data used to track
Google Play Data Safety Data collected, data shared, security practices, data deletion

Privacy Standard

ISO/IEC 27701:2019 — Privacy Information Management System. Controls referenced for processing inventory, data subject rights, and third-party processor management.

Risk Framework

NIST Privacy Framework v1.0 — Five core functions applied throughout: Identify (data inventory), Govern (legal basis, policies), Control (data subject rights), Communicate (transparency, consent), Protect (technical safeguards).

Mobile Storage Standard

OWASP MASVS-STOR (Mobile Application Security Verification Standard — Storage) — specifically MASVS-STOR-1 (sensitive data not stored in unprotected locations) and MASVS-STOR-2 (sensitive data not stored in backup-eligible locations without justification).

Scope

In scope: All ten screens provided in the documentation:

Mobile-specific scope additions: On-device storage mechanisms, device permission data collection (push notifications documented; camera/microphone/location not documented in these screens), biometric data handling, backup/restore behavior, native module storage.

Out of scope: Screens not provided in the documentation (profile screens, feed screens, onboarding, settings beyond messaging, server-side infrastructure, CDN, analytics backend).

Limitations

This assessment is based on technical documentation review only. The following cannot be verified without live analysis:


On-Device Storage Privacy Assessment

4.1 Storage Mechanism Inventory

The ten documented screens do not directly reference AsyncStorage, MMKV, or expo-secure-store by name in their component-level code. Storage is abstracted behind the session state layer (useSession/useSessionApi from #/state/session) and a custom native module (ExpoBackgroundNotificationHandler). The table below reflects what is documented and what requires investigation.

Storage Mechanism Encrypted Data Stored Survives Uninstall Backup Eligible Privacy Risk MASVS-STOR
Session state layer (#/state/session) — underlying mechanism not documented at screen level Not documented — requires investigation Authentication tokens (accessJwt), account DID, handle, stored account list Not documented — depends on underlying mechanism Not documented — depends on underlying mechanism Critical if AsyncStorage; Low if Keychain/Keystore MASVS-STOR-1 — requires verification
ExpoBackgroundNotificationHandler native module — iOS: UserDefaults or shared app group; Android: SharedPreferences (inferred from documentation description of "native key-value storage") No — UserDefaults and SharedPreferences are not encrypted by default (inferred from platform behavior — verify against native module source) playSoundChat boolean preference iOS: persists (UserDefaults survives uninstall on some configurations); Android: cleared on uninstall (inferred from platform behavior — verify against native module source) iOS: may be included in iCloud backup if not excluded; Android: may be included in Google Drive auto-backup Low — non-sensitive boolean preference MASVS-STOR-1 — low risk for this data type
React component state (in-memory) N/A — in-memory only Passwords, reset tokens, 2FA codes, email addresses, form field values — held during screen lifecycle only No — cleared on app close No Low — ephemeral, never persisted N/A
TanStack Query cache (in-memory) N/A — in-memory only User profiles, conversation lists, message metadata No — cleared on app close No Low — ephemeral N/A

[Not documented — WHO: iOS lead and Android lead; WHAT: What is the underlying storage mechanism used by #/state/session for accessJwt and the stored account list? Is it AsyncStorage, MMKV, Keychain (iOS) / Keystore (Android), or SecureStore? Please provide the storage key names and encryption status.; WHERE: Insert in Section 4.1 Storage Mechanism Inventory — Session state layer row, and in Data Retention & Deletion table — DC-003 row]

[Not documented — WHO: iOS lead; WHAT: Does the ExpoBackgroundNotificationHandler native module use a shared app group container for UserDefaults, and is the data excluded from iCloud backup via NSURLIsExcludedFromBackupKey?; WHERE: Insert in Section 4.1 Storage Mechanism Inventory — ExpoBackgroundNotificationHandler row, and Section 4.5 Backup and Restore Consent table]

⚠️ Critical finding: The ChooseAccountForm screen reads account.accessJwt from the session store to determine whether a stored session token exists. This confirms that authentication tokens are persisted on-device between app sessions. The storage mechanism for these tokens is the single most important privacy and security question for this application. If tokens are stored in AsyncStorage, they are stored in plain-text SQLite on the device, included in device backups, and accessible to any process with file system access on a rooted/jailbroken device — a Critical MASVS-STOR-1 violation and GDPR Article 32 failure.

4.2 Biometric Data Handling

No biometric authentication is documented in any of the ten reviewed screens. The Login Form documentation explicitly states: "No biometric auth, jailbreak detection, or certificate pinning is implemented in this component." The same statement appears in the security sections of the Password Updated Form, Set New Password Form, Forgot Password Form, and Choose Account Form.

API / Library Biometric Data Accessed Data Stored On-Device Template Stored by App Privacy Risk
None documented in reviewed screens N/A N/A N/A N/A — no biometric processing identified in these screens

GDPR Article 9 assessment: No biometric special category data processing is documented in the ten reviewed screens. If biometric authentication is implemented in other screens not covered by this document (e.g., a device unlock screen or profile verification flow), a separate DPIA would be required.

[Not documented — WHO: iOS lead and Android lead; WHAT: Does the application use Face ID, Touch ID, or Android Fingerprint authentication anywhere in the app (including screens not covered in this document)? If so, which library is used (expo-local-authentication, react-native-biometrics, or custom)?; WHERE: Insert in Section 4.2 Biometric Data Handling table, and add a new ROPA entry if applicable]

4.3 Secure Enclave and Keystore Usage

No direct references to iOS Secure Enclave (SecKeyCreateRandomKey with kSecAttrTokenIDSecureEnclave) or Android Keystore (KeyPairGenerator with AndroidKeyStore provider) are documented in the ten reviewed screens. If the session state layer uses expo-secure-store or react-native-keychain, these libraries automatically use the Secure Enclave/Keystore on supported devices (inferred from expo-secure-store and react-native-keychain library behavior — verify against the session state layer implementation in #/state/session).

[Not documented — WHO: iOS lead; WHAT: Does #/state/session use expo-secure-store, react-native-keychain, or a custom Keychain wrapper for storing accessJwt? If so, what kSecAttrAccessible access class is used (e.g., kSecAttrAccessibleWhenUnlockedThisDeviceOnly vs. kSecAttrAccessibleAlways)?; WHERE: Insert in Section 4.3 and Section 4.4 App Uninstall Data Cleanup table]

4.4 App Uninstall Data Cleanup

Storage Behavior on Uninstall Residual Data Risk Recommendation
Session state layer (underlying mechanism unknown) Not documented — if AsyncStorage: cleared automatically; if iOS Keychain: persists by default unless kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly is used If Keychain: accessJwt and stored account list may persist after uninstall — potential unauthorized access on device re-use or resale Investigate storage mechanism immediately (see DF-001); if Keychain, explicitly delete all items on logout and on first launch after reinstall
React component state Cleared automatically on app close None N/A
TanStack Query cache Cleared automatically on app close None N/A
ExpoBackgroundNotificationHandler (UserDefaults / SharedPreferences) iOS: behavior depends on whether shared app group is used; Android: cleared on uninstall (inferred from platform behavior — verify against native module source) iOS: playSoundChat preference may persist — low risk (non-sensitive) Low priority; document behavior for completeness
Platform Storage Backup Behavior User Consent Risk
iOS Session state layer (if AsyncStorage) Included in iCloud backup by default User controls iCloud backup in iOS Settings accessJwt and account list may be backed up to iCloud and restored to a different device — session token replay risk
iOS Session state layer (if Keychain) Excluded from iCloud backup by default (unless kSecAttrAccessible allows it) N/A Low if Keychain is used correctly
iOS ExpoBackgroundNotificationHandler (UserDefaults) Included in iCloud backup unless excluded via NSURLIsExcludedFromBackupKey User controls iCloud backup Low — playSoundChat boolean is non-sensitive
Android Session state layer (if AsyncStorage) Included in Google Drive auto-backup (Android 6+) User controls Google account backup accessJwt may be backed up to Google Drive — session token replay risk
Android Session state layer (if Keystore) Not backed up N/A Low
Android ExpoBackgroundNotificationHandler (SharedPreferences) May be included in Google Drive auto-backup depending on backup rules configuration User controls Google account backup Low — non-sensitive preference

⚠️ Backup exposure risk: If accessJwt is stored in AsyncStorage, it is included in both iOS iCloud backups and Android Google Drive auto-backups by default. Restoring a device backup to a new device would restore the session token, potentially allowing unauthorized access to the account from the new device. This is a GDPR Article 32 concern (appropriate technical measures) and a MASVS-STOR-2 concern.


Device Permission Data Flows

PERM-001: Push Notification Permission

Field Value
Permission Push Notifications
iOS API expo-notifications / requestNotificationsPermission (from #/lib/notifications/notifications) — UNUserNotificationCenter.requestAuthorization
Android API POST_NOTIFICATIONS (Android 13+); implicit on earlier versions
Data Collected Device push notification token (APNs token on iOS, FCM registration token on Android); notification delivery metadata (notification ID, timestamp, payload)
GDPR Classification Personal Data — device identifier linked to user account
Legal Basis Consent — permission is requested post-login; user can deny
Purpose Delivering push notifications for new direct messages and other social activity
Data Minimization The permission request is triggered post-login (requestNotificationsPermission('Login')) rather than on app launch — this is a privacy-positive practice (contextually appropriate timing). The minimum permission level (notification delivery) is requested.
When Requested After successful login — called in the success path of onPressNext in the Login Form
Denied Behavior Handled within useRequestNotificationsPermission hook — not documented at the Login Form screen level; Login Form treats this as fire-and-forget
Permanently Denied Behavior Not documented at the Login Form screen level — handled within useRequestNotificationsPermission
Data Destination APNs (Apple Push Notification service) on iOS; FCM (Firebase Cloud Messaging) on Android; device token likely transmitted to the Bluesky/ATProto backend for notification routing
Screen Source Login Form (/login/login-form) — Section 8 (post-login side effect)

Source Evidence: Login Form (/login/login-form) — Section 5 (Post-login side effects) and Section 8 (Business Rules)

[Not documented — WHO: backend lead; WHAT: Is the APNs/FCM device token transmitted to the Bluesky PDS or a separate notification service? What data is included in the notification payload (e.g., does it include message content or only a notification ID)?; WHERE: Insert in PERM-001 Data Destination field and in Third-Party Data Sharing table]

[Not documented — WHO: iOS lead; WHAT: What happens when the user denies or permanently denies the notification permission? Is there a fallback UI or retry mechanism in useRequestNotificationsPermission?; WHERE: Insert in PERM-001 Denied Behavior and Permanently Denied Behavior fields]

Special assessment — Notification payload content: The Chat List screen documentation references decrementBadgeCount(convo.unreadCount) and pushToConversation route param, confirming that push notifications carry at minimum a conversation ID. Whether notification payloads include message content (which would constitute direct message content transmitted through APNs/FCM infrastructure) is not documented and requires investigation.

[Not documented — WHO: backend lead and iOS lead; WHAT: Do push notification payloads for chat messages include message text content, or only a conversation ID / notification type? If message content is included, this constitutes personal data (DC-009) transmitted through Apple/Google infrastructure and requires disclosure in the App Store Privacy Nutrition Label and Google Play Data Safety section.; WHERE: Insert in PERM-001 and in Third-Party Data Sharing table — APNs/FCM row]


Data Category Inventory

ID Data Category Classification Examples Found Collection Screens Storage Location On-Device Storage Legal Basis
DC-001 Username / Handle Personal Data ATProto handle (e.g., alice.bsky.social), bare username (e.g., alice) Login Form, Choose Account Form Server (ATProto PDS) + On-device (session store) Session state layer (mechanism not documented — requires investigation) Performance of Contract (GDPR Art. 6(1)(b))
DC-002 Email Address Personal Data User's registered email address Forgot Password Form, Set New Password Form (indirectly — reset code sent to email) Server (ATProto PDS) In-memory only (React state, discarded on unmount) — not persisted on-device by documented screens Performance of Contract (GDPR Art. 6(1)(b))
DC-003 Authentication Token (accessJwt) Sensitive Data JSON Web Token for session authentication Choose Account Form (reads token presence), Login Form (session created) On-device (session state layer — mechanism not documented) + Server (ATProto PDS validates) Session state layer (mechanism not documented — Critical: requires investigation) Performance of Contract (GDPR Art. 6(1)(b))
DC-004 Password Sensitive Data User's account password (current or new) Login Form, Set New Password Form Never persisted — in-memory only (React ref / React state) In-memory only — secureTextEntry={true} on all password fields Performance of Contract (GDPR Art. 6(1)(b))
DC-005 Two-Factor Authentication Code Sensitive Data 6-character OTP code sent to user's email Login Form (2FA input) Never persisted — in-memory only (React state authFactorToken) In-memory only Performance of Contract (GDPR Art. 6(1)(b))
DC-006 Password Reset Token Sensitive Data XXXXX-XXXXX format one-time reset code Set New Password Form Never persisted — in-memory only (React state resetCode) In-memory only Performance of Contract (GDPR Art. 6(1)(b))
DC-007 User Profile Data Personal Data Display name, handle, DID, profile avatar, associated.chat.allowIncoming Conversation, Inbox, Chat List, Settings Server (ATProto PDS) + TanStack Query in-memory cache In-memory only (TanStack Query cache, cleared on app close) Performance of Contract (GDPR Art. 6(1)(b))
DC-008 Decentralized Identifier (DID) Personal Data did:plc:abc123 — unique persistent account identifier All messaging screens, Choose Account Form, Settings Server (ATProto PDS) + On-device (session state layer) Session state layer (mechanism not documented) Performance of Contract (GDPR Art. 6(1)(b))
DC-009 Direct Message Content Sensitive Data Text content of DM messages, embedded media references, reactions Conversation, Inbox, Chat List Server (ATProto Chat service) + In-memory (ConvoProvider / TanStack Query cache) In-memory only during session (ConvoProvider) — on-device persistence between sessions not documented Performance of Contract (GDPR Art. 6(1)(b))
DC-010 Conversation Metadata Personal Data Conversation ID, participant DIDs, unread count, last message timestamp, muted status, conversation status (accepted/request) Conversation, Inbox, Chat List Server (ATProto Chat service) + In-memory (TanStack Query cache) In-memory only (TanStack Query cache) Performance of Contract (GDPR Art. 6(1)(b))
DC-011 Messaging Preferences Personal Data allowIncoming setting (all/following/none) — controls who can message the user Settings Server (ATProto PDS — actor declaration) + In-memory (React Query cache) In-memory only (React Query cache) Performance of Contract (GDPR Art. 6(1)(b))
DC-012 Notification Preferences Personal Data playSoundChat boolean — notification sound preference Settings On-device only (native module — UserDefaults iOS / SharedPreferences Android) ExpoBackgroundNotificationHandler native storage Legitimate Interest (GDPR Art. 6(1)(f)) — device-local preference with no server transmission documented
DC-013 Analytics / Behavioral Data Personal Data Login events (account:loggedIn, withPassword: false/true), password reset outcomes (signin:passwordResetSuccess/Failure), chat open events (chat:open, logContext: 'ChatsList') Login Form, Set New Password Form, Choose Account Form, Chat List Analytics service (destination not documented) Not documented — depends on analytics SDK Consent or Legitimate Interest — legal basis not documented; requires investigation
DC-014 Device Push Notification Token Personal Data APNs token (iOS), FCM registration token (Android) Login Form (permission requested post-login) Server (notification routing service — destination not documented) Not documented — depends on notification SDK Consent (GDPR Art. 6(1)(a)) — permission requested at runtime

Records of Processing Activities (ROPA)

PA-001: User Authentication (Login)

Field Value
Purpose Authenticate a user's identity and establish an authenticated session on the ATProto PDS
Legal Basis Performance of Contract (GDPR Art. 6(1)(b))
Data Categories DC-001 (Username/Handle), DC-002 (Email Address — used as identifier), DC-003 (Authentication Token), DC-004 (Password), DC-005 (2FA Code)
Data Subjects Registered users of the Bluesky/ATProto application
Recipients ATProto PDS (user's chosen hosting provider, e.g., bsky.social or self-hosted)
On-Device Processing Handle expansion (createFullHandle) — bare username is expanded to full handle before transmission; password and identifier are lowercased/trimmed; 2FA token is trimmed
On-Device Storage Authentication token (accessJwt) stored in session state layer — storage mechanism not documented (requires investigation); password and 2FA code held in-memory only and discarded after submission
Transfers Cross-border transfer possible — user may be hosted on a PDS in any jurisdiction; serviceUrl is user-configurable. Transfer mechanism (SCCs, adequacy) not documented for third-party PDS instances.
Retention — Server Not documented — WHO: backend lead; WHAT: What is the server-side retention period for session tokens and login audit logs?; WHERE: Insert in this ROPA entry
Retention — On-Device Authentication token: until logout or app uninstall (if AsyncStorage) / potentially indefinitely post-uninstall (if iOS Keychain without explicit deletion) — requires investigation
Security Measures secureTextEntry={true} on password field; autoCorrect={false}, autoCapitalize="none" on password field; credentials transmitted via ATProto SDK (TLS assumed — verify); cleanError() applied to error messages before display
Source Screen(s) Login Form (/login/login-form)

Source Evidence: Login Form (/login/login-form) — Sections 4, 5, 12, 14


PA-002: Password Reset

Field Value
Purpose Allow a user who has forgotten their password to reset it via an email-based verification flow
Legal Basis Performance of Contract (GDPR Art. 6(1)(b))
Data Categories DC-002 (Email Address), DC-006 (Password Reset Token), DC-004 (New Password)
Data Subjects Registered users initiating a password reset
Recipients ATProto PDS (user's chosen hosting provider)
On-Device Processing Email format validation (EmailValidator.validate); reset code format validation and normalization (checkAndFormatResetCode); password presence check
On-Device Storage Email address, reset token, and new password held in React component state (in-memory only); discarded on component unmount. No persistence to any storage mechanism.
Transfers Cross-border transfer possible — same as PA-001; serviceUrl is user-configurable
Retention — Server Not documented — WHO: backend lead; WHAT: How long are password reset tokens valid, and are they invalidated immediately after use?; WHERE: Insert in this ROPA entry
Retention — On-Device In-memory only — no on-device retention
Security Measures secureTextEntry={true} on new password field; autoComplete="new-password" hint; cleanError() applied to error messages; Agent instantiated with null session (no existing token used or leaked); reset token transmitted directly to PDS
Source Screen(s) Forgot Password Form (/login/forgot-password-form), Set New Password Form (/login/set-new-password-form), Password Updated Form (/login/password-updated-form)

Source Evidence: Forgot Password Form — Sections 5, 7, 14; Set New Password Form — Sections 5, 7, 14


PA-003: Session Resumption (Multi-Account)

Field Value
Purpose Allow a user to resume a previously authenticated session without re-entering credentials, using a stored authentication token
Legal Basis Performance of Contract (GDPR Art. 6(1)(b))
Data Categories DC-001 (Username/Handle), DC-003 (Authentication Token), DC-008 (DID)
Data Subjects Registered users with previously stored sessions
Recipients ATProto PDS (user's chosen hosting provider)
On-Device Processing Presence check on accessJwt; DID comparison with currentAccount.did; resumeSession call to exchange stored token for fresh session
On-Device Storage Authentication token and account list read from session state layer (storage mechanism not documented — requires investigation)
Transfers Cross-border transfer possible — same as PA-001
Retention — Server Not documented
Retention — On-Device Until logout or app uninstall (mechanism-dependent — see Section 4.4)
Security Measures Race condition guard (pendingDid check prevents concurrent resumption); error logging without credential exposure; graceful fallback to password form on failure
Source Screen(s) Choose Account Form (/login/choose-account-form)

Source Evidence: Choose Account Form (/login/choose-account-form) — Sections 4, 5, 8, 14


PA-004: Direct Messaging

Field Value
Purpose Enable authenticated users to send and receive one-on-one direct messages
Legal Basis Performance of Contract (GDPR Art. 6(1)(b))
Data Categories DC-007 (User Profile Data), DC-008 (DID), DC-009 (Direct Message Content), DC-010 (Conversation Metadata)
Data Subjects Authenticated users and their message recipients
Recipients ATProto Chat service (Bluesky-operated or PDS-hosted); message recipients
On-Device Processing Moderation decisions computed client-side (moderateProfile); display name sanitization (sanitizeDisplayName); last message preview text derivation; unread count tracking
On-Device Storage Message content and conversation metadata held in-memory via ConvoProvider and TanStack Query cache; cleared on app close. On-device persistence between sessions not documented — requires investigation.
Transfers Cross-border transfer possible — ATProto is a federated protocol; message recipients may be on PDS instances in any jurisdiction
Retention — Server Not documented — WHO: backend lead; WHAT: What is the server-side retention period for direct message content and conversation metadata?; WHERE: Insert in this ROPA entry
Retention — On-Device In-memory only during session (documented behavior — verify with data flow analysis); no documented on-device persistence between sessions
Security Measures Email verification required before messaging; age assurance gate; moderation decisions applied client-side; ConvoProvider manages connection lifecycle; no direct auth token handling in screen-level code
Source Screen(s) Conversation (/messages/conversation), Inbox (/messages/inbox), Chat List (/messages/chat-list)

Source Evidence: Conversation — Sections 4, 5, 14; Inbox — Sections 4, 5, 14; Chat List — Sections 4, 5, 14


PA-005: Chat Request Management

Field Value
Purpose Allow users to review, accept, reject, block, and report incoming chat requests from other users
Legal Basis Performance of Contract (GDPR Art. 6(1)(b)); Legitimate Interest for blocking/reporting (GDPR Art. 6(1)(f))
Data Categories DC-007 (User Profile Data of requester), DC-008 (DID), DC-009 (Message Content — preview), DC-010 (Conversation Metadata)
Data Subjects Authenticated users (recipients) and chat requesters (third parties whose profile data is displayed)
Recipients ATProto Chat service; ATProto moderation service (for block and report operations)
On-Device Processing Moderation decisions (moderateProfile); display name sanitization; deleted account detection (handle === 'missing.invalid'); optimistic UI filtering (useLeftConvos)
On-Device Storage In-memory only (TanStack Query cache)
Transfers Cross-border transfer possible — same as PA-004
Retention — Server Not documented
Retention — On-Device In-memory only during session
Security Measures Email verification gate for accepting chats; age assurance gate; moderation integration; input sanitization on display names
Source Screen(s) Inbox (/messages/inbox)

Source Evidence: Inbox (/messages/inbox) — Sections 4, 5, 8, 14


PA-006: Messaging Preferences Management

Field Value
Purpose Allow users to configure who can send them new messages and whether notification sounds play for chat
Legal Basis Performance of Contract (GDPR Art. 6(1)(b)) for allowIncoming; Legitimate Interest (GDPR Art. 6(1)(f)) for playSoundChat
Data Categories DC-007 (User Profile Data — associated.chat.allowIncoming), DC-011 (Messaging Preferences), DC-012 (Notification Preferences)
Data Subjects Authenticated users
Recipients ATProto PDS (for allowIncoming actor declaration update); no server transmission for playSoundChat (device-local only)
On-Device Processing allowIncoming value derived from profile query; playSoundChat read from and written to native module storage
On-Device Storage playSoundChat stored in ExpoBackgroundNotificationHandler native storage (UserDefaults iOS / SharedPreferences Android); allowIncoming in TanStack Query cache (in-memory)
Transfers allowIncoming update transmitted to ATProto PDS — cross-border transfer possible
Retention — Server allowIncoming retained as part of user's actor declaration on the PDS — no documented deletion mechanism
Retention — On-Device playSoundChat: persists until app uninstall (Android) or potentially beyond (iOS UserDefaults)
Security Measures Authenticated session required; closed set of valid values (no free-text input); Toast error feedback on mutation failure
Source Screen(s) Settings (/messages/settings)

Source Evidence: Settings (/messages/settings) — Sections 4, 5, 8, 14


PA-007: Analytics Event Collection

Field Value
Purpose Collect behavioral metrics for product analytics (login success rates, password reset outcomes, chat engagement)
Legal Basis Not documented — WHO: DPO; WHAT: What is the legal basis for analytics event collection? If Consent, is a consent mechanism implemented? If Legitimate Interest, has a Legitimate Interest Assessment (LIA) been conducted?; WHERE: Insert in this ROPA entry
Data Categories DC-013 (Analytics/Behavioral Data), DC-001 (Username/Handle — included in logContext), DC-008 (DID — potentially included in analytics payload)
Data Subjects Authenticated and unauthenticated users
Recipients Analytics service (destination not documented — requires investigation)
On-Device Processing Event construction (event name + properties object)
On-Device Storage Not documented — depends on analytics SDK (may queue events locally before transmission)
Transfers Not documented — analytics service location unknown
Retention — Server Not documented
Retention — On-Device Not documented
Security Measures Not documented
Source Screen(s) Login Form, Set New Password Form, Choose Account Form, Chat List

Source Evidence: Login Form — Section 5 (post-login side effects); Set New Password Form — Section 10; Choose Account Form — Section 10; Chat List — Section 10


PA-008: Push Notification Delivery

Field Value
Purpose Deliver push notifications to users for new direct messages and other social activity
Legal Basis Consent (GDPR Art. 6(1)(a)) — OS-level permission requested post-login
Data Categories DC-014 (Device Push Notification Token), DC-010 (Conversation Metadata — notification routing)
Data Subjects Authenticated users who have granted notification permission
Recipients Apple Push Notification service (APNs) on iOS; Firebase Cloud Messaging (FCM) on Android; Bluesky notification routing service (backend)
On-Device Processing decrementBadgeCount called on conversation open; pushToConversation param processed on Chat List screen
On-Device Storage Not documented — device token storage mechanism not documented
Transfers APNs (Apple infrastructure, US-based); FCM (Google infrastructure, US-based) — cross-border transfer from EU users
Retention — Server Not documented
Retention — On-Device Not documented
Security Measures Permission requested post-login (contextually appropriate); fire-and-forget (Login Form does not handle denial)
Source Screen(s) Login Form (/login/login-form), Chat List (/messages/chat-list)

Source Evidence: Login Form — Section 5 (post-login side effects), Section 8; Chat List — Section 10, Section 13


Screen-by-Screen Data Flows

8.1 Form Container — /login/form-container

Data Profile:

Aspect Assessment
Data Collection No
Data Display No
Data Modification No
Data Deletion No
External Sharing No
Sensitive Data No
On-Device Storage None
Device Permissions None
Privacy Risk Level Low

Data Collection Points: None — this is a pure layout wrapper component.

Data Flows:

Privacy Concerns: None identified. This component is a pure layout primitive with no data handling.


8.2 Login Form — /login/login-form

Data Profile:

Aspect Assessment
Data Collection Yes — username/handle, password, 2FA code
Data Display Yes — pre-filled username handle (initialHandle), error messages
Data Modification No
Data Deletion No
External Sharing Yes — credentials transmitted to ATProto PDS; notification permission requested post-login
Sensitive Data Yes — password (DC-004), 2FA code (DC-005), authentication token created (DC-003)
On-Device Storage Session state layer (mechanism not documented) — authentication token stored post-login
Device Permissions Push Notifications (PERM-001) — requested post-login
Privacy Risk Level High

Data Collection Points:

Field Data Category Required Validation Legal Basis On-Device Storage Notes
Username / email input DC-001, DC-002 Yes Non-empty after trim; lowercased Performance of Contract In-memory only (React ref identifierValueRef) — never persisted by this component autoComplete="username" enables OS credential autofill
Password input DC-004 Yes Non-empty Performance of Contract In-memory only (React ref passwordValueRef) — never persisted by this component secureTextEntry={true}, autoCorrect={false}, autoCapitalize="none"
2FA code input DC-005 Conditional (server-driven) Server-side only Performance of Contract In-memory only (React state authFactorToken) autoComplete="one-time-code" enables SMS/email OTP autofill; displayed uppercase but submitted as-typed

Data Flows:

Privacy Concerns:


DF-001: Authentication Token Storage Mechanism Unknown

Field Value
Risk Level Critical
Data Affected DC-003 (Authentication Token), DC-001 (Username/Handle), DC-008 (DID)
Regulation GDPR Art. 32 (security of processing), MASVS-STOR-1, MASVS-STOR-2
Description The Login Form creates an authenticated session via useSessionApi().login(). The resulting accessJwt is stored by the session state layer (#/state/session). The underlying storage mechanism (AsyncStorage, MMKV, Keychain, or SecureStore) is not documented at the screen level. If AsyncStorage is used, the token is stored in plain-text SQLite on the device, included in iOS iCloud and Android Google Drive backups, and accessible on rooted/jailbroken devices.
Evidence Login Form Section 12: "No client-side persistence (AsyncStorage, MMKV, SecureStore) is used directly in this component." — This confirms the component itself does not persist, but the session layer does (confirmed by Choose Account Form reading account.accessJwt).
Recommendation Investigate #/state/session storage implementation immediately. If AsyncStorage is used, migrate to expo-secure-store (wraps iOS Keychain / Android Keystore). See MASVS-STOR-1. Ensure kSecAttrAccessibleWhenUnlockedThisDeviceOnly is used on iOS to prevent Keychain persistence post-uninstall.

DF-002: TODO Comment — Double Login

Field Value
Risk Level Medium
Data Affected DC-003 (Authentication Token), DC-004 (Password)
Regulation GDPR Art. 5(1)(c) (data minimization), GDPR Art. 32
Description The Login Form source contains a // TODO remove double login comment on the login() call. If the login function is called twice under certain conditions, credentials may be transmitted to the PDS twice, potentially creating duplicate session tokens or audit log entries.
Evidence Login Form Section 17: "A comment in the code explicitly marks the login() call with // TODO remove double login."
Recommendation Investigate and resolve the double-login issue. Ensure the login() function is called exactly once per user submission. Credit: development team has documented awareness of this issue.

DF-003: 2FA Token Case Handling Gap

Field Value
Risk Level Low
Data Affected DC-005 (2FA Code)
Regulation GDPR Art. 5(1)(f) (integrity and confidentiality)
Description The 2FA input displays text in uppercase via CSS textTransform: 'uppercase' (display-only), but the value submitted is whatever the user typed. If the server requires uppercase tokens, the submitted value may fail silently or be rejected, causing user confusion. The login() implementation should uppercase the value — this is not done in LoginForm.
Evidence Login Form Section 17: "authFactorToken case handling: The 2FA input displays in uppercase via textTransform: 'uppercase' (CSS display only), but the value submitted is whatever the user typed."
Recommendation Apply .toUpperCase() to authFactorToken.trim() before submission, or verify that the ATProto server accepts case-insensitive 2FA tokens. Credit: development team has documented awareness.

DF-004: Notification Permission — Fire-and-Forget

Field Value
Risk Level Medium
Data Affected DC-014 (Device Push Notification Token)
Regulation GDPR Art. 6(1)(a) (consent), GDPR Art. 13 (transparency)
Description requestNotificationsPermission('Login') is called post-login as a fire-and-forget operation. The Login Form does not handle the permission result. There is no documented user-facing explanation of why notifications are needed before the permission dialog appears, which may not satisfy GDPR transparency requirements for consent.
Evidence Login Form Section 10: "The handling of granted/denied/permanently-denied states is implemented within useRequestNotificationsPermission hook, not in LoginForm itself. LoginForm does not handle the permission result — it is fire-and-forget."
Recommendation Ensure useRequestNotificationsPermission presents a pre-permission rationale screen before the OS dialog (iOS best practice). Document the denied/permanently-denied handling. Verify that the permission request meets GDPR transparency requirements (Art. 13).

8.3 Password Updated Form — /login/password-updated-form

Data Profile:

Aspect Assessment
Data Collection No
Data Display No — static success message only
Data Modification No
Data Deletion No
External Sharing No
Sensitive Data No
On-Device Storage None
Device Permissions None
Privacy Risk Level Low

Data Collection Points: None — this is a static confirmation screen.

Data Flows:

Privacy Concerns: None identified. This screen is a pure confirmation view with no data handling.


8.4 Set New Password Form — /login/set-new-password-form

Data Profile:

Aspect Assessment
Data Collection Yes — password reset token, new password
Data Display No — no personal data displayed
Data Modification Yes — user's password is changed on the PDS
Data Deletion No
External Sharing Yes — reset token and new password transmitted to ATProto PDS
Sensitive Data Yes — DC-004 (new password), DC-006 (reset token)
On-Device Storage None — all data in-memory only
Device Permissions None
Privacy Risk Level Medium

Data Collection Points:

Field Data Category Required Validation Legal Basis On-Device Storage Notes
Reset code input DC-006 Yes checkAndFormatResetCode — must match XXXXX-XXXXX pattern Performance of Contract In-memory only (React state resetCode) Auto-formatted on blur; autoFocus={true}
New password input DC-004 Yes Non-empty only (no complexity rules — see DF-005) Performance of Contract In-memory only (React state password) secureTextEntry={true}, clearButtonMode="while-editing", autoComplete="new-password", passwordRules="minlength: 8;"

Data Flows:

Privacy Concerns:


DF-005: Weak Password Strength Validation

Field Value
Risk Level Medium
Data Affected DC-004 (Password)
Regulation GDPR Art. 32 (appropriate technical measures), GDPR Art. 5(1)(f) (integrity and confidentiality)
Description The Set New Password Form only validates that the password field is non-empty. No minimum length, complexity rules, or common-password checks are enforced client-side. The passwordRules="minlength: 8;" attribute is a hint to iOS password managers only and is not enforced by the app's validation logic. A user could set a single-character password that would only be rejected if the PDS enforces a policy.
Evidence Set New Password Form Section 7: "There is a // TODO Better password strength check comment in the code — the current password validation only checks for non-empty." Section 17: "No minimum length enforcement, complexity rules, or common-password checks are implemented client-side."
Recommendation Implement client-side password strength validation: minimum 8 characters, at least one uppercase letter, one number, and one special character. Consider integrating a common-password blocklist. Credit: development team has documented awareness via // TODO.

DF-006: isProcessing Not Reset on Success — Potential Form Lock

Field Value
Risk Level Low
Data Affected DC-004 (Password), DC-006 (Reset Token)
Regulation GDPR Art. 5(1)(e) (storage limitation — user should be able to correct errors)
Description isProcessing is intentionally left as true after a successful API call, relying on the parent to navigate away. If onPasswordSet() does not navigate away (e.g., due to a parent bug), the form is permanently locked with no way for the user to retry.
Evidence Set New Password Form Section 17: "If onPasswordSet() does not navigate away (e.g., due to a bug in the parent), the form will be permanently locked with no way for the user to retry."
Recommendation Add a timeout-based reset of isProcessing as a safety net, or verify that onPasswordSet() always navigates away. Credit: development team has documented awareness.

DF-007: Analytics Events — Destination and Legal Basis Undocumented

Field Value
Risk Level High
Data Affected DC-013 (Analytics/Behavioral Data)
Regulation GDPR Art. 6 (lawfulness), GDPR Art. 13 (transparency), GDPR Art. 30 (ROPA)
Description ax.metric('signin:passwordResetSuccess', {}) and ax.metric('signin:passwordResetFailure', {}) are fired from this screen. The analytics abstraction (useAnalytics from #/analytics) destination service, data transmitted, retention period, and legal basis are not documented in the screen documentation. This applies to all analytics events across the reviewed screens.
Evidence Set New Password Form Section 13: "ax.metric('signin:passwordResetSuccess', {}) — on successful password reset. ax.metric('signin:passwordResetFailure', {}) — on client-side validation failure or API error."
Recommendation Document the analytics service used (PostHog, Mixpanel, Amplitude, etc.), the data transmitted with each event (including whether user DID or handle is included), the retention period, and the legal basis. If Consent is the legal basis, implement a consent mechanism. Add the analytics service to the Third-Party Data Sharing table and the App Store Privacy Nutrition Label.

8.5 Forgot Password Form — /login/forgot-password-form

Data Profile:

Aspect Assessment
Data Collection Yes — email address
Data Display No — no personal data displayed
Data Modification No — initiates server-side password reset request
Data Deletion No
External Sharing Yes — email address transmitted to ATProto PDS
Sensitive Data No — email address is personal data but not special category
On-Device Storage None — email address in-memory only
Device Permissions None
Privacy Risk Level Low

Data Collection Points:

Field Data Category Required Validation Legal Basis On-Device Storage Notes
Email address input DC-002 Yes EmailValidator.validate(email) — RFC-compliant format check Performance of Contract In-memory only (React state email) — discarded on unmount autoFocus={true}, autoCapitalize="none", autoCorrect={false}, autoComplete="email"

Data Flows:

Privacy Concerns:


DF-008: Email Address Transmitted to User-Configurable PDS

Field Value
Risk Level Medium
Data Affected DC-002 (Email Address)
Regulation GDPR Art. 46 (transfers to third countries), GDPR Art. 13 (transparency)
Description The email address is transmitted to the serviceUrl — a user-configurable ATProto PDS URL. Users may be hosted on self-hosted or third-party PDS instances in any jurisdiction. The application does not document what transfer mechanism (SCCs, adequacy decision) applies when the PDS is outside the EEA.
Evidence Forgot Password Form Section 5: "Service target: Dynamically set to serviceUrl prop via new Agent(null, { service: serviceUrl })." Section 8: "Dynamic service targeting: Rather than using a global or session-level agent, a new Agent instance is created with new Agent(null, { service: serviceUrl }) at submission time."
Recommendation Document the data transfer mechanism for third-party PDS instances. Consider displaying a notice to users when their PDS is not operated by Bluesky, informing them that their data will be processed by a third-party operator.

8.6 Choose Account Form — /login/choose-account-form

Data Profile:

Aspect Assessment
Data Collection No — reads existing stored accounts
Data Display Yes — stored account list (handles, display names, avatars)
Data Modification No
Data Deletion No
External Sharing Yes — resumeSession transmits stored token to ATProto PDS
Sensitive Data Yes — DC-003 (Authentication Token) read from session store
On-Device Storage Session state layer (reads accessJwt and account list — mechanism not documented)
Device Permissions None
Privacy Risk Level High

Data Collection Points: None — this screen reads existing stored data; it does not collect new data from the user.

Data Flows:

Privacy Concerns:


DF-009: Multiple Stored Accounts Displayed — Privacy Exposure on Shared Devices

Field Value
Risk Level Medium
Data Affected DC-001 (Username/Handle), DC-007 (User Profile Data), DC-008 (DID)
Regulation GDPR Art. 5(1)(f) (integrity and confidentiality), GDPR Art. 25 (privacy by design)
Description The Choose Account Form displays all stored accounts (handles, display names, potentially avatars) to anyone who opens the login screen. On a shared or lost device, this exposes the list of accounts associated with the device to unauthorized viewers.
Evidence Choose Account Form Section 3: "AccountList — The primary interactive element. Renders the list of stored accounts." Section 14: "The component reads account.accessJwt from the session store to determine whether a stored session token exists."
Recommendation Consider requiring device authentication (PIN, biometric) before displaying the account list, or masking handles partially (e.g., a***e.bsky.social). Assess whether this is acceptable under the app's threat model.

DF-010: Session Resumption — Race Condition in Session API

Field Value
Risk Level Low
Data Affected DC-003 (Authentication Token)
Regulation GDPR Art. 32 (security of processing)
Description The code contains an inline comment: "The session API isn't resilient to race conditions so let's just ignore this." The pendingDid guard is a workaround, not a fix. Concurrent resumeSession calls could cause session state corruption.
Evidence Choose Account Form Section 17: "Race condition comment: The code contains an inline comment: 'The session API isn't resilient to race conditions so let's just ignore this.'"
Recommendation Harden the session API to be resilient to concurrent calls, or implement a proper mutex/lock mechanism. Credit: development team has documented awareness.

8.7 Conversation — /messages/conversation

Data Profile:

Aspect Assessment
Data Collection Yes — message content composed and sent (within MessagesList component)
Data Display Yes — message history, recipient profile, moderation state
Data Modification Yes — messages can be sent
Data Deletion No — deletion not documented at this screen level
External Sharing Yes — message content transmitted to ATProto Chat service; email verification state checked
Sensitive Data Yes — DC-009 (Direct Message Content), DC-007 (User Profile Data)
On-Device Storage In-memory only (ConvoProvider, TanStack Query cache) — on-device persistence between sessions not documented
Device Permissions None documented at this screen level (media attachments handled within MessagesList)
Privacy Risk Level High

Data Collection Points: Message composition is encapsulated within the MessagesList component — not directly visible at this screen level.

Data Flows:

Privacy Concerns:


DF-011: ConvoProvider Transport Mechanism Not Documented

Field Value
Risk Level High
Data Affected DC-009 (Direct Message Content), DC-010 (Conversation Metadata)
Regulation GDPR Art. 32 (security of processing — encryption in transit)
Description ConvoProvider manages the full lifecycle of a DM conversation including connection, message history, and real-time updates. The specific transport mechanism (WebSocket, polling, Bluesky firehose) is described as "encapsulated in ConvoProvider" and is not documented. It is not confirmed whether message content is transmitted over TLS, whether end-to-end encryption is used, or whether message content is stored on-device between sessions.
Evidence Conversation Section 11: "Real-time message delivery is managed entirely by ConvoProvider. The specific transport mechanism (WebSocket, polling, Bluesky firehose) is encapsulated in ConvoProvider."
Recommendation Document the ConvoProvider transport mechanism. Confirm TLS is used for all message transmission. Assess whether end-to-end encryption is implemented or planned. Determine whether message content is cached on-device between sessions (if so, assess storage mechanism against MASVS-STOR-1).

DF-012: Email Verification — Client-Side Only Gate

Field Value
Risk Level Medium
Data Affected DC-009 (Direct Message Content)
Regulation GDPR Art. 32 (security of processing)
Description Email verification is enforced client-side via needsEmailVerification check. The documentation notes: "the enforcement is purely UI-level — a determined user could potentially bypass the dialog by manipulating navigation state. Server-side enforcement in the API is the authoritative gate."
Evidence Conversation Section 14: "The screen enforces email verification before allowing any messaging interaction. This is a server-side requirement surfaced client-side. However, the enforcement is purely UI-level."
Recommendation Confirm that server-side enforcement of email verification is implemented in the ATProto Chat API. Document the server-side enforcement mechanism. The client-side gate is acceptable as a UX layer but must not be the sole enforcement mechanism.

DF-013: HACKFIX Load-Bearing setTimeout

Field Value
Risk Level Low
Data Affected DC-002 (Email Address — email verification state)
Regulation GDPR Art. 25 (privacy by design — reliability of privacy controls)
Description The email verification dialog open call is wrapped in a setTimeout with no delay as a HACKFIX. This is a fragile timing dependency — if the shell listener's execution time changes, the email verification gate could break, potentially allowing unverified users to access messaging.
Evidence Conversation Section 17: "The comment in InnerReady explicitly marks a setTimeout with no delay as a HACKFIX."
Recommendation Refactor the email verification gate to use a reliable mechanism (e.g., a navigation guard or a proper state machine) rather than a timing-dependent workaround. Credit: development team has documented awareness.

8.8 Inbox — /messages/inbox

Data Profile:

Aspect Assessment
Data Collection No — reads existing conversation data
Data Display Yes — chat request list with sender profiles, message previews, timestamps
Data Modification Yes — accept, reject, block, delete, mark as read
Data Deletion Yes — leave/delete conversation
External Sharing Yes — block and report operations transmitted to ATProto; accept transmitted to ATProto Chat service
Sensitive Data Yes — DC-009 (Message Content preview), DC-007 (User Profile Data of requesters)
On-Device Storage In-memory only (TanStack Query cache)
Device Permissions None
Privacy Risk Level Medium

Data Collection Points: None — this screen reads and manages existing data.

Data Flows:

Privacy Concerns:


DF-014: Third-Party Profile Data Displayed Without Explicit Consent

Field Value
Risk Level Medium
Data Affected DC-007 (User Profile Data of chat requesters)
Regulation GDPR Art. 6 (lawfulness — third-party data), GDPR Art. 13 (transparency)
Description The Inbox displays profile data (display name, handle, avatar, KnownFollowers) of users who have sent chat requests. These are third parties who have not directly interacted with this screen. Their profile data is fetched and displayed as part of the chat request management flow.
Evidence Inbox Section 3: "Each ChatListItem renders: PreviewableUserAvatar — 52×52 avatar... Display name (Text) + ProfileBadges... @handle... KnownFollowers."
Recommendation This is standard social application behavior and is likely covered by the ATProto protocol's public profile data model. Ensure the privacy policy discloses that other users' public profile data is displayed in the messaging interface. Verify that KnownFollowers data (mutual followers) is sourced from public ATProto data and not from private contact lists.

DF-015: Deleted Account Handling — Potential Bug with ConvoMenu

Field Value
Risk Level Low
Data Affected DC-010 (Conversation Metadata)
Regulation GDPR Art. 17 (right to erasure — deleted account data handling)
Description For deleted account conversations, the onPress handler calls menuControl.open(), but ConvoMenu is not rendered (showMenu={false}). This is a potential bug that could cause unexpected behavior. Additionally, conversations with deleted accounts (handle === 'missing.invalid') are displayed with "Deleted Account" as the display name — the underlying DID may still be stored and transmitted.
Evidence Inbox Section 17: "showMenu={false} in Inbox: ChatListItem is rendered with showMenu={false} in RequestListItem, which means ConvoMenu is not rendered. However... the onPress handler for deleted accounts calls menuControl.open(), but since ConvoMenu is not rendered, this would open a menu that doesn't exist — potential bug."
Recommendation Fix the deleted account press handler bug. Assess whether conversations with deleted accounts should be automatically removed from the inbox or retained for user reference. Ensure deleted account DIDs are not transmitted to analytics or third-party services. Credit: development team has documented awareness.

8.9 Chat List — /messages/chat-list

Data Profile:

Aspect Assessment
Data Collection No — reads existing conversation data
Data Display Yes — accepted conversation list with participant profiles, message previews, timestamps
Data Modification Yes — mark as read, leave conversation, mute
Data Deletion Yes — leave/delete conversation
External Sharing Yes — mark as read, leave operations transmitted to ATProto; analytics event on chat open
Sensitive Data Yes — DC-009 (Message Content preview), DC-007 (User Profile Data)
On-Device Storage In-memory only (TanStack Query cache)
Device Permissions None directly — push notification token used for pushToConversation routing (handled at app shell level)
Privacy Risk Level Medium

Data Collection Points: None — this screen reads and manages existing data.

Data Flows:

Privacy Concerns:


DF-016: pushToConversation — Push Notification Payload Privacy

Field Value
Risk Level Medium
Data Affected DC-009 (Direct Message Content), DC-010 (Conversation Metadata), DC-014 (Device Push Notification Token)
Regulation GDPR Art. 32 (security of processing), GDPR Art. 13 (transparency)
Description The pushToConversation route param is injected by the push notification handler at the app shell level. The notification payload content (whether it includes message text or only a conversation ID) is not documented. If message content is included in the notification payload, it is transmitted through Apple/Google infrastructure (APNs/FCM) and may be stored in notification history on the device and in Apple/Google servers.
Evidence Chat List Section 4: "pushToConversation (`string
Recommendation Ensure push notification payloads contain only a conversation ID (not message content). Document the notification payload structure. Disclose APNs/FCM data transmission in the App Store Privacy Nutrition Label and Google Play Data Safety section.

DF-017: Inbox Fetch Error Silently Ignored

Field Value
Risk Level Low
Data Affected DC-010 (Conversation Metadata)
Regulation GDPR Art. 5(1)(d) (accuracy)
Description Errors from refetchInbox() during pull-to-refresh are caught and logged but not surfaced to the user. If the inbox fetch fails, the InboxPreview banner may disappear or show stale data without any user-facing indication.
Evidence Chat List Section 17: "Inbox Fetch Error Silently Ignored: Errors from refetchInbox() during pull-to-refresh are caught and logged but not surfaced to the user."
Recommendation Surface inbox fetch errors to the user with a non-blocking indicator (e.g., a subtle error state on the InboxPreview banner). Credit: development team has documented awareness.

8.10 Settings — /messages/settings

Data Profile:

Aspect Assessment
Data Collection No — modifies existing preferences
Data Display Yes — current allowIncoming setting, current playSoundChat setting
Data Modification Yes — allowIncoming actor declaration updated on PDS; playSoundChat written to native storage
Data Deletion No
External Sharing Yes — allowIncoming update transmitted to ATProto PDS
Sensitive Data No — preferences are not sensitive data
On-Device Storage ExpoBackgroundNotificationHandler native storage (UserDefaults / SharedPreferences) for playSoundChat
Device Permissions None
Privacy Risk Level Low

Data Collection Points: None — this screen modifies existing preferences via radio button selection.

Data Flows:

Privacy Concerns:


DF-018: allowIncoming Setting — Privacy Control Effectiveness

Field Value
Risk Level Low
Data Affected DC-011 (Messaging Preferences)
Regulation GDPR Art. 25 (privacy by design), GDPR Art. 17 (right to erasure — no documented deletion mechanism for actor declaration)
Description The allowIncoming setting is a privacy control that determines who can message the user. The Admonition tip informs users that "ongoing conversations are unaffected by this setting." There is no documented mechanism for deleting the actor declaration (only updating it). If a user deletes their account, it is unclear whether the actor declaration is deleted from the PDS.
Evidence Settings Section 3: "An Admonition component of type 'tip' below the radio group, informing the user that ongoing conversations are unaffected by this setting." Settings Section 17: "No optimistic rollback for actor declaration."
Recommendation Document the deletion behavior of the actor declaration when a user deletes their account. Ensure the right to erasure (GDPR Art. 17) is implemented for actor declarations.

DF-019: Native Module Error Handling Gap

Field Value
Risk Level Low
Data Affected DC-012 (Notification Preferences)
Regulation GDPR Art. 5(1)(d) (accuracy — preference may be inaccurate if native write fails silently)
Description BackgroundNotificationHandler.getAllPrefsAsync() and setBoolAsync() calls have no error handling. A native module failure would result in a silent error and potentially a stale or incorrect UI state — the user believes they have changed a preference that was not actually saved.
Evidence Settings Section 17: "Unhandled native module errors: BackgroundNotificationHandler.getAllPrefsAsync() and setBoolAsync() calls in BackgroundNotificationPreferencesProvider have no try/catch or .catch() handlers."
Recommendation Add try/catch error handling to native module calls. Show a Toast error if the preference write fails. Credit: development team has documented awareness.

DF-020: Non-Null Assertion on currentAccount

Field Value
Risk Level Low
Data Affected DC-008 (DID)
Regulation GDPR Art. 32 (security of processing — application stability)
Description currentAccount!.did uses a TypeScript non-null assertion. If the session state is ever null when this screen renders (e.g., during logout race conditions), this will throw a runtime error rather than failing gracefully.
Evidence Settings Section 17: "Non-null assertion on currentAccount: currentAccount!.did uses a TypeScript non-null assertion."
Recommendation Replace currentAccount!.did with a defensive check: if (!currentAccount) return null. Credit: development team has documented awareness.

Data Flow Inventory

ID Screen Data Category Direction Destination Storage Mechanism Risk Level Regulation Recommendation Phase
DF-001 Login Form DC-003 (Auth Token) On-Device Session state layer (mechanism unknown) Unknown — requires investigation Critical GDPR Art. 32, MASVS-STOR-1 Phase 1 (Immediate)
DF-007 Set New Password Form, Login Form, Choose Account Form, Chat List DC-013 (Analytics) Out — Third Party Analytics service (unknown) Unknown High GDPR Art. 6, Art. 13, Art. 30 Phase 1 (Immediate)
DF-011 Conversation DC-009 (DM Content), DC-010 (Metadata) Out — Server ATProto Chat service In-memory (ConvoProvider) High GDPR Art. 32 Phase 1 (Immediate)
DF-004 Login Form DC-014 (Push Token) Out — Third Party APNs / FCM / Notification service Unknown Medium GDPR Art. 6(1)(a), Art. 13 Phase 2 (Short-Term)
DF-008 Forgot Password Form DC-002 (Email) Out — Server User-configurable ATProto PDS In-memory only Medium GDPR Art. 46, Art. 13 Phase 2 (Short-Term)
DF-009 Choose Account Form DC-001 (Handle), DC-007 (Profile), DC-008 (DID) Display On-screen (account list) Session state layer Medium GDPR Art. 25, Art. 5(1)(f) Phase 2 (Short-Term)
DF-012 Conversation DC-009 (DM Content) Internal Email verification gate In-memory Medium GDPR Art. 32 Phase 2 (Short-Term)
DF-014 Inbox DC-007 (Third-party profiles) In — Server ATProto API In-memory (TanStack Query) Medium GDPR Art. 6, Art. 13 Phase 3 (Medium-Term)
DF-016 Chat List DC-009 (DM Content), DC-014 (Push Token) In — Push APNs / FCM Unknown Medium GDPR Art. 32, Art. 13 Phase 2 (Short-Term)
DF-002 Login Form DC-003 (Auth Token), DC-004 (Password) Out — Server ATProto PDS In-memory Medium GDPR Art. 5(1)(c), Art. 32 Phase 2 (Short-Term)
DF-005 Set New Password Form DC-004 (Password) Internal Validation In-memory Medium GDPR Art. 32, Art. 5(1)(f) Phase 2 (Short-Term)
DF-013 Conversation DC-002 (Email verification) Internal Email dialog In-memory Low GDPR Art. 25 Phase 3 (Medium-Term)
DF-015 Inbox DC-010 (Conversation Metadata) Internal ConvoMenu (bug) In-memory Low GDPR Art. 17 Phase 3 (Medium-Term)
DF-017 Chat List DC-010 (Conversation Metadata) In — Server TanStack Query cache In-memory Low GDPR Art. 5(1)(d) Phase 4 (Backlog)
DF-018 Settings DC-011 (Messaging Preferences) Out — Server ATProto PDS In-memory (React Query) Low GDPR Art. 25, Art. 17 Phase 3 (Medium-Term)
DF-019 Settings DC-012 (Notification Preferences) On-Device Native module storage UserDefaults / SharedPreferences Low GDPR Art. 5(1)(d) Phase 3 (Medium-Term)
DF-020 Settings DC-008 (DID) Internal Session state Session state layer Low GDPR Art. 32 Phase 3 (Medium-Term)
DF-003 Login Form DC-005 (2FA Code) Out — Server ATProto PDS In-memory Low GDPR Art. 5(1)(f) Phase 3 (Medium-Term)
DF-006 Set New Password Form DC-004 (Password), DC-006 (Reset Token) Internal Form state In-memory Low GDPR Art. 5(1)(e) Phase 4 (Backlog)
DF-010 Choose Account Form DC-003 (Auth Token) Internal Session API Session state layer Low GDPR Art. 32 Phase 3 (Medium-Term)

Third-Party Data Sharing

Service Integration Method Data Shared Purpose Legal Basis DPA Required Transfer Mechanism Risk Level
ATProto PDS (user's chosen hosting provider — e.g., bsky.social or self-hosted) @atproto/api SDK — XRPC over HTTPS DC-001 (Handle), DC-002 (Email), DC-003 (Auth Token), DC-004 (Password — during login/reset only), DC-005 (2FA Code), DC-006 (Reset Token), DC-007 (Profile), DC-008 (DID), DC-009 (DM Content), DC-010 (Conversation Metadata), DC-011 (Messaging Preferences) Core application functionality — authentication, messaging, profile management Performance of Contract (GDPR Art. 6(1)(b)) Yes — for third-party PDS operators; Bluesky-operated PDS covered by Bluesky's own privacy policy SCCs or adequacy decision required for non-EEA PDS instances — not documented High
Analytics service (#/analytics abstraction — ax.metric) Internal SDK abstraction — underlying service not documented DC-013 (Behavioral events: login, password reset, chat open) — whether DC-001/DC-008 are included in payloads is not documented Product analytics and usage measurement Not documented — requires investigation Yes — if personal data is transmitted Not documented High
Apple Push Notification service (APNs) iOS native — expo-notifications DC-014 (APNs device token); potentially DC-009/DC-010 if notification payload includes message content Push notification delivery Consent (GDPR Art. 6(1)(a)) Standard Contractual Clauses (Apple DPA) SCCs — Apple is a US-based processor Medium
Firebase Cloud Messaging (FCM) Android native — expo-notifications DC-014 (FCM registration token); potentially DC-009/DC-010 if notification payload includes message content Push notification delivery Consent (GDPR Art. 6(1)(a)) Standard Contractual Clauses (Google DPA) SCCs — Google is a US-based processor Medium
ATProto Moderation Service @atproto/api SDK DC-007 (Profile data of reported users), DC-009 (Reported message content), DC-010 (Conversation metadata) Content moderation and abuse reporting Legitimate Interest (GDPR Art. 6(1)(f)) Covered by Bluesky's data processing terms Same as ATProto PDS Medium
@bsky.app/expo-scroll-edge-effect Expo package (Bluesky-authored) No personal data — visual effects only Scroll edge visual effects N/A No N/A Low
react-remove-scroll-bar npm package (web only) No personal data — DOM manipulation only Web scroll bar suppression N/A No N/A Low
email-validator npm package No data transmitted — client-side validation only Email format validation N/A No N/A Low

[Not documented — WHO: engineering lead; WHAT: What analytics service does #/analytics (ax.metric) transmit data to? Is it PostHog, Mixpanel, Amplitude, Firebase Analytics, or a custom service? What data fields are included in each event payload (specifically, are user DID or handle included)?; WHERE: Insert in Third-Party Data Sharing table — Analytics service row, and in PA-007 ROPA entry]

[Not documented — WHO: engineering lead; WHAT: Are there any crash reporting SDKs (Sentry, Bugsnag, Crashlytics) integrated into the application? If so, do crash payloads include app state that might contain PII (e.g., the current user's handle, message content from ConvoProvider state)?; WHERE: Insert as a new row in Third-Party Data Sharing table, and add a new DF entry if PII is included in crash payloads]

⚠️ This workflow continues beyond the documented screens. The ConvoProvider component manages real-time message delivery and its integration with the ATProto Chat service is not covered in this document. Verify the complete data flow for message transmission (including transport protocol, encryption, and server-side storage) with the development team before treating this as a comprehensive process description.


Data Retention & Deletion

Data Category Retention — Server Retention — On-Device Storage Storage Mechanism Deletion Mechanism Right to Erasure Notes
DC-001 (Username/Handle) Not documented — WHO: backend lead; WHAT: What is the server-side retention period for user handles?; WHERE: Insert here Until logout or app uninstall (if AsyncStorage) / potentially indefinitely post-uninstall (if iOS Keychain) Session state layer (mechanism not documented) Logout clears session state (mechanism-dependent); app uninstall clears AsyncStorage/MMKV but not iOS Keychain by default Not documented — requires investigation Handle is a public ATProto identifier; deletion may require account deletion on PDS
DC-002 (Email Address) Not documented In-memory only — discarded on component unmount React state Automatic on unmount N/A — not persisted on-device Email is stored server-side on the PDS; right to erasure requires server-side deletion
DC-003 (Authentication Token) Not documented — WHO: backend lead; WHAT: What is the server-side session token lifetime and invalidation mechanism?; WHERE: Insert here Until logout or app uninstall (mechanism-dependent — Critical: see DF-001) Session state layer (mechanism not documented) Logout should invalidate token server-side and clear on-device; iOS Keychain persistence post-uninstall is a risk if Keychain is used without kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly Partial — logout clears active session; residual Keychain items may persist post-uninstall Most critical retention risk in this document
DC-004 (Password) Not stored in plaintext server-side (assumed — verify) In-memory only — discarded after submission React ref / React state Automatic on component unmount N/A — not persisted secureTextEntry={true} on all password fields
DC-005 (2FA Code) Not applicable — one-time use In-memory only — discarded after submission React state Automatic on component unmount N/A — not persisted Short-lived OTP
DC-006 (Password Reset Token) Not documented — WHO: backend lead; WHAT: How long are reset tokens valid? Are they invalidated immediately after use?; WHERE: Insert here In-memory only — discarded after submission React state Automatic on component unmount N/A — not persisted One-time use token
DC-007 (User Profile Data) Not documented In-memory only (TanStack Query cache — cleared on app close) TanStack Query in-memory cache Automatic on app close Not documented — requires investigation Profile data is public on ATProto; right to erasure requires account deletion on PDS
DC-008 (DID) Permanent — DID is a persistent identifier on ATProto Until logout or app uninstall (same as DC-003) Session state layer Same as DC-003 DID deletion requires account deletion on PDS — ATProto protocol implications DID is a permanent identifier; "deletion" on ATProto is complex
DC-009 (Direct Message Content) Not documented — WHO: backend lead; WHAT: What is the server-side retention period for DM content? Is there a deletion mechanism for individual messages or entire conversations?; WHERE: Insert here In-memory only during session (ConvoProvider) — on-device persistence between sessions not documented ConvoProvider (in-memory) App close clears in-memory cache; server-side deletion mechanism not documented Not documented — requires investigation Most sensitive data category in messaging flow
DC-010 (Conversation Metadata) Not documented In-memory only (TanStack Query cache) TanStack Query in-memory cache Automatic on app close; useLeaveConvo deletes conversation server-side Partial — useLeaveConvo provides user-initiated deletion Leave conversation deletes from user's view; server-side retention unclear
DC-011 (Messaging Preferences) Retained as actor declaration on PDS — no documented deletion mechanism In-memory only (React Query cache) TanStack Query in-memory cache No documented deletion mechanism — only update Not documented — requires investigation Actor declaration may persist after account deletion
DC-012 (Notification Preferences) Not transmitted to server Persists until app uninstall (Android) / potentially beyond (iOS UserDefaults) ExpoBackgroundNotificationHandler native storage App uninstall (Android); unclear on iOS N/A — device-local only, non-sensitive Low risk — boolean preference
DC-013 (Analytics/Behavioral Data) Not documented — WHO: DPO and engineering lead; WHAT: What is the analytics service's data retention period? Is there a mechanism for users to request deletion of their analytics data?; WHERE: Insert here Not documented — depends on analytics SDK Analytics SDK (unknown) Not documented Not documented — requires investigation Legal basis not documented
DC-014 (Device Push Notification Token) Retained by notification routing service — not documented Not documented Unknown Not documented Not documented — requires investigation APNs/FCM tokens are rotated by the OS; server-side cleanup on token rotation not documented

Mobile-specific retention notes:

iOS Keychain post-uninstall persistence: If the session state layer uses iOS Keychain to store accessJwt, Keychain items with kSecAttrAccessibleWhenUnlocked or kSecAttrAccessibleAlways access classes survive app uninstall by default on iOS. This means a user who uninstalls the app may have their authentication token remain on the device, accessible to a reinstalled version of the app. This is a GDPR right-to-erasure concern — a user who uninstalls the app expecting all data to be removed would be surprised to find their session token persists. Recommendation: use kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly and explicitly delete all Keychain items on logout.

AsyncStorage backup exposure: If accessJwt is stored in AsyncStorage, it is included in iOS iCloud backups and Android Google Drive auto-backups. Restoring a backup to a new device would restore the session token, potentially allowing session replay from the new device.


Privacy Risk Assessment

Risk ID Risk Description Likelihood Impact Risk Level Data Categories NIST Function Recommendation
PR-001 Authentication token stored in unencrypted on-device storage (AsyncStorage) — accessible on rooted/jailbroken devices, included in device backups, exposed to session replay attacks Medium (common pattern in RN apps) High (full account compromise) Critical DC-003, DC-001, DC-008 Protect Investigate session state layer storage immediately; migrate to expo-secure-store if AsyncStorage is used (see DF-001)
PR-002 Analytics service destination, data transmitted, and legal basis undocumented — potential unlawful processing of behavioral data High (analytics is actively used) High (GDPR Art. 6 violation, regulatory fine risk) Critical DC-013 Govern Document analytics service, data transmitted, and legal basis; implement consent mechanism if required (see DF-007)
PR-003 ConvoProvider transport mechanism undocumented — direct message content may not be encrypted in transit Medium High (DM content exposure) High DC-009, DC-010 Protect Document and verify TLS for all message transmission; assess end-to-end encryption (see DF-011)
PR-004 iOS Keychain items may persist after app uninstall — right-to-erasure gap Medium (iOS default behavior) Medium (residual auth token on device) High DC-003, DC-008 Govern Use kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; explicitly delete Keychain items on logout and on first launch after reinstall
PR-005 Push notification payload content undocumented — DM content may be transmitted through APNs/FCM infrastructure Medium High (DM content exposure through third-party infrastructure) High DC-009, DC-014 Protect Ensure notification payloads contain only conversation ID, not message content (see DF-016)
PR-006 Authentication token included in iOS iCloud / Android Google Drive backups (if AsyncStorage) — session replay risk on device restore Medium (if AsyncStorage is used) High (account takeover via backup restore) High DC-003 Protect Exclude sensitive data from device backups; use Keychain/Keystore which are excluded from backups by default
PR-007 Weak password strength validation in Set New Password Form — single-character passwords accepted client-side High (no complexity enforcement) Medium (weak account security) High DC-004 Protect Implement client-side password strength validation (see DF-005)
PR-008 No documented right-to-erasure mechanism for direct message content, authentication tokens, or analytics data High (no deletion flow documented) High (GDPR Art. 17 non-compliance) High DC-003, DC-009, DC-013 Control Document and implement right-to-erasure flows for all data categories
PR-009 Cross-border data transfers to user-configurable ATProto PDS instances — transfer mechanism not documented Medium (federated protocol by design) Medium (GDPR Art. 46 non-compliance) Medium DC-001, DC-002, DC-009, DC-010, DC-011 Govern Document transfer mechanisms for third-party PDS instances; consider user notice when PDS is non-EEA (see DF-008)
PR-010 Stored account list displayed on login screen — privacy exposure on shared or lost devices Medium Medium (account enumeration) Medium DC-001, DC-007, DC-008 Protect Consider device authentication before displaying account list (see DF-009)
PR-011 Notification permission requested post-login without documented pre-permission rationale — may not meet GDPR transparency requirements Medium Medium (consent validity) Medium DC-014 Communicate Implement pre-permission rationale screen before OS notification dialog (see DF-004)
PR-012 Native module error handling gap — playSoundChat preference write failures are silent Low Low (preference inaccuracy) Low DC-012 Protect Add error handling to native module calls (see DF-019)
PR-013 // TODO remove double login — potential duplicate credential transmission Low (workaround in place) Medium (duplicate session creation) Medium DC-003, DC-004 Protect Resolve double-login issue (see DF-002)
PR-014 HACKFIX setTimeout in email verification gate — fragile timing dependency could break privacy control Low (timing-dependent) Medium (unverified users accessing messaging) Medium DC-009 Protect Refactor email verification gate to use reliable mechanism (see DF-013)

Remediation Recommendations

Phase 1: Immediate (0–7 days) — Critical Privacy Risks

Phase 2: Short-Term (1–4 weeks) — High-Risk Items

Phase 3: Medium-Term (1–3 months) — Medium-Risk Items

Phase 4: Backlog — Low-Risk Items and Best Practice Improvements


Glossary

Data Privacy Terms

Term Definition
GDPR General Data Protection Regulation (EU) 2016/679 — the primary EU data protection law governing the processing of personal data of EU residents.
CCPA/CPRA California Consumer Privacy Act / California Privacy Rights Act — California state laws granting consumers rights over their personal information.
Personal Data Any information relating to an identified or identifiable natural person (GDPR Art. 4(1)). In this document: usernames, email addresses, DIDs, message content, device tokens.
Sensitive Data Data requiring additional protection beyond standard personal data. In this document: passwords, authentication tokens, 2FA codes, reset tokens, direct message content.
Special Category Data GDPR Art. 9 data: racial/ethnic origin, political opinions, religious beliefs, genetic/biometric data, health data, sex life/orientation. No special category data was identified in the reviewed screens.
ROPA Records of Processing Activities — required under GDPR Art. 30 for organizations processing personal data. Documents the purposes, legal bases, data categories, recipients, and retention periods for each processing activity.
DPIA Data Protection Impact Assessment — required under GDPR Art. 35 for high-risk processing activities. Assesses privacy risks and mitigation measures.
DPA Data Processing Agreement — a contract required under GDPR Art. 28 between a data controller and a data processor. Required for all third-party services that process personal data on behalf of the controller.
Legal Basis The lawful ground for processing personal data under GDPR Art. 6: Consent, Performance of Contract, Legal Obligation, Vital Interest, Public Interest, or Legitimate Interest.
Right to Erasure GDPR Art. 17 — the right of data subjects to request deletion of their personal data. Also known as the "right to be forgotten."
Privacy by Design GDPR Art. 25 — the principle that privacy protections should be built into systems from the outset, not added as an afterthought.
Data Minimization GDPR Art. 5(1)(c) — the principle that only the minimum necessary personal data should be collected and processed.
SCC Standard Contractual Clauses — a legal mechanism for transferring personal data from the EEA to third countries without an adequacy decision.
BCR Binding Corporate Rules — a legal mechanism for intra-group transfers of personal data to third countries.
LIA Legitimate Interest Assessment — a three-part test (purpose, necessity, balancing) required before relying on Legitimate Interest as a legal basis under GDPR Art. 6(1)(f).

GDPR Articles Referenced

Article Subject
Art. 5 Principles relating to processing of personal data (lawfulness, fairness, transparency, purpose limitation, data minimization, accuracy, storage limitation, integrity and confidentiality)
Art. 6 Lawfulness of processing — legal bases
Art. 9 Processing of special categories of personal data
Art. 13 Information to be provided where personal data are collected from the data subject (transparency)
Art. 17 Right to erasure ('right to be forgotten')
Art. 25 Data protection by design and by default
Art. 28 Processor obligations and Data Processing Agreements
Art. 30 Records of processing activities (ROPA)
Art. 32 Security of processing — appropriate technical and organisational measures
Art. 35 Data protection impact assessment (DPIA)
Art. 46 Transfers subject to appropriate safeguards (SCCs, BCRs)

ISO 27701 Controls Referenced

Control Subject
ISO 27701:2019 §7.2 Conditions for collection and processing
ISO 27701:2019 §7.3 Obligations to PII principals (data subjects)
ISO 27701:2019 §7.4 Privacy by design and privacy by default
ISO 27701:2019 §8.2 Conditions for collection and processing (processor obligations)

MASVS-STOR References

Control Subject
MASVS-STOR-1 The app only stores sensitive data in protected storage (Keychain on iOS, Keystore on Android). Sensitive data must not be stored in AsyncStorage, MMKV (without encryption), or other unprotected locations.
MASVS-STOR-2 Sensitive data is not stored in backup-eligible locations without justification. AsyncStorage and unencrypted MMKV are included in iOS iCloud and Android Google Drive backups by default.

Mobile-Specific Storage Terms

Term Definition
AsyncStorage A React Native key-value storage system backed by plain-text SQLite on the device. Not encrypted. Included in iOS iCloud backups and Android Google Drive auto-backups by default. Cleared on app uninstall.
MMKV A high-performance key-value storage library for React Native (from WeChat). Not encrypted by default — requires explicit encryption key configuration. Included in device backups by default. Cleared on app uninstall.
Keychain (iOS) Apple's secure credential storage system. Encrypted using the device's hardware security. Excluded from iCloud backup by default (depending on kSecAttrAccessible class). Persists after app uninstall by default unless kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly is used.
Keystore (Android) Android's hardware-backed key storage system. Encrypted. Not included in Google Drive backups. Cleared on app uninstall (Android 9+).
SecureStore (Expo) Expo's wrapper around iOS Keychain and Android Keystore. Provides encrypted storage with the same persistence characteristics as the underlying platform APIs.
expo-secure-store The Expo SDK package implementing SecureStore. Uses iOS Keychain and Android Keystore automatically.
Secure Enclave Apple's dedicated security chip (A-series and M-series processors) that performs cryptographic operations without exposing private keys to the main processor. Used by iOS Keychain for hardware-backed key protection.
kSecAttrAccessible An iOS Keychain attribute that controls when a Keychain item can be accessed (e.g., kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly). The ThisDeviceOnly variants prevent Keychain items from surviving app uninstall.
UserDefaults Apple's key-value storage system for app preferences on iOS/macOS. Not encrypted. Included in iCloud backups unless excluded via NSURLIsExcludedFromBackupKey.
SharedPreferences Android's key-value storage system for app preferences. Not encrypted by default. May be included in Google Drive auto-backups depending on backup rules configuration.
iCloud backup Apple's device backup service. Includes AsyncStorage, MMKV, and UserDefaults data by default. Excludes Keychain items (unless kSecAttrAccessible allows it).
Google Drive auto-backup Android's automatic backup service (Android 6+). Includes AsyncStorage, MMKV, and SharedPreferences data by default. Excludes Keystore data.
IDFA Identifier for Advertisers — Apple's advertising identifier for iOS devices. Used by ad networks for cross-app tracking. Not referenced in the reviewed screens.
GAID Google Advertising ID — Android's advertising identifier. Used by ad networks for cross-app tracking. Not referenced in the reviewed screens.
APNs Apple Push Notification service — Apple's infrastructure for delivering push notifications to iOS devices. Receives device tokens and notification payloads from app servers.
FCM Firebase Cloud Messaging — Google's infrastructure for delivering push notifications to Android devices (and iOS via Firebase SDK).

Mobile Permission Terms

Term Definition
POST_NOTIFICATIONS Android 13+ permission required to post push notifications. Implicit on earlier Android versions.
NSLocationWhenInUseUsageDescription iOS Info.plist key required to request location access while the app is in use. Not referenced in the reviewed screens.
NSCameraUsageDescription iOS Info.plist key required to request camera access. Not referenced in the reviewed screens.
UNUserNotificationCenter iOS framework for managing notification permissions and delivery. Used by expo-notifications.

Application Domain Terms

Term Definition
ATProto / AT Protocol The decentralized social networking protocol developed by Bluesky. Authentication, messaging, and profile management in this app are performed against ATProto-compatible servers.
PDS (Personal Data Server) The server that hosts a user's ATProto account data. Users may be on Bluesky's default PDS (bsky.social) or a self-hosted/third-party PDS.
DID (Decentralized Identifier) A globally unique, persistent identifier for a user on the AT Protocol (e.g., did:plc:abc123). Used as the primary key for accounts.
Handle An ATProto user identifier in the format username.domain (e.g., alice.bsky.social). Human-readable identity in the ATProto ecosystem.
accessJwt A JSON Web Token used to authenticate API requests for an ATProto session. Its presence in the session store indicates an active or recently active session.
XRPC Cross-service Remote Procedure Call — the HTTP-based RPC protocol used by ATProto. Method names like com.atproto.server.createSession are XRPC lexicon identifiers.
ConvoProvider A React context provider that manages the full state machine for a single DM conversation, including connection, message history, and real-time updates.
AllowIncoming A union type (`'all'
Actor Declaration An ATProto concept where a user declares preferences about themselves on the network. In this context, controls chat message permissions (allowIncoming).
Age Assurance A feature requiring users to confirm their age before accessing certain content (specifically: chats). Enforced by AgeRestrictedScreen.
Minimal Shell Mode An app-wide UI state where the tab bar and navigation chrome are hidden, giving the active screen full-screen real estate. Activated during conversation viewing.

Acronyms

Acronym Expansion
PII Personally Identifiable Information
DPA Data Processing Agreement (also: Data Protection Authority)
DPIA Data Protection Impact Assessment
ROPA Records of Processing Activities
SCC Standard Contractual Clauses
BCR Binding Corporate Rules
MASVS Mobile Application Security Verification Standard
MSTG Mobile Security Testing Guide
GDPR General Data Protection Regulation
CCPA California Consumer Privacy Act
CPRA California Privacy Rights Act
DPO Data Protection Officer
EEA European Economic Area
TLS Transport Layer Security
OTP One-Time Password
JWT JSON Web Token
APNs Apple Push Notification service
FCM Firebase Cloud Messaging
IDFA Identifier for Advertisers (iOS)
GAID Google Advertising ID (Android)
RN React Native
PDS Personal Data Server (ATProto)
DID Decentralized Identifier
XRPC Cross-service Remote Procedure Call
LIA Legitimate Interest Assessment
ISO International Organization for Standardization
NIST National Institute of Standards and Technology
OWASP Open Web Application Security Project

End of Data Flow & Privacy Map (Mobile) — social-app — April 2026

Generated by DocAgent — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.