Architecture Overview

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

3. Document Roadmap

Stakeholder Recommended Sections
Executives and product managers §4 Executive Summary, §5 Stakeholders & Concerns, §13 Quality Attributes
React Native developers joining the team §4, §7 Solution Strategy, §8 Building Block View, §9 Runtime View, §10 Data Flow, §11 Cross-Cutting Concerns
Mobile platform engineers (iOS/Android) §7.2 Platform-Branching Strategy, §8, §11.3 Gesture Handling, §11.4 Platform-Branching Consistency, §12 Architecture Decisions
Backend / AT Protocol engineers §6 System Context, §9 Runtime View, §10.1 Data Sources, §12.4 AD-4
Security reviewers §6, §10.4 Persistence Layer, §11.6 Authentication, §14 Risks & Technical Debt
Architects evaluating the system All sections
QA and test engineers §9 Runtime View, §11 Cross-Cutting Concerns, §14 Risks & Technical Debt

4. Executive Summary

Bluesky Social (social-app) is a decentralized social networking application built on the AT Protocol (Authenticated Transfer Protocol), an open standard for federated social data. The application allows users to create accounts, compose posts, follow other users, discover content through algorithmic and custom feeds, exchange direct messages, and manage moderation preferences. The primary users are individuals seeking an open, user-controlled social media experience across iOS, Android, and web platforms.

The application is built with React Native and Expo (bare workflow), sharing a single TypeScript codebase across iOS, Android, and Expo Web. Navigation is implemented with React Navigation using native stack navigators. Server state is managed exclusively with TanStack Query (React Query), which handles caching, pagination, and background refetching for all AT Protocol API calls. The application communicates with the AT Protocol network — specifically Bluesky's AppView and Personal Data Servers — using the @atproto/api SDK. All user-facing strings are internationalized using Lingui, with support for over 40 locales.

The most significant architectural characteristics of this system are: (1) the AT Protocol as the sole backend integration point, meaning all data — posts, profiles, feeds, notifications, direct messages, and preferences — flows through AT Protocol XRPC endpoints; (2) a multi-platform codebase with a consistent platform-branching strategy using IS_IOS, IS_ANDROID, IS_NATIVE, and IS_WEB constants derived at build time; (3) a rich set of custom Expo native modules for capabilities not covered by the standard Expo SDK, including background notification handling, GIF rendering, emoji picking, and shared preferences; and (4) OTA (over-the-air) update delivery via EAS Updates with RSA code-signing, enabling production updates without App Store review cycles.

This Architecture Overview covers 130 screens of the Bluesky Social React Native application. The screens assessed include: Value Proposition Pager.shared, Reply Notification Settings, Index.web (onboarding/step-find-contacts-intro), Settings (messages), Index.web (onboarding/step-find-contacts), Profile Follows, Profile Header Standard, Choose Account Form, Avatar Circle, Status Bar Shadow.web, Handle, Miscellaneous Notification Settings, Mention Notification Settings, Quote Notification Settings, App Passwords, About Settings, Interests Settings, Policies, Types (app-icon-settings), Use App Icon Sets, Content And Media Settings, Automation Label Settings, Appearance Settings, Following Feed Preferences, External Media Preferences, Find Contacts Settings, Profile Header Labeler, Shared Preferences Tester, Shell (profile/header), Status Bar Shadow, Like Notification Settings, Likes On Reposts Notification Settings, Index.web (settings/app-icon-settings), App Icon Image, Settings List Item (app-icon-settings), Activity Privacy Settings, Account Settings, Settings List Item.web, Signup Queued, Legacy Notification Settings, Utils (search), State (onboarding), Metrics, Use Suggested Onboarding Users, Accessibility Settings, Deactivated, Language Settings, Gesture Action, Activity Notification Settings, Settings, Splash, Inbox, Expo Scroll Forwarder, Gif, List Hidden, Find Contacts Flow, No Saved Feeds Of Any Type, Set New Password Form, Password Updated Form, No Following Feed, Log, Activity List, Form Container, Chat List, Emoji Picker, Forgot Password Form, Layout (onboarding), Verification Settings, Login Form, No Feeds Pinned, Conversation, Hashtag, Growable Banner, Edit Profile Dialog, Avatar Creator Items, Const (post-thread), Types (onboarding/step-profile), Interest Button, Avatar Creator Circle, Util (onboarding), Placeholder Canvas, Post Reposted By, Post Liked By, Error State, Display Name, Post Quotes, Starter Pack Card, Growable Avatar, Feed Section, About Section, Types (profile/sections), Profile Labeler Liked By, Profile Followers, Profile Search, Known Followers, Explore Suggested Accounts, Labels, Suggested Follows, Feed (profile/sections), Explore Interests Card, Explore Recommendations, Search Results, Saved Feeds, Explore Trending Videos, Explore Trending Topics, Explore, Shell (search), Back Next Buttons, New Follower Notification Settings, Handle Suggestions, Repost Notification Settings, Thread Preferences, Reposts On Reposts Notification Settings, State (signup), Captcha Web View.web, Captcha Web, Privacy And Security Settings, Draggable Scroll, State (starter-pack/wizard), Error, Step Profiles, Index.web (video-feed), Step Details, Starter Pack Landing, Starter Pack, Step Feeds, Takendown, Types (video-feed), Feed (src/view/com/feeds/feed), and Topic. Components, patterns, and features not included in the assessed documentation are outside the scope of this document.

This document reflects a partial view of the application, as 130 screens represent a significant but not exhaustive portion of the full codebase.

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


5. Stakeholders & Concerns

5.1 Stakeholder Table

Stakeholder Role Key Concerns
iOS Users Primary native platform audience Performance, Face ID/Touch ID support, iOS-specific UX conventions (swipe-back, safe area, Dynamic Island), App Clip support
Android Users Native platform audience Back button behavior, adaptive icons, FCM push notifications, material design conventions, edge-to-edge display
Web Users (Expo Web) Browser-based audience Responsive layout, keyboard navigation, sticky headers, no native module dependencies
Bluesky Social (Product Team) Application owner Feature velocity, AT Protocol compliance, moderation tooling, subscription monetization (Bluesky+)
React Native Developers Feature implementers Consistent patterns, type safety, clear module boundaries, testability
Technical Leads / Architects System design owners Scalability of the navigator hierarchy, state management consistency, cross-platform parity
App Store Reviewers (Apple / Google) Distribution gatekeepers Privacy manifest compliance, permission usage descriptions, age assurance, content moderation
AT Protocol / Bluesky Backend Team API providers Correct lexicon usage, session management, PDS compatibility
Security Reviewers Risk assessors Token storage security, input sanitization, OTA update integrity, sensitive data handling
Localization Contributors i18n maintainers Lingui catalog completeness, RTL support, locale-specific formatting

5.2 Top Architectural Quality Goals

Based on patterns observed across the 130 assessed screens, the following quality goals are demonstrably prioritized in the codebase:

  1. Cross-Platform Consistency — A single TypeScript codebase targets iOS, Android, and web with explicit, controlled platform divergence via IS_IOS, IS_NATIVE, and IS_WEB constants.
  2. AT Protocol Compliance — All data operations flow through the @atproto/api SDK; no alternative backend integrations are present.
  3. Internationalization Completeness — All user-facing strings are wrapped in Lingui macros (msg, <Trans>, <Plural>), supporting 40+ locales.
  4. Performance at Scale — TanStack Query infinite queries with FlatList/FlashList virtualization, React.memo, useMemo, and useCallback are applied consistently across list-heavy screens.
  5. Moderation & SafetymoderateProfile and related AT Protocol moderation functions are applied before rendering user-generated content throughout the profile and feed surfaces.

6. System Context

6.1 System Boundary Description

The Bluesky Social mobile application is the system under description. It runs as a React Native JavaScript bundle executing within the iOS and Android runtimes (and as a web application in browsers via Expo Web). The application boundary encompasses all JavaScript/TypeScript code, bundled assets, and custom native modules.

Outside the application boundary are: the AT Protocol network (Bluesky AppView, Personal Data Servers), the native iOS and Android operating system layers, push notification delivery infrastructure (APNs and FCM), the EAS Updates OTA service, the Sentry crash reporting service (when configured), the hCaptcha verification service (used during signup), and the Bitdrift network instrumentation service.

The mobile client communicates with the AT Protocol backend exclusively over HTTPS using the XRPC protocol (a JSON-over-HTTP RPC convention defined by the AT Protocol lexicon). Authentication uses JWT-based session tokens managed by the @atproto/api agent.

[Diagram: System Context — show the React Native application as a box in the center with the iOS runtime, Android runtime, AT Protocol AppView/PDS, EAS Updates service, APNs, FCM, Sentry, hCaptcha, and Bitdrift connected as external systems]

6.2 External Interfaces Table

External System Direction Protocol Purpose
Native iOS Runtime Internal-platform Native modules / JSI Keychain, Face ID, camera, microphone, photo library, APNs, contacts, location, UserDefaults, App Clip
Native Android Runtime Internal-platform Native modules / JNI Android Keystore, biometrics, FCM, contacts, SharedPreferences, adaptive icons, edge-to-edge display
AT Protocol AppView (bsky.app) Outbound HTTPS / XRPC Feed data, profile data, search, notifications, moderation labels
AT Protocol PDS (bsky.social and others) Outbound HTTPS / XRPC Account creation, session management, record writes (posts, follows, likes, preferences)
Bluesky Moderation Service (Ozone) Outbound HTTPS / XRPC Moderation reports, appeals
Bluesky Chat Service Outbound HTTPS / XRPC Direct messages, conversation management
EAS Updates (updates.bsky.app) Outbound HTTPS OTA JavaScript bundle delivery with RSA code-signing
APNs (Apple Push Notification service) Inbound Native / APNs iOS push notification delivery
FCM (Firebase Cloud Messaging) Inbound Native / FCM Android push notification delivery
Sentry (sentry.io) Outbound HTTPS Crash reporting and error monitoring (conditional on SENTRY_AUTH_TOKEN)
hCaptcha Outbound (WebView) HTTPS / iframe redirect Bot-prevention CAPTCHA during account signup
Bitdrift Outbound HTTPS Network instrumentation and observability
Bluesky Link Shortener Outbound HTTPS Generating short share URLs for starter packs and content

7. Solution Strategy

7.1 Technology Stack

Layer Technology Notes
Framework React Native (Expo bare workflow) app.config.js confirms Expo configuration; newArchEnabled: false in config
Language TypeScript tsconfig.json extends @react-native/typescript-config; strict mode via ESLint
Navigation React Navigation (native stack) NativeStackScreenProps used throughout; no Expo Router file-based routing
Server State TanStack Query (React Query) useQuery, useMutation, useInfiniteQuery used across all data-fetching screens
Animations react-native-reanimated Used in profile header, gesture actions, feed, onboarding, and settings screens
Gesture Handling react-native-gesture-handler Used in GestureActionView, BlockDrawerGesture, and swipe-to-action patterns
Internationalization Lingui (@lingui/core, @lingui/react) 40+ locales; lingui.config.ts defines catalog paths
Build System EAS Build (Expo Application Services) app.config.js references eas.build configuration
OTA Updates EAS Updates updates.url: 'https://updates.bsky.app/manifest'; RSA code-signing enabled
Crash Reporting Sentry (@sentry/react-native) Conditional on SENTRY_AUTH_TOKEN; Metro config uses getSentryExpoConfig
Bundler Metro metro.config.js extends Sentry's Expo config; custom resolver for multiformats
AT Protocol SDK @atproto/api All API calls, type definitions, and moderation functions
UI Component Library Internal (#/alf, #/components) No third-party UI library (no NativeBase, Tamagui, etc.)

7.2 Platform-Branching Strategy

The codebase uses a constants-based branching strategy as its primary mechanism. Platform constants are defined in #/env and derived from Platform.OS at build time:

These constants are used throughout the codebase in conditional expressions (e.g., IS_NATIVE ? 2 : 0.25 for onEndReachedThreshold), style utilities (e.g., native(a.absolute), web(a.sticky)), and full component exclusions (e.g., if (IS_ANDROID) return null in ProfileHeaderSuggestedFollows).

File-extension platform splits (.web.tsx) are used for screens that have no viable web implementation. Examples observed:

Platform.select() is not observed as a primary branching mechanism; the constants approach is dominant.

7.3 Navigation System Choice

The application uses React Navigation with native stack navigators. The root navigator structure is not fully visible from the assessed screens alone, but the following navigator types are evidenced:

[Not documented — WHO: Mobile platform lead; WHAT: What is the complete root navigator structure (root navigator type, auth stack vs. main stack split, tab navigator configuration, drawer navigator if any)?; WHERE: §7.3 Navigation System Choice and §8.2 Navigator Hierarchy]

7.4 Key Architectural Patterns

  1. TanStack Query for all server state — Every API call is wrapped in a useQuery, useMutation, or useInfiniteQuery hook. No raw fetch calls appear in screen components (except in ExportCarDialog for a specific JSONL endpoint that the XRPC client cannot handle).
  2. Custom hooks for data fetching — Query hooks are co-located in #/state/queries/ and expose typed data, loading, and error states to screens.
  3. Profile shadow systemuseProfileShadow wraps profile data with a local optimistic overlay, allowing follow/block state changes to reflect immediately without a full refetch.
  4. Shared wizard state via React Context — Multi-step flows (onboarding, signup, starter pack wizard) use a useReducer-backed context provider (useWizardState, useOnboardingInternalState, SignupContext) to share state across steps.
  5. Layout component systemLayout.Screen, Layout.Header.*, and Layout.Content are shared primitives used consistently across all settings and profile screens.
  6. SettingsList component systemSettingsList.Container, SettingsList.Item, SettingsList.LinkItem, SettingsList.Group, and related components provide a consistent settings list UI across all settings screens.
  7. Portal-based layout injectioncreatePortalGroup() is used in SettingsList.Group and the onboarding Layout to teleport icon/header content into a parent row without prop drilling.
  8. cleanError for user-facing error messages — All API error strings are passed through cleanError() before display to strip internal details.

7.5 Approach to Quality Goals

Quality Goal Architectural Approach
Cross-Platform Consistency IS_IOS/IS_NATIVE/IS_WEB constants; .web.tsx file splits for incompatible screens; platform() and web() style utilities from #/alf
AT Protocol Compliance All API calls via @atproto/api SDK; lexicon types used for all data shapes; moderateProfile applied before rendering
Internationalization Lingui macros on all user-facing strings; lingui.config.ts defines 40+ locales; babel-plugin-lingui-macro in build pipeline
Performance at Scale TanStack Query caching; FlatList/FlashList virtualization; React.memo, useMemo, useCallback on list-heavy screens; react-native-reanimated for UI-thread animations
Moderation & Safety moderateProfile and moderatePost from @atproto/api applied before rendering; sanitizeDisplayName, sanitizeHandle utilities; cleanError for error messages

8. Building Block View

8.1 Level 1 — High-Level Containers

Container Technology Responsibility
React Native JavaScript Bundle Metro-bundled TypeScript/JS All screen components, hooks, business logic, API calls, navigation
iOS Native Layer CocoaPods-linked native modules UIKit, AVFoundation, CoreLocation, Keychain, APNs, App Clip, custom Expo modules
Android Native Layer Gradle-linked native modules Android Keystore, FCM, contacts, SharedPreferences, adaptive icons, custom Expo modules
AT Protocol Backend Bluesky AppView + PDS Feed data, profile data, preferences, moderation, direct messages
EAS Updates Service Expo Application Services OTA JavaScript bundle delivery

8.2 Level 2 — Navigator Hierarchy

The following hierarchy is derived from NativeStackScreenProps type annotations, CommonNavigatorParams, AllNavigatorParams, and MessagesTabNavigatorParams observed across the 130 assessed screens. The root navigator structure is inferred from the presence of auth-gated screens and the logged-out shell.

[Not documented — WHO: Mobile platform lead; WHAT: The complete root navigator definition file(s) — specifically the root navigator type (Stack/Tab/Drawer), the auth stack vs. main stack split condition, and the tab navigator configuration for the main app; WHERE: §8.2 Navigator Hierarchy tree below]

Root Navigator (type: Stack — inferred from NativeStackScreenProps usage — verify against root navigator definition)
  ├── Logged-Out Shell
  │     ├── Splash (/src/view/com/auth/splash)
  │     ├── Login Stack
  │     │     ├── LoginForm (/login/login-form)
  │     │     ├── ChooseAccountForm (/login/choose-account-form)
  │     │     ├── ForgotPasswordForm (/login/forgot-password-form)
  │     │     ├── SetNewPasswordForm (/login/set-new-password-form)
  │     │     └── PasswordUpdatedForm (/login/password-updated-form)
  │     ├── Signup Stack
  │     │     ├── State (/signup/state)
  │     │     ├── BackNextButtons (/signup/back-next-buttons)
  │     │     ├── Policies (/signup/step-info/policies)
  │     │     ├── HandleSuggestions (/signup/step-handle/handle-suggestions)
  │     │     ├── CaptchaWeb (/signup/step-captcha/captcha-web)
  │     │     └── CaptchaWebView.web (/signup/step-captcha/captcha-web-view.web)
  │     ├── SignupQueued (/signup-queued)
  │     └── StarterPackLanding (/starter-pack/starter-pack-landing)
  │
  └── Main App (authenticated)
        ├── Deactivated (/deactivated)
        ├── Takendown (/takendown)
        ├── Onboarding Stack
        │     ├── Layout (/onboarding/layout)
        │     ├── State (/onboarding/state)
        │     ├── AvatarCircle (/onboarding/step-profile/avatar-circle)
        │     ├── AvatarCreatorCircle (/onboarding/step-profile/avatar-creator-circle)
        │     ├── AvatarCreatorItems (/onboarding/step-profile/avatar-creator-items)
        │     ├── PlaceholderCanvas (/onboarding/step-profile/placeholder-canvas)
        │     ├── InterestButton (/onboarding/step-interests/interest-button)
        │     ├── StarterPackCard (/onboarding/step-suggested-starterpacks/starter-pack-card)
        │     ├── ValuePropositionPager.shared (/onboarding/step-finished/value-proposition-pager.shared)
        │     ├── Index.web — StepFindContactsIntro (/onboarding/step-find-contacts-intro/index.web)
        │     └── Index.web — StepFindContacts (/onboarding/step-find-contacts/index.web)
        │
        ├── Tab Navigator (inferred — verify against root navigator definition)
        │     ├── Home Tab
        │     │     ├── Feed (/src/view/com/feeds/feed)
        │     │     ├── NoFeedsPinned (/home/no-feeds-pinned)
        │     │     ├── NoFollowingFeed (/feeds/no-following-feed)
        │     │     └── NoSavedFeedsOfAnyType (/feeds/no-saved-feeds-of-any-type)
        │     ├── Search Tab
        │     │     ├── Shell (/search/shell)
        │     │     ├── Explore (/search/explore)
        │     │     ├── SearchResults (/search/search-results)
        │     │     ├── Utils (/search/utils)
        │     │     ├── ExploreSuggestedAccounts (/search/modules/explore-suggested-accounts)
        │     │     ├── ExploreInterestsCard (/search/modules/explore-interests-card)
        │     │     ├── ExploreRecommendations (/search/modules/explore-recommendations)
        │     │     ├── ExploreTrendingVideos (/search/modules/explore-trending-videos)
        │     │     └── ExploreTrendingTopics (/search/modules/explore-trending-topics)
        │     ├── Notifications Tab
        │     │     ├── ActivityList (/notifications/activity-list)
        │     │     └── Log (/log)
        │     └── Messages Tab (MessagesTabNavigatorParams)
        │           ├── Inbox (/messages/inbox)
        │           ├── ChatList (/messages/chat-list)
        │           ├── Conversation (/messages/conversation)
        │           └── Settings (/messages/settings)
        │
        ├── CommonNavigatorParams screens (modal/push stack)
        │     ├── Profile Stack
        │     │     ├── ProfileHeaderStandard (/profile/header/profile-header-standard)
        │     │     ├── ProfileHeaderLabeler (/profile/header/profile-header-labeler)
        │     │     ├── Shell (/profile/header/shell)
        │     │     ├── Handle (/profile/header/handle)
        │     │     ├── DisplayName (/profile/header/display-name)
        │     │     ├── Metrics (/profile/header/metrics)
        │     │     ├── GrowableBanner (/profile/header/growable-banner)
        │     │     ├── GrowableAvatar (/profile/header/growable-avatar)
        │     │     ├── StatusBarShadow (/profile/header/status-bar-shadow)
        │     │     ├── StatusBarShadow.web (/profile/header/status-bar-shadow.web)
        │     │     ├── EditProfileDialog (/profile/header/edit-profile-dialog)
        │     │     ├── SuggestedFollows (/profile/header/suggested-follows)
        │     │     ├── ErrorState (/profile/error-state)
        │     │     ├── ProfileFollows (/profile/profile-follows)
        │     │     ├── ProfileFollowers (/profile/profile-followers)
        │     │     ├── ProfileSearch (/profile/profile-search)
        │     │     ├── KnownFollowers (/profile/known-followers)
        │     │     ├── ProfileLabelerLikedBy (/profile/profile-labeler-liked-by)
        │     │     ├── Labels (/profile/sections/labels)
        │     │     ├── Feed (/profile/sections/feed)
        │     │     └── Types (/profile/sections/types)
        │     ├── Post Stack
        │     │     ├── PostRepostedBy (/post/post-reposted-by)
        │     │     ├── PostLikedBy (/post/post-liked-by)
        │     │     ├── PostQuotes (/post/post-quotes)
        │     │     └── Const (/post-thread/const)
        │     ├── Hashtag (/hashtag)
        │     ├── Topic (/topic)
        │     ├── SavedFeeds (/saved-feeds)
        │     ├── ListHidden (/list/list-hidden)
        │     ├── FindContactsFlow (/find-contacts-flow)
        │     ├── ProfileList Stack
        │     │     ├── FeedSection (/profile-list/feed-section)
        │     │     └── AboutSection (/profile-list/about-section)
        │     ├── StarterPack (/starter-pack)
        │     ├── StarterPackLanding (/starter-pack/starter-pack-landing)
        │     ├── StarterPack Wizard
        │     │     ├── State (/starter-pack/wizard/state)
        │     │     ├── StepDetails (/starter-pack/wizard/step-details)
        │     │     ├── StepProfiles (/starter-pack/wizard/step-profiles)
        │     │     └── StepFeeds (/starter-pack/wizard/step-feeds)
        │     ├── VideoFeed
        │     │     ├── Index.web (/video-feed/index.web)
        │     │     └── Types (/video-feed/types)
        │     └── Settings Stack
        │           ├── Settings (/settings)
        │           ├── AccountSettings (/settings/account-settings)
        │           ├── AppPasswords (/settings/app-passwords)
        │           ├── AboutSettings (/settings/about-settings)
        │           ├── InterestsSettings (/settings/interests-settings)
        │           ├── AppearanceSettings (/settings/appearance-settings)
        │           ├── AccessibilitySettings (/settings/accessibility-settings)
        │           ├── LanguageSettings (/settings/language-settings)
        │           ├── ContentAndMediaSettings (/settings/content-and-media-settings)
        │           ├── AutomationLabelSettings (/settings/automation-label-settings)
        │           ├── FollowingFeedPreferences (/settings/following-feed-preferences)
        │           ├── ExternalMediaPreferences (/settings/external-media-preferences)
        │           ├── FindContactsSettings (/settings/find-contacts-settings)
        │           ├── ThreadPreferences (/settings/thread-preferences)
        │           ├── PrivacyAndSecuritySettings (/settings/privacy-and-security-settings)
        │           ├── ActivityPrivacySettings (/settings/activity-privacy-settings)
        │           ├── VerificationSettings (/moderation/verification-settings)
        │           ├── LegacyNotificationSettings (/settings/legacy-notification-settings)
        │           ├── AppIconSettings Stack
        │           │     ├── Index.web (/settings/app-icon-settings/index.web)
        │           │     ├── Types (/settings/app-icon-settings/types)
        │           │     ├── UseAppIconSets (/settings/app-icon-settings/use-app-icon-sets)
        │           │     ├── AppIconImage (/settings/app-icon-settings/app-icon-image)
        │           │     ├── SettingsListItem (/settings/app-icon-settings/settings-list-item)
        │           │     └── SettingsListItem.web (/settings/app-icon-settings/settings-list-item.web)
        │           └── Notification Settings Stack
        │                 ├── ReplyNotificationSettings (/settings/notification-settings/reply-notification-settings)
        │                 ├── MiscellaneousNotificationSettings (/settings/notification-settings/miscellaneous-notification-settings)
        │                 ├── MentionNotificationSettings (/settings/notification-settings/mention-notification-settings)
        │                 ├── QuoteNotificationSettings (/settings/notification-settings/quote-notification-settings)
        │                 ├── LikeNotificationSettings (/settings/notification-settings/like-notification-settings)
        │                 ├── LikesOnRepostsNotificationSettings (/settings/notification-settings/likes-on-reposts-notification-settings)
        │                 ├── RepostNotificationSettings (/settings/notification-settings/repost-notification-settings)
        │                 ├── RepostsOnRepostsNotificationSettings (/settings/notification-settings/reposts-on-reposts-notification-settings)
        │                 ├── NewFollowerNotificationSettings (/settings/notification-settings/new-follower-notification-settings)
        │                 └── ActivityNotificationSettings (/settings/notification-settings/activity-notification-settings)
        │
        └── E2E / Dev Screens
              └── SharedPreferencesTester (/e2e/shared-preferences-tester)

[Diagram: Building Block View — show the navigator hierarchy tree, screen components as leaves, and native module integration points (ExpoBackgroundNotificationHandler, ExpoBlueskyGifView, EmojiPicker, ExpoScrollForwarder, SharedPrefs)]

8.3 Shared Building Blocks

Building Block Type Used By Responsibility
Layout.Screen / Layout.Header.* / Layout.Content Shared UI components All settings, profile, and notification screens Consistent screen chrome with safe-area handling
SettingsList.* Shared UI components All settings screens Consistent settings list rows, groups, and dividers
useNotificationSettingsQuery Custom hook (TanStack Query) All 10 notification settings screens Fetches and caches the user's full notification preferences
useNotificationSettingsUpdateMutation Custom hook (TanStack Query) All 10 notification settings screens Persists notification preference changes to the AT Protocol backend
PreferenceControls Shared UI component All notification settings screens Renders push/in-app toggle group and optional filter radio group
useProfileQuery Custom hook (TanStack Query) Profile screens, Settings, Account Settings, Automation Label Fetches a user's full AT Protocol profile by DID
useSession / useSessionApi Custom hooks Auth screens, Settings, Profile, Messages Provides current account state and session management functions
useAnalytics Custom hook Analytics-instrumented screens Provides ax.metric() for event tracking
PostFeed Shared UI component Home feed, Profile sections, Activity List Virtualized, paginated, polled post list
moderateProfile AT Protocol SDK function Profile header, Search, Explore, Chat List Computes content moderation decisions for a profile
cleanError Utility function All screens with error states Sanitizes raw API error strings for user display
sanitizeDisplayName / sanitizeHandle Utility functions Profile header, Display Name, Handle, Chat List Normalizes user-generated strings before rendering
ExpoBackgroundNotificationHandler Custom native module Messages Settings Reads/writes background notification preferences to native storage
ExpoBlueskyGifView Custom native module GIF rendering in posts Renders and animates GIF content natively
EmojiPicker Custom native module Emoji picker in composer Renders platform-native emoji picker UI
SharedPrefs Custom native module SharedPreferencesTester, app-wide Cross-platform native key-value storage

9. Runtime View

9.1 App Launch & Auth State Check

Participants: JS bundle, useSession, SplashScreen, root navigator, LoggedOutShell or main app navigator

Sequence:

  1. Metro-bundled JS bundle loads; React Native runtime initializes.
  2. Root component mounts; useSession reads persisted session tokens from the session store.
  3. SplashScreen renders the Bluesky logo and background illustration while session state is being hydrated.
  4. If no valid session exists → root navigator renders LoggedOutShellSplashScreen with "Create account" and "Sign in" buttons.
  5. If a valid session exists → root navigator renders the main app navigator → home feed loads.
  6. If the session is a queued-signup session (isSignupQueued(accessJwt)) → SignupQueued screen renders with a 60-second polling loop.
  7. If the account is deactivated → Deactivated screen renders.
  8. If the account is taken down → Takendown screen renders.

Error path: If session hydration fails (e.g., corrupted token store), the app falls back to the logged-out shell.

[Diagram: Sequence — App Launch & Auth State Check]

9.2 Authenticated Feed Load

Participants: Feed screen, usePostFeedQuery, AT Protocol AppView, PostFeed, List (FlatList)

Sequence:

  1. User navigates to the Home tab; Feed screen mounts with feed="following" descriptor.
  2. usePostFeedQuery("following", ...) fires; TanStack Query checks cache — if stale or absent, sends GET /xrpc/app.bsky.feed.getTimeline to the AT Protocol AppView.
  3. Response arrives; TanStack Query stores pages in cache; feedItems array is computed via useMemo.
  4. PostFeed renders List (Animated.FlatList) with initialNumToRender items.
  5. User scrolls; onEndReached fires when 2 viewport-heights remain; fetchNextPage() is called.
  6. After 60 seconds, checkForNew() polls for new posts; if new posts exist, hasNew becomes true and LoadLatestBtn appears.
  7. User taps LoadLatestBtn; truncateAndInvalidate clears cached pages beyond page 1 and triggers a background refetch; list scrolls to top.

Error path: Network failure → isError becomes truePostFeedErrorMessage renders with detectKnownError() classification and a retry button.

[Diagram: Sequence — Authenticated Feed Load]

9.3 Notification Settings Update

Participants: ReplyNotificationSettings (representative of all 10 notification settings screens), useNotificationSettingsQuery, useNotificationSettingsUpdateMutation, AT Protocol PDS, PreferenceControls

Sequence:

  1. User navigates to a notification settings screen (e.g., Reply Notification Settings).
  2. useNotificationSettingsQuery() fires on mount; fetches AppBskyNotificationDefs.Preferences from the AT Protocol backend.
  3. preferences.reply is passed to PreferenceControls; toggle group renders with current channel state.
  4. User toggles "Push notifications" off; onChangeChannels(['list']) fires in PreferenceControls.
  5. ax.metric('activityPreference:changeChannels', ...) fires for analytics.
  6. useNotificationSettingsUpdateMutation().mutate({ reply: { ...preference, push: false } }) is called.
  7. AT Protocol backend persists the updated preference; TanStack Query cache is updated.
  8. UI reflects the new state (toggle is now off).

Error path: Mutation fails → error is silently swallowed (no error UI shown to user — see §14 Risks & Technical Debt).

[Diagram: Sequence — Notification Settings Update]

9.4 Background → Foreground Transition

Participants: AppState (React Native), PostFeed, usePostFeedQuery, Messages screens, useMessagesEventBus

Sequence:

  1. App moves to background; AppState changes to 'background'.
  2. Message polling interval requests are released by useFocusEffect cleanup functions.
  3. App returns to foreground; AppState changes to 'active'.
  4. PostFeed's AppState.addEventListener callback fires; checkForNew() is called for the active feed.
  5. If new posts exist, hasNew becomes true; LoadLatestBtn appears with a dot indicator.
  6. If the Messages screen is focused, messagesBus.requestPollInterval(MESSAGE_SCREEN_POLL_INTERVAL) is re-registered via useFocusEffect.
  7. useRefreshOnFocus triggers a refetch of conversation lists when the Messages screen regains focus.

Error path: Network unavailable on foreground → checkForNew() fails silently (network errors are swallowed in the polling path).

[Diagram: Sequence — Background → Foreground Transition]

9.5 Signup Flow

Participants: SignupContext (reducer), StepInfo, StepHandle, StepCaptcha, useSubmitSignup, AT Protocol PDS, onboardingDispatch

Sequence:

  1. User taps "Create account" on SplashScreen; signup navigator mounts; SignupContext initializes with createInitialState().
  2. StepInfo renders; user enters email, password, date of birth, and optionally an invite code.
  3. User taps "Next"; dispatch({ type: 'next' }) advances to StepHandle.
  4. User enters a handle; useActorAutocompleteQuery provides suggestions via HandleSuggestions.
  5. User taps "Next"; if serviceDescription.phoneVerificationRequired, dispatch({ type: 'next' }) advances to StepCaptcha.
  6. CaptchaWebView (native) or CaptchaWebView.web (web) loads the hCaptcha challenge URL in a WebView/iframe.
  7. User completes the CAPTCHA; the WebView detects the redirect to bsky.app and extracts the code parameter.
  8. dispatch({ type: 'setPendingSubmit', value: { verificationCode: code } }) stores the code.
  9. useSubmitSignup calls createAccount(...) on the AT Protocol PDS.
  10. On success, onboardingDispatch({ type: 'start' }) initiates the onboarding flow.

Error path: createAccount fails with InvalidInviteCodeError → user is returned to StepInfo with an error on the invite code field. Other errors → user is returned to StepInfo with a generic error message.

⚠️ This workflow continues beyond the documented screens. Steps involving the full onboarding wizard (StepProfile, StepInterests, StepSuggestedStarterpacks, StepFindContacts, StepFinished) are partially documented but the complete onboarding navigator structure is not fully covered in this document. Verify the complete workflow with the development team before treating this as a comprehensive process description.

[Diagram: Sequence — Signup Flow]


10. Data Flow & State Management

10.1 Data Sources

Source Data Type Access Mechanism
AT Protocol AppView (bsky.app) Feed posts, profiles, search results, notifications, trending topics TanStack Query hooks in #/state/queries/
AT Protocol PDS (bsky.social and others) Account records, preferences, follow/like/repost records TanStack Query mutation hooks
Bluesky Chat Service Conversations, messages ConvoProvider state machine; polling via useMessagesEventBus
Native device storage (iOS UserDefaults / Android SharedPreferences) Background notification preferences (playSoundChat) ExpoBackgroundNotificationHandler native module
App-level persisted storage Search history, account history, dev mode flags, demo mode useStorage hook (abstracted; likely MMKV-backed)
React Query in-memory cache All server state TanStack Query cache; useQueryClient for manual manipulation
React Context / useReducer Onboarding state, signup state, starter pack wizard state useOnboardingInternalState, SignupContext, useWizardState
#/state/preferences hooks User preferences (autoplay, in-app browser, trending, language, thread, external embeds) Custom hooks backed by persisted storage

10.2 State Management Approach

The application uses a layered state management strategy:

  1. Server state — TanStack Query (React Query) is the exclusive mechanism for all AT Protocol API data. Screens do not hold server data in local useState; they read from the query cache.
  2. Global UI state — React Context with useReducer is used for multi-step wizard flows (onboarding, signup, starter pack wizard) and for shell-level state (minimal shell mode, active starter pack, logged-out view controls).
  3. Local component stateuseState is used for ephemeral UI state (loading flags, form inputs, dialog open/close, pull-to-refresh indicators).
  4. Persisted preferences — Custom hooks in #/state/preferences/ abstract the underlying storage mechanism (likely MMKV) for user preferences that survive app restarts.
  5. Profile shadow systemuseProfileShadow provides a local optimistic overlay on top of TanStack Query-cached profile data, enabling immediate UI updates for follow/block/like actions.

10.3 Navigation State as Data

Route params are used as the primary mechanism for passing data between screens in the navigator stack:

10.4 Persistence Layer

Data Storage Mechanism Encrypted Notes
AT Protocol session tokens (accessJwt, refreshJwt) Session store (abstracted) [Not documented — WHO: Mobile platform lead; WHAT: What storage mechanism backs the session store — is it SecureStore/Keychain, MMKV, or AsyncStorage? Is it encrypted?; WHERE: §10.4 Persistence Layer] Critical security concern — see §14
Background notification preferences (playSoundChat) Native module (iOS UserDefaults / Android SharedPreferences) No Non-sensitive boolean flag
Search term history useStorage hook (per-account) No Non-sensitive; capped at 6 items
Profile DID history useStorage hook (per-account) No Non-sensitive; capped at 10 items
Dev mode flag useStorage hook No Non-sensitive boolean
Demo mode flag useStorage hook No Non-sensitive boolean; iOS only
User preferences (autoplay, language, thread, etc.) #/state/preferences hooks No Non-sensitive UI preferences
App icon selection useCurrentAppIcon hook (abstracted) No Non-sensitive
NUX completion state useSaveNux / AT Protocol preferences No Stored server-side
Shared preferences (E2E test keys) SharedPrefs native module No Test-only; non-sensitive

10.5 Data Transformation Pipeline

AT Protocol API response (JSON)
  → @atproto/api SDK (typed deserialization)
  → TanStack Query cache (in-memory, keyed by RQKEY)
  → Custom hook (usePostFeedQuery, useProfileQuery, etc.)
  → Screen component (reads data, isLoading, error)
  → Moderation layer (moderateProfile, moderatePost)
  → Sanitization (sanitizeDisplayName, sanitizeHandle, cleanError)
  → UI rendering (PostFeed, ProfileHeader, etc.)

For preferences:

AT Protocol preferences API
  → usePreferencesQuery (TanStack Query)
  → normalizeSort / normalizeView / channels derivation (useMemo)
  → Toggle.Group / Toggle.Item (controlled components)
  → useNotificationSettingsUpdateMutation / useSetFeedViewPreferencesMutation
  → AT Protocol preferences API (write)

10.6 Cross-Screen Data Sharing

Mechanism Used For Examples
Route params Screen-to-screen data transfer via navigation name/rkey for profile/post screens; topic for Hashtag/Topic
TanStack Query cache Shared server state across screens Profile data shared between Profile header and Profile sections
React Context Wizard step state useOnboardingInternalState, SignupContext, useWizardState
Profile shadow cache Optimistic follow/block state useProfileShadow used in Profile header, Chat List, Explore
Shell state atoms Global UI state useSetMinimalShellMode, useActiveStarterPack, useCurrentConvoId
useSession Current account identity Used in all authenticated screens

11. Cross-Cutting Concerns

11.1 Safe-Area Handling

Safe-area handling is consistently delegated to the Layout component system. Layout.Screen, Layout.Header.Outer, and Layout.Content are shared primitives that internally handle SafeAreaView behavior. Individual screen components do not directly import SafeAreaView or call useSafeAreaInsets in most cases.

Exceptions where useSafeAreaInsets is called directly:

This pattern is consistent and appropriate. No screens were observed to lack safe-area handling where it would be expected.

11.2 Keyboard Handling

Keyboard handling is inconsistent across the codebase:

There is no single, app-wide KeyboardAvoidingView strategy. Keyboard handling is addressed per-screen or per-dialog as needed.

11.3 Gesture Handling System

react-native-gesture-handler is used in the application, evidenced by:

[Not documented — WHO: Mobile platform lead; WHAT: Is GestureHandlerRootView wrapping the root of the application? If not, gesture handling may fail on Android; WHERE: §11.3 Gesture Handling System and §14 Risks & Technical Debt]

11.4 Platform-Branching Consistency

The platform-branching strategy is consistent across the assessed screens. The primary mechanism is the IS_IOS, IS_ANDROID, IS_NATIVE, and IS_WEB constants from #/env, supplemented by the platform(), native(), ios(), android(), and web() style utilities from #/alf.

The .web.tsx file-extension split is used exclusively for screens that have no viable web implementation (throwing an error or returning null). This is a deliberate, documented pattern.

No screens were observed using Platform.OS === 'ios' or Platform.select() directly in screen-level code; these are abstracted behind the constants. This is a positive consistency finding.

11.5 Error Handling Strategy

The error handling strategy is layered but partially inconsistent:

11.6 Authentication & Authorization

Authentication is enforced at the navigator level (not within individual screens). Screens assume a valid session exists and use useSession() to access currentAccount. The useRequireAuth() hook is used in specific action handlers (e.g., follow/unfollow in ProfileHeaderStandard) to redirect unauthenticated users to login before executing mutations.

Auth tokens are managed by the @atproto/api BskyAgent instance, which handles token refresh automatically. Screens do not directly read or write JWT tokens.

The session storage mechanism is abstracted behind useSession / useSessionApi. The underlying storage (SecureStore, Keychain, MMKV, or AsyncStorage) is not visible in the assessed screen documentation and represents a security concern (see §14 Risks & Technical Debt).

11.7 Loading & Empty States

Loading and empty states are handled consistently using shared components:

The pattern of showing a Loader spinner when preference is undefined (loading) and an Admonition error when isError is true is applied consistently across all 10 notification settings screens.

11.8 Logging & Observability

System Integration Scope
Sentry @sentry/react-native/expo plugin in app.config.js; getSentryExpoConfig in metro.config.js; conditional on SENTRY_AUTH_TOKEN Crash reporting and error monitoring
Bitdrift @bitdrift/react-native plugin in app.config.js with networkInstrumentation: true Network request instrumentation
Internal logger #/logger module; logger.error, logger.warn, logger.debug Structured application logging
Analytics useAnalytics() hook (#/analytics); ax.metric(...) Custom event tracking (provider not identified in assessed documentation)

[Not documented — WHO: Analytics/data engineering team; WHAT: What is the analytics provider behind useAnalytics() / ax.metric()? Is it PostHog, Mixpanel, a custom system, or another provider?; WHERE: §11.8 Logging & Observability]

11.9 Performance Patterns

Pattern Usage
FlatList virtualization PostFeed (home feed, profile sections, activity list), ChatList, Inbox, SearchResults, Explore, SavedFeeds, FindContactsSettings, KnownFollowers, ProfileFollowers, StepProfiles, StepFeeds
FlashList Not observed in assessed documentation
React.memo PostFeed, FeedItemInner, PostContent, ChatListItem, SuggestedProfileCard, SearchResults sub-components, List
useMemo Feed item arrays, moderation decisions, derived preference values, interest display names, navigator sections
useCallback Event handlers in all list-heavy screens; focus effect callbacks
react-native-reanimated (UI-thread animations) Profile header (growable banner/avatar, status bar shadow), gesture actions, appearance settings, app passwords dialog, onboarding layout
useInitialNumToRender PostFeed, KnownFollowers, SearchResults, Topic — dynamically calculates initial render count based on screen height
truncateAndInvalidate Feed refresh — preserves first page of cache to avoid loading flash
enabled: active pattern SearchResults tabs, TopicScreenTab, StepProfiles, StepFeeds — prevents inactive tabs from fetching
staleTime: Infinity StepDetails profile query — prevents refetch during wizard flow

11.10 Missing or Not Observed


12. Architecture Decisions

AD-1: React Navigation with Native Stack

AD-2: Expo Bare Workflow with EAS Build

AD-3: TanStack Query for Server State

AD-4: AT Protocol as the Exclusive Backend API

AD-5: Platform-Branching via IS_IOS / IS_NATIVE / IS_WEB Constants

AD-6: Lingui for Internationalization

AD-7: React Compiler (babel-plugin-react-compiler)

AD-8: EAS Updates with Code-Signing for OTA Delivery

AD-9: Custom Native Modules via Expo Modules API

AD-10: Sentry for Crash Reporting (Conditional)

AD-11: New Architecture Disabled (newArchEnabled: false)


13. Quality Attributes

Quality Attribute Current State Evidence Risk Level
Performance Good — virtualized lists, memoization, UI-thread animations FlatList with windowSize, maxToRenderPerBatch, removeClippedSubviews in PostFeed; React.memo on ChatListItem, SuggestedProfileCard; react-native-reanimated worklets in profile header Low
Maintainability Good — consistent patterns, typed navigator params, shared component system Layout.*, SettingsList.*, PreferenceControls shared across screens; CommonNavigatorParams / AllNavigatorParams typed param lists; ESLint rules enforce import ordering and text wrapping Low–Medium
Security Partial — session token storage mechanism not confirmed; mutation errors silently swallowed cleanError() applied before display; sanitizeDisplayName/sanitizeHandle applied before rendering; BLUESKY_MOD_SERVICE_HEADERS for moderation routing; hCaptcha host allowlist in CaptchaWebView; session token storage mechanism not visible in assessed documentation Medium
Reliability Partial — no centralized error boundary; mutation errors silently swallowed in notification settings ListMaybePlaceholder for list error states; cleanError for error display; logger.error for observability; no React Error Boundary observed; notification settings mutation errors not surfaced to users Medium
Portability Good — single codebase for iOS, Android, and web; .web.tsx stubs for incompatible screens IS_NATIVE/IS_WEB constants; platform()/web()/native() style utilities; .web.tsx file splits for contacts, app icon, video feed screens Low
Usability Good — consistent loading/empty states, i18n for 40+ locales, accessibility rules enforced ListMaybePlaceholder, Loader, Admonition used consistently; Lingui macros on all strings; eslint-plugin-react-native-a11y in ESLint config; useReducedMotion() respected in animations Low
Internationalization Excellent — 40+ locales, compile-time extraction, pluralization support lingui.config.ts defines 40+ locales; babel-plugin-lingui-macro; <Plural> used for follower/following counts; i18n.number() for locale-aware number formatting Low
Testability Partial — testID props on key interactive elements; E2E screen present testID on buttons, inputs, and screens throughout; SharedPreferencesTester E2E screen; no unit test patterns visible in assessed documentation Medium

14. Risks & Technical Debt

ID Category Description Severity Recommendation
R-1 Security Risk Session token storage mechanism not confirmed. The useSession/useSessionApi hooks abstract the session store, but the underlying storage (SecureStore/Keychain vs. AsyncStorage vs. MMKV) is not visible in the assessed documentation. If tokens are stored in unencrypted AsyncStorage, they are accessible to other apps on rooted/jailbroken devices. High Audit #/state/session implementation to confirm tokens are stored in SecureStore (iOS) / Android Keystore-backed storage. Document the storage mechanism in this section.
R-2 Risk GestureHandlerRootView wrapper not confirmed. react-native-gesture-handler is used in GestureActionView and BlockDrawerGesture, but the presence of GestureHandlerRootView at the app root is not confirmed in the assessed documentation. Without it, gestures may fail silently on Android. High Verify that the root component wraps the navigator in <GestureHandlerRootView style={{ flex: 1 }}>.
R-3 Tech Debt Notification settings mutation errors silently swallowed. All 10 notification settings screens use useNotificationSettingsUpdateMutation without onError callbacks. A failed preference save gives the user no feedback and may leave them believing their change was saved. Medium Add onError handling to PreferenceControls to show a toast or inline error when the mutation fails.
R-4 Tech Debt SettingsListItem.web.tsx is an empty stub. The web implementation of the App Icon Settings list item returns undefined with no UI. This is a known incomplete implementation. Medium Implement the web variant or explicitly document that app icon customization is not supported on web.
R-5 Tech Debt VideoFeed/index.web.tsx returns null. The web video feed is a stub with no implementation. Medium Implement the web video feed or document the intentional exclusion.
R-6 Tech Debt StarterPackCard.onFollowAll does not return early on mutation error. After the bulkWriteFollows catch block, execution continues to setIsFollowingAll(true) and setIsProcessing(false), marking the operation as successful even when it failed. Medium Add return after the error handling in the bulkWriteFollows catch block.
R-7 Tech Debt STARTER_PACK_MAX_SIZE off-by-one in wizard state. The profile cap check uses > (strictly greater than), meaning the cap is STARTER_PACK_MAX_SIZE + 1 profiles before blocking. Low Change the check to >= to enforce the cap at exactly STARTER_PACK_MAX_SIZE.
R-8 Risk decodeURIComponent without try/catch in Hashtag, Topic, and ActivityList screens. A malformed URI-encoded route param would throw an unhandled URIError, crashing the screen. Medium Wrap decodeURIComponent calls in try/catch with a fallback value.
R-9 Tech Debt TODO remove double login in LoginForm. A code comment documents a known issue where login() may be called twice in some flow. Low Investigate and resolve the double-login condition.
R-10 Tech Debt Unpinned feeds list in SavedFeeds uses plain map(). The unpinned feeds section renders with Array.prototype.map() rather than a virtualized list. For users with many saved feeds, this could cause performance issues. Low Migrate the unpinned feeds list to FlatList or FlashList if user research indicates large saved feed lists are common.
R-11 Risk app.bsky.unspecced endpoints used in production. ExploreTrendingTopics and ExploreRecommendations use AppBskyUnspeccedDefs types, indicating reliance on unstable, unspecced AT Protocol endpoints that may change or be removed without notice. Medium Monitor AT Protocol changelog for stabilization of these endpoints; implement graceful degradation if they become unavailable.
R-12 Tech Debt SharedPreferencesTester E2E screen has no production build guard. The screen is accessible in production builds (no __DEV__ or IS_INTERNAL guard) and exposes the ability to write to shared preferences storage. Medium Add an IS_INTERNAL guard to prevent this screen from being accessible in production builds.
R-13 Tech Debt stickyHeaderIndices computation in Explore is O(n²). items.indexOf(curr) inside a reduce over the items array produces quadratic complexity. For current data volumes this is not a bottleneck, but it should be addressed if the items array grows. Low Track indices during the items array construction rather than searching for them afterward.
R-14 Risk No React Error Boundary observed. Render errors in screen components (e.g., from .web.tsx stubs that throw) would propagate to the nearest error boundary or crash the app. No ErrorBoundary component usage is observed in the assessed documentation. Medium Implement React Error Boundaries at the navigator level and around major feature areas.
R-15 Tech Debt newArchEnabled: false — New Architecture not adopted. The application runs on the legacy bridge architecture, missing JSI performance benefits and Turbo Module lazy loading. Low Plan a migration to the New Architecture; audit all native modules for Fabric/Turbo Module compatibility.

15. Glossary

15.1 Domain Terms

Term Definition
AT Protocol Authenticated Transfer Protocol — an open standard for federated social networking developed by Bluesky. Defines lexicons (schemas), XRPC (HTTP-based RPC), and DIDs for identity.
AppView The Bluesky application-layer service that aggregates and indexes AT Protocol data for efficient querying (e.g., bsky.app).
PDS (Personal Data Server) An AT Protocol server that hosts a user's repository of records (posts, follows, likes, preferences). Users may self-host or use a hosted PDS (e.g., bsky.social).
DID (Decentralized Identifier) A globally unique, persistent identifier for an AT Protocol account (e.g., did:plc:abc123). Used as the canonical identity key throughout the app.
AT URI An AT Protocol URI identifying a specific record (e.g., at://did:plc:abc123/app.bsky.feed.post/rkey123).
Lexicon An AT Protocol schema definition for a record type or XRPC method (e.g., app.bsky.feed.post, com.atproto.server.createAccount).
XRPC A JSON-over-HTTP RPC convention used by the AT Protocol. Queries are GET requests; procedures are POST requests.
Feed Generator An AT Protocol service that provides a custom algorithmic feed. Identified by a feedgen AT URI.
Starter Pack A Bluesky feature allowing users to bundle a list of accounts and feeds for easy onboarding of new users.
Labeler An AT Protocol moderation service that applies content labels to posts and accounts.
NUX (New User Experience) A one-time onboarding prompt or card shown to new users. Tracked via the Nux enum and useSaveNux hook.
Bluesky+ A Bluesky subscription tier that unlocks premium app icons and other features.
App Clip An iOS feature allowing users to experience a lightweight version of the app without installing it. Used for starter pack onboarding.
Germ DM A third-party direct messaging integration (com.germnetwork.declaration) visible in the profile header.
TID (Timestamp ID) A lexicographically sortable, collision-resistant identifier used as record keys in AT Protocol repositories. Generated via TID.nextStr() from @atproto/common-web.
Profile Shadow A local optimistic overlay on top of a cached AT Protocol profile, allowing follow/block state changes to reflect immediately without a server round-trip.
Ozone Bluesky's moderation service infrastructure. Reports and appeals are routed to Ozone via BLUESKY_MOD_SERVICE_HEADERS.

15.2 Architecture Terms

Term Definition
Navigator A React Navigation component that manages a set of screens and their transitions (e.g., NativeStackNavigator, BottomTabNavigator).
ParamList A TypeScript type mapping route names to their parameter shapes (e.g., CommonNavigatorParams, AllNavigatorParams). Used to type-check navigation.navigate() calls and route.params.
Route key The string name of a screen within a navigator (e.g., 'ProfileFollows', 'MessagesConversation').
Stack navigator A React Navigation navigator that manages screens in a push/pop stack with slide transitions. Used throughout the app via @react-navigation/native-stack.
Tab navigator A React Navigation navigator that manages screens accessible via a tab bar. Used for the main app tabs (Home, Search, Notifications, Messages).
Drawer navigator A React Navigation navigator that manages screens accessible via a side drawer. Presence in this app is inferred from BlockDrawerGesture usage — verify against root navigator definition.
Deep link A URL that navigates directly to a specific screen within the app (e.g., bsky.app/profile/alice.bsky.social). Configured via associatedDomains in app.config.js.
Native module A module that bridges JavaScript to platform-native code (Swift/Kotlin). Custom native modules in this app use the Expo Modules API.
Platform branching The practice of rendering different code paths based on the current platform (iOS, Android, web). In this app, implemented via IS_IOS, IS_NATIVE, IS_WEB constants and .web.tsx file splits.
Metro bundler The JavaScript bundler used by React Native. Configured in metro.config.js; extended with Sentry's Expo config.
Hermes The JavaScript engine used by React Native on iOS and Android. Enabled by default in modern React Native versions.
JSI (JavaScript Interface) React Native's synchronous bridge between JavaScript and native code. Not yet active in this app (newArchEnabled: false).
Fabric React Native's New Architecture renderer. Not yet active in this app (newArchEnabled: false).
Turbo Module React Native's New Architecture native module system with lazy loading. Not yet active in this app (newArchEnabled: false).
Managed workflow An Expo workflow where Expo manages the native project files. This app uses the bare workflow instead.
Bare workflow An Expo workflow where the developer has full access to the native ios/ and android/ directories. Used by this app.
EAS (Expo Application Services) Expo's cloud build and update infrastructure. Used for binary builds (EAS Build) and OTA updates (EAS Updates).
OTA update An over-the-air JavaScript bundle update delivered via EAS Updates without requiring an App Store release.
TanStack Query A server state management library (also known as React Query) providing caching, background refetching, and pagination for async data. Used exclusively for all AT Protocol API calls in this app.
FeedDescriptor A pipe-delimited string encoding the type and identity of a feed (e.g., "following", `"feedgen
RQKEY / FEED_RQKEY Functions that produce TanStack Query cache keys for specific query types. Used to identify and invalidate cached data.
Profile shadow See Domain Terms above.
cleanError A utility function that strips internal error prefixes and stack traces from API error strings before displaying them to users.
sanitizeDisplayName / sanitizeHandle Utility functions that normalize user-generated display names and handles before rendering, including LTR enforcement to prevent BiDi text spoofing.
Lingui The internationalization library used in this app. Provides compile-time string extraction (msg, <Trans>, <Plural> macros) and runtime translation via useLingui().
#/alf The internal Atomic Layout Framework — the app's design system providing style atoms (a.*), theme tokens (t.atoms.*, t.palette.*), and platform utilities (platform(), native(), web(), ios(), android()).
Portal group A React pattern (implemented via createPortalGroup()) that allows child components to inject content into a parent's designated outlet without prop drilling. Used in SettingsList.Group and the onboarding Layout.
useProfileShadow A hook that wraps a TanStack Query-cached profile with a local optimistic overlay, enabling immediate UI updates for follow/block/like actions.
truncateAndInvalidate A TanStack Query cache utility that truncates cached pages to the first page and marks the query as stale, triggering a background refetch without a full loading flash.