Architecture Overview

bluesky-social/social-app

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

May 5, 2026

Document Roadmap

Stakeholder Recommended Sections
Executives and product managers Executive Summary (§4), Stakeholders & Concerns (§5), Quality Attributes (§13)
React Native developers joining the team Solution Strategy (§7), Building Block View (§8), Data Flow & State Management (§10), Cross-Cutting Concerns (§11), Glossary (§15)
Architects evaluating the system All sections
iOS/Android engineers working on native modules System Context (§6), Solution Strategy §7.2, Architecture Decisions AD-2, AD-9, AD-11, Cross-Cutting Concerns §11.3
QA engineers and test automation Runtime View (§9), Risks & Technical Debt (§14)
Security reviewers System Context (§6), Architecture Decisions AD-2, AD-8, Data Flow §10.4, Cross-Cutting Concerns §11.6, Risks §14

Executive Summary

The Bluesky social-app is a cross-platform social networking application built on the AT Protocol (Authenticated Transfer Protocol), a decentralized social networking standard. The application enables users to post short-form content, follow other users, engage with feeds curated by third-party feed generators, send direct messages, manage moderation preferences, and participate in a federated social graph. The primary users are individuals seeking an open, decentralized alternative to centralized social media platforms. The application targets iOS, Android, and web browsers from a single React Native codebase.

The technical approach is built on Expo's bare workflow with React Native, enabling access to custom native modules while retaining Expo's build tooling (EAS Build) and over-the-air update infrastructure (EAS Updates). The AT Protocol SDK (@atproto/api) serves as the sole data access layer — there is no traditional REST API or GraphQL layer; all data operations are AT Protocol XRPC calls to the user's Personal Data Server (PDS) and the Bluesky AppView. TanStack Query (React Query) manages all server state, caching, and pagination. React Navigation provides the navigator hierarchy. Lingui handles internationalization across more than 40 locales. React Native Reanimated drives all scroll-synchronized and gesture-driven animations on the UI thread.

The most significant architectural characteristics of this system are: (1) its deep coupling to the AT Protocol as both the identity and data layer, making the application a first-class AT Protocol client rather than a traditional mobile app with a proprietary backend; (2) its cross-platform scope — iOS, Android, and web are all served from the same codebase with platform branching handled through Platform.OS checks, platform() utilities, and .web.tsx file splits; (3) its use of a large number of custom Expo native modules for capabilities such as GIF playback, emoji picking, shared preferences, and scroll forwarding; and (4) its investment in performance through Reanimated UI-thread animations, TanStack Query caching, and the React Compiler for automatic memoization.

This Architecture Overview covers 131 screens of the Bluesky social-app React Native application. The screens assessed include: Inbox, Shell (Search), Feed, Images (Onboarding), Choose Account Form, Status Bar Shadow, Shell (Profile Header), Handle Suggestions, Miscellaneous Notification Settings, Account Settings, Settings (Messages), Utils (Search), Mention Notification Settings, Settings, Deactivated, Log, Hashtag, No Saved Feeds Of Any Type, No Following Feed, No Feeds Pinned, Find Contacts Flow, List Hidden, Shared Preferences Tester, Form Container, Set New Password Form, State (Onboarding), Login Form, Chat List, Emoji Picker, Value Proposition Pager.shared, Gif, Password Updated Form, Verification Settings, Forgot Password Form, Value Proposition Pager, Expo Scroll Forwarder, Index.web (Step Find Contacts Intro), Index.web (Step Find Contacts), Activity List, Layout (Onboarding), Conversation, Avatar Creator Items, Edit Profile Dialog, Growable Banner, Starter Pack Card, Feed Section, Types (Onboarding Step Profile), Const (Post Thread), Value Proposition Pager.web, Interest Button, Metrics, Handle, Growable Avatar, Avatar Circle, Util (Onboarding), Post Liked By, Status Bar Shadow.web, Profile Labeler Liked By, Explore Trending Videos, Explore Trending Topics, Suggested Follows, Profile Followers, Profile Follows, Known Followers, Profile Search, Explore Recommendations, Feed (Profile Sections), Explore Suggested Accounts, Labels (Profile Sections), Search Results, Saved Feeds, Types (Profile Sections), Explore Interests Card, Explore, App Passwords, Activity Privacy Settings, External Media Preferences, Index.web (App Icon Settings), Settings List Item.web (App Icon Settings), App Icon Image, Settings List Item (App Icon Settings), Types (App Icon Settings), Automation Label Settings, Accessibility Settings, Appearance Settings, About Settings, Interests Settings, Content And Media Settings, Find Contacts Settings, Following Feed Preferences, Legacy Notification Settings, Reposts On Reposts Notification Settings, New Follower Notification Settings, Reply Notification Settings, Quote Notification Settings, Privacy And Security Settings, Like Notification Settings, Thread Preferences, Likes On Reposts Notification Settings, Repost Notification Settings, Splash, Starter Pack, Gesture Action, Signup Queued, Draggable Scroll, Takendown, Index.web (Video Feed), Error, Back Next Buttons, Step Details (Starter Pack Wizard), Captcha Web, Types (Video Feed), State (Signup), State (Starter Pack Wizard), Starter Pack Landing, Topic, Step Profiles (Starter Pack Wizard), Step Feeds (Starter Pack Wizard), Policies, Captcha Web View.web, Placeholder Canvas, Display Name, Post Quotes, Avatar Creator Circle, Activity Notification Settings, Profile Header Standard, Post Reposted By, Language Settings, Error State, About Section, and Profile Header Labeler.

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 131 screens represent a significant but not exhaustive portion of the full codebase.

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


Stakeholders & Concerns

5.1 Stakeholder Table

Stakeholder Role Key Concerns
iOS Users Primary mobile platform audience Performance, Face ID/Touch ID support, iOS-specific UX conventions (swipe-back, haptics, safe area), App Clip support, dynamic app icons
Android Users Secondary mobile platform audience Back button behavior, adaptive icons, FCM push notifications, material conventions, large heap support
Web Users Browser-based audience Responsive layout, keyboard navigation, no native module dependencies, web-specific scroll behavior
Bluesky Social (Product Team) Application owner and AT Protocol operator Feature velocity, AT Protocol compliance, moderation tooling, content safety
Third-Party Feed Generator Operators External service providers Feed API compatibility, error surfacing, graceful degradation when feeds are offline or misconfigured
Labeler/Moderation Service Operators External moderation service providers Labeler subscription UI, label display, subscription limits
React Native Developers (Internal) Primary engineering audience Code maintainability, consistent patterns, testability, platform branching clarity
App Store Reviewers (Apple/Google) Gatekeepers for distribution Privacy manifest compliance, permission usage descriptions, content policy adherence
Localization Contributors Community translators Lingui catalog completeness, string extraction correctness, 40+ locale support

5.2 Top Architectural Quality Goals

Derived from patterns observed across the 131 assessed screens:

  1. Cross-Platform Consistency — The codebase serves iOS, Android, and web from a single React Native source. Platform differences are handled through explicit branching rather than separate codebases.
  2. AT Protocol Compliance — All data operations must conform to AT Protocol lexicons. The application is a first-class AT Protocol client, not a proprietary-backend app.
  3. Performance at Scale — Feed screens handle large paginated datasets. Reanimated UI-thread animations, TanStack Query caching, FlatList virtualization, and the React Compiler are all employed to maintain responsiveness.
  4. Internationalization — The application supports 40+ locales via Lingui. All user-facing strings must be wrapped in translation macros.
  5. Moderation & Safety — Content moderation decisions from the AT Protocol moderation system must be applied consistently across all profile, feed, and messaging surfaces.

System Context

6.1 System Boundary Description

The Bluesky social-app JavaScript bundle runs inside the React Native runtime on iOS and Android, and inside a browser on web. The application boundary encompasses the React Native JavaScript bundle, the custom Expo native modules, and the platform-specific native layers (iOS CocoaPods-linked modules, Android Gradle-linked modules).

Outside the application boundary are: the AT Protocol network (the user's Personal Data Server, the Bluesky AppView, feed generator servers, and labeler services), the Bluesky chat service (a separate DM service endpoint), the Bluesky moderation service (Ozone), push notification infrastructure (APNs on iOS, FCM on Android), the EAS Updates server for OTA bundle delivery, the Sentry error reporting service (when SENTRY_AUTH_TOKEN is configured), and the Bitdrift network instrumentation service.

The mobile client → backend API boundary is the HTTPS/XRPC boundary between the React Native JavaScript runtime and the AT Protocol network. Authentication at this boundary uses AT Protocol session tokens (JWTs) managed by the BskyAgent from @atproto/api. The base URL for the primary Bluesky AppView is https://bsky.social (production) or https://staging.bsky.dev (staging). The chat service uses a separate endpoint routed via DM_SERVICE_HEADERS.

[Diagram: System Context — show the React Native application as a box in the center with the iOS runtime, Android runtime, AT Protocol network (PDS, AppView, feed generators, labelers), Bluesky chat service, Bluesky moderation service (Ozone), APNs, FCM, EAS Updates server, Sentry, and Bitdrift connected]

6.2 External Interfaces Table

External System Direction Protocol Purpose
Native iOS Runtime Internal-platform Native modules / JSI iOS Keychain, Face ID, camera, photo library, APNs, safe area insets, haptics, App Clip
Native Android Runtime Internal-platform Native modules / JNI Android Keystore, biometrics, FCM, permissions, large heap, adaptive icons
AT Protocol PDS (Personal Data Server) Outbound HTTPS / XRPC Account creation, session management, record reads/writes (profile, follows, blocks, likes, reposts, posts)
Bluesky AppView Outbound HTTPS / XRPC Feed queries, search, actor lookups, moderation decisions, trending topics
Bluesky Chat Service Outbound HTTPS / XRPC Direct message conversations, conversation management
Bluesky Moderation Service (Ozone) Outbound HTTPS / XRPC Moderation reports, appeals
Third-Party Feed Generator Servers Outbound HTTPS / XRPC Custom algorithmic feed content
Third-Party Labeler Services Outbound HTTPS / XRPC Content labeling and moderation
APNs (Apple Push Notification Service) Inbound Native / APNs iOS push notifications for new messages, mentions, likes, follows
FCM (Firebase Cloud Messaging) Inbound Native / FCM Android push notifications
EAS Updates Server (updates.bsky.app) Outbound HTTPS OTA JavaScript bundle delivery with RSA code signing
Sentry Outbound HTTPS Crash reporting and error monitoring (conditional on SENTRY_AUTH_TOKEN)
Bitdrift Outbound HTTPS Network instrumentation and observability
hCaptcha Outbound (via WebView/iframe) HTTPS CAPTCHA verification during signup
Germ DM (landing.ger.mx) Outbound HTTPS (deep link) Third-party direct messaging integration

Solution Strategy

7.1 Technology Stack

Layer Technology Notes
Framework React Native (Expo bare workflow) newArchEnabled: false in app.config.js
Build System EAS Build iOS deployment target 15.1; Android compileSdkVersion 36, targetSdkVersion 35
Navigation React Navigation (Stack + Tab navigators) Not Expo Router; file-based routing is not used
Server State TanStack Query v5 (@tanstack/react-query) All AT Protocol data fetching, caching, pagination
Animation React Native Reanimated UI-thread worklets for scroll-driven and gesture-driven animations
Gesture Handling React Native Gesture Handler Swipe gestures in chat, feed interactions
Internationalization Lingui (@lingui/core, @lingui/react) 40+ locales; Babel macro for string extraction
AT Protocol Client @atproto/api All XRPC calls, session management, type definitions
Compiler Optimization React Compiler (babel-plugin-react-compiler, target '19') Automatic memoization
Crash Reporting Sentry (@sentry/react-native) Conditional on SENTRY_AUTH_TOKEN environment variable
Network Observability Bitdrift (@bitdrift/react-native) Network instrumentation plugin
OTA Updates EAS Updates RSA code signing with rsa-v1_5-sha256; checkAutomatically: 'NEVER'
Image Rendering expo-image Replaces React Native's built-in Image for caching and performance
Video expo-video Native video playback
Font Inter (variable font on web, static weights on Android) Loaded via expo-font
UI Component Library Internal design system (#/alf, #/components/*) No third-party UI library (MUI, Radix, shadcn)
Web Bundler Webpack (via @expo/webpack-config) For web builds; Metro for native

7.2 Platform-Branching Strategy

The codebase uses multiple coexisting platform-branching strategies:

  1. Platform.OS === 'ios' / 'android' / 'web' inline checks — Used throughout for platform-specific behavior (e.g., IS_IOS, IS_ANDROID, IS_NATIVE, IS_WEB constants from #/env).
  2. Platform.select({ ios: ..., android: ..., web: ... }) — Used for value-based branching (e.g., onEndReachedThreshold, maxToRenderPerBatch).
  3. .web.tsx file splits — Used to provide web-specific implementations or stubs. Examples observed: StatusBarShadow.web.tsx (returns null), ValuePropositionPager.web.tsx, CaptchaWebView.web.tsx, VideoFeed/index.web.tsx (returns null), StepFindContactsIntro/index.web.tsx (throws), StepFindContacts/index.web.tsx (throws), AppIconSettings/index.web.tsx (throws), AppIconSettings/SettingsListItem.web.tsx (empty stub).
  4. platform() utility from #/alf — Used within the design system for style-level platform branching (e.g., platform({ios: ..., android: ...})).
  5. native() / web() utilities from #/alf — Used to apply styles conditionally to native or web only.
  6. IS_NATIVE / IS_WEB constants — Build-time constants from #/env used to gate entire feature sections (e.g., the Notification Sounds section in Messages Settings is gated by IS_NATIVE).

The primary strategy is inline IS_* constant checks for behavioral branching and .web.tsx file splits for complete platform-specific implementations or stubs.

7.3 Navigation System Choice

The application uses React Navigation with a combination of Stack and Tab navigators. Expo Router (file-based routing) is not used. The navigator hierarchy is documented in Section 8.2.

Key navigation characteristics observed:

[Not documented — WHO: Navigation/routing engineer; WHAT: What is the root navigator type (Stack or Tab) and what are the top-level tab names and their associated stack navigators? WHERE: Section 8.2, Navigator Hierarchy tree]

7.4 Key Architectural Patterns

7.5 Approach to Quality Goals

Quality Goal Architectural Approach
Cross-Platform Consistency IS_* constants, .web.tsx file splits, platform() / native() / web() utilities from #/alf
AT Protocol Compliance @atproto/api SDK as the sole data access layer; all mutations use AT Protocol lexicons; bsky.validate for schema validation
Performance at Scale TanStack Query caching, FlatList virtualization (windowSize, maxToRenderPerBatch), Reanimated UI-thread animations, React Compiler for automatic memoization
Internationalization Lingui with 40+ locale catalogs; msg macro for extraction; Trans/Plural components for rendering
Moderation & Safety moderateProfile() applied at every profile render surface; moderation.ui() gates content display; sanitizeDisplayName() and sanitizeHandle() applied before rendering

Building Block View

8.1 Level 1 — High-Level Containers

Container Technology Responsibility
React Native JavaScript Bundle Metro (native) / Webpack (web) All screen components, hooks, business logic, AT Protocol API calls, navigation
iOS Native Layer CocoaPods-linked modules Custom Expo modules (GIF view, emoji picker, scroll forwarder, Swiss Army), expo-video, expo-blur, expo-linear-gradient, react-native-reanimated, react-native-gesture-handler, Sentry native SDK
Android Native Layer Gradle-linked modules Same custom Expo modules, FCM integration, large heap configuration, adaptive icons
AT Protocol Network External PDS, AppView, feed generators, labelers, chat service, moderation service
EAS Updates Server External OTA JavaScript bundle delivery

8.2 Level 2 — Navigator Hierarchy

The following tree is derived from screen documentation. Screens not covered in the assessed documentation are marked with [not assessed].

Root Navigator [type not documented — verify with navigation engineer]
  ├── Auth Flow (condition: user is not authenticated or account is deactivated)
  │     ├── Splash (/src/view/com/auth/splash)
  │     ├── Login Flow
  │     │     ├── FormContainer (/login/form-container)
  │     │     ├── ChooseAccountForm (/login/choose-account-form)
  │     │     ├── LoginForm (/login/login-form)
  │     │     ├── ForgotPasswordForm (/login/forgot-password-form)
  │     │     ├── SetNewPasswordForm (/login/set-new-password-form)
  │     │     └── PasswordUpdatedForm (/login/password-updated-form)
  │     ├── Signup Flow
  │     │     ├── State (/signup/state)
  │     │     ├── BackNextButtons (/signup/back-next-buttons)
  │     │     ├── StepInfo → Policies (/signup/step-info/policies)
  │     │     ├── StepHandle → HandleSuggestions (/signup/step-handle/handle-suggestions)
  │     │     ├── StepCaptcha → CaptchaWeb (/signup/step-captcha/captcha-web)
  │     │     │                → CaptchaWebView.web (/signup/step-captcha/captcha-web-view.web)
  │     │     └── SignupQueued (/signup-queued)
  │     ├── Deactivated (/deactivated)
  │     └── Takendown (/takendown)
  │
  └── Main Navigator [type: Tab — not documented, verify with navigation engineer]
        ├── Home Tab [not assessed]
        │     └── Home Stack
        │           ├── NoFeedsPinned (/home/no-feeds-pinned)
        │           └── Feed (/src/view/com/feeds/feed)
        │
        ├── Search Tab
        │     └── Search Stack
        │           ├── Shell (/search/shell)
        │           ├── Utils (/search/utils)
        │           ├── SearchResults (/search/search-results)
        │           ├── Explore (/search/explore)
        │           ├── Explore Modules:
        │           │     ├── ExploreTrendingVideos (/search/modules/explore-trending-videos)
        │           │     ├── ExploreTrendingTopics (/search/modules/explore-trending-topics)
        │           │     ├── ExploreRecommendations (/search/modules/explore-recommendations)
        │           │     ├── ExploreSuggestedAccounts (/search/modules/explore-suggested-accounts)
        │           │     └── ExploreInterestsCard (/search/modules/explore-interests-card)
        │           ├── Hashtag (/hashtag)
        │           └── Topic (/topic)
        │
        ├── Notifications Tab [not assessed]
        │     └── Notifications Stack
        │           └── ActivityList (/notifications/activity-list)
        │
        ├── Messages Tab
        │     └── Messages Stack
        │           ├── ChatList (/messages/chat-list)
        │           ├── Inbox (/messages/inbox)
        │           ├── Conversation (/messages/conversation)
        │           └── Settings (/messages/settings)
        │
        └── Profile/Settings Tab [not assessed]
              └── Settings Stack
                    ├── Settings (/settings)
                    ├── AccountSettings (/settings/account-settings)
                    ├── AppPasswords (/settings/app-passwords)
                    ├── ActivityPrivacySettings (/settings/activity-privacy-settings)
                    ├── ExternalMediaPreferences (/settings/external-media-preferences)
                    ├── AppIconSettings (native only)
                    │     ├── Index.web (/settings/app-icon-settings/index.web) [throws on web]
                    │     ├── SettingsListItem (/settings/app-icon-settings/settings-list-item)
                    │     ├── SettingsListItem.web (/settings/app-icon-settings/settings-list-item.web) [stub]
                    │     ├── AppIconImage (/settings/app-icon-settings/app-icon-image)
                    │     └── Types (/settings/app-icon-settings/types)
                    ├── AutomationLabelSettings (/settings/automation-label-settings)
                    ├── AccessibilitySettings (/settings/accessibility-settings)
                    ├── AppearanceSettings (/settings/appearance-settings)
                    ├── AboutSettings (/settings/about-settings)
                    ├── InterestsSettings (/settings/interests-settings)
                    ├── ContentAndMediaSettings (/settings/content-and-media-settings)
                    ├── FindContactsSettings (/settings/find-contacts-settings)
                    ├── FollowingFeedPreferences (/settings/following-feed-preferences)
                    ├── LegacyNotificationSettings (/settings/legacy-notification-settings) [redirects]
                    ├── NotificationSettings [not assessed]
                    │     └── Notification Sub-Settings:
                    │           ├── MiscellaneousNotificationSettings (/settings/notification-settings/miscellaneous-notification-settings)
                    │           ├── MentionNotificationSettings (/settings/notification-settings/mention-notification-settings)
                    │           ├── RepostsOnRepostsNotificationSettings (/settings/notification-settings/reposts-on-reposts-notification-settings)
                    │           ├── NewFollowerNotificationSettings (/settings/notification-settings/new-follower-notification-settings)
                    │           ├── ReplyNotificationSettings (/settings/notification-settings/reply-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)
                    │           └── ActivityNotificationSettings (/settings/notification-settings/activity-notification-settings)
                    ├── PrivacyAndSecuritySettings (/settings/privacy-and-security-settings)
                    ├── ThreadPreferences (/settings/thread-preferences)
                    ├── LanguageSettings (/settings/language-settings)
                    └── VerificationSettings (/moderation/verification-settings)

Profile Screens (accessible from multiple tabs):
  ├── Profile Header Shell (/profile/header/shell)
  ├── ProfileHeaderStandard (/profile/header/profile-header-standard)
  ├── ProfileHeaderLabeler (/profile/header/profile-header-labeler)
  ├── Profile Header Components:
  │     ├── StatusBarShadow (/profile/header/status-bar-shadow)
  │     ├── StatusBarShadow.web (/profile/header/status-bar-shadow.web) [returns null]
  │     ├── GrowableBanner (/profile/header/growable-banner)
  │     ├── GrowableAvatar (/profile/header/growable-avatar)
  │     ├── DisplayName (/profile/header/display-name)
  │     ├── Handle (/profile/header/handle)
  │     ├── Metrics (/profile/header/metrics)
  │     ├── SuggestedFollows (/profile/header/suggested-follows)
  │     └── EditProfileDialog (/profile/header/edit-profile-dialog)
  ├── Profile Sections:
  │     ├── Feed (/profile/sections/feed)
  │     ├── Labels (/profile/sections/labels)
  │     └── Types (/profile/sections/types)
  ├── ProfileFollowers (/profile/profile-followers)
  ├── ProfileFollows (/profile/profile-follows)
  ├── KnownFollowers (/profile/known-followers)
  ├── ProfileSearch (/profile/profile-search)
  ├── ProfileLabelerLikedBy (/profile/profile-labeler-liked-by)
  └── ErrorState (/profile/error-state)

Post Screens:
  ├── PostLikedBy (/post/post-liked-by)
  ├── PostQuotes (/post/post-quotes)
  └── PostRepostedBy (/post/post-reposted-by)

Onboarding Flow:
  ├── Layout (/onboarding/layout)
  ├── State (/onboarding/state)
  ├── Util (/onboarding/util)
  ├── StepProfile:
  │     ├── AvatarCreatorItems (/onboarding/step-profile/avatar-creator-items)
  │     ├── AvatarCreatorCircle (/onboarding/step-profile/avatar-creator-circle)
  │     ├── AvatarCircle (/onboarding/step-profile/avatar-circle)
  │     ├── PlaceholderCanvas (/onboarding/step-profile/placeholder-canvas)
  │     └── Types (/onboarding/step-profile/types)
  ├── StepInterests → InterestButton (/onboarding/step-interests/interest-button)
  ├── StepSuggestedStarterpacks → StarterPackCard (/onboarding/step-suggested-starterpacks/starter-pack-card)
  ├── StepFindContactsIntro:
  │     └── Index.web (/onboarding/step-find-contacts-intro/index.web) [throws on web]
  ├── StepFindContacts:
  │     └── Index.web (/onboarding/step-find-contacts/index.web) [throws on web]
  └── StepFinished:
        ├── Images (/onboarding/step-finished/images)
        ├── ValuePropositionPager (/onboarding/step-finished/value-proposition-pager)
        ├── ValuePropositionPager.shared (/onboarding/step-finished/value-proposition-pager.shared)
        └── ValuePropositionPager.web (/onboarding/step-finished/value-proposition-pager.web)

Starter Pack Screens:
  ├── StarterPack (/starter-pack)
  ├── StarterPackLanding (/starter-pack/starter-pack-landing)
  └── Wizard:
        ├── State (/starter-pack/wizard/state)
        ├── StepDetails (/starter-pack/wizard/step-details)
        ├── StepProfiles (/starter-pack/wizard/step-profiles)
        └── StepFeeds (/starter-pack/wizard/step-feeds)

Feeds Screens:
  ├── SavedFeeds (/saved-feeds)
  ├── NoSavedFeedsOfAnyType (/feeds/no-saved-feeds-of-any-type)
  └── NoFollowingFeed (/feeds/no-following-feed)

List Screens:
  ├── ListHidden (/list/list-hidden)
  ├── ProfileList → FeedSection (/profile-list/feed-section)
  └── ProfileList → AboutSection (/profile-list/about-section)

Video Feed:
  ├── Index.web (/video-feed/index.web) [returns null]
  └── Types (/video-feed/types)

Find Contacts Flow (/find-contacts-flow)

Utility Screens:
  ├── Log (/log)
  ├── Error (/src/view/com/util/error)
  └── SharedPreferencesTester (/e2e/shared-preferences-tester) [E2E only]

Custom Animations:
  └── GestureAction (/src/lib/custom-animations/gesture-action)

Pager Components:
  └── DraggableScroll (/src/view/com/pager/draggable-scroll)

Native Modules (Custom Expo Modules):
  ├── EmojiPicker (/modules/expo-emoji-picker/src/emoji-picker)
  ├── GifView (/modules/expo-bluesky-gif-view/src/gif)
  └── ExpoScrollForwarder (/modules/expo-scroll-forwarder/src/expo-scroll-forwarder)

⚠️ The root navigator type and the complete tab structure are not fully documented in the assessed screens. The navigator hierarchy above is reconstructed from route keys and screen props observed across 131 screens. Verify the complete navigator tree against the application's root navigation configuration file before treating this as authoritative.

[Diagram: Building Block View — show the navigator hierarchy tree as above, with screen components as leaves, native module integration points (EmojiPicker, GifView, ExpoScrollForwarder, SharedPrefs), and the AT Protocol API boundary]

8.3 Shared Building Blocks

Building Block Type Used By Responsibility
usePostFeedQuery Custom hook (TanStack Query) Feed, ActivityList, ProfileSections/Feed Fetches paginated AT Protocol feed data; handles polling, pagination, and cache invalidation
useListConvosQuery Custom hook (TanStack Query) ChatList, Inbox Fetches paginated conversation lists from the AT Protocol chat service
useProfileQuery Custom hook (TanStack Query) Profile screens, Settings, Conversation Fetches a single AT Protocol actor profile
useNotificationSettingsQuery Custom hook (TanStack Query) All notification settings screens Fetches AppBskyNotificationDefs.Preferences
usePreferencesQuery Custom hook (TanStack Query) Explore, SavedFeeds, FollowingFeedPreferences, VerificationSettings, InterestsSettings Fetches app.bsky.actor.getPreferences
useSession / useSessionApi Context hooks All authenticated screens Provides currentAccount, accounts, resumeSession, createAccount, logoutCurrentAccount
moderateProfile AT Protocol SDK function All profile render surfaces Computes ModerationDecision from profile + ModerationOpts
useProfileShadow Custom hook Profile headers, ChatList, ActivityNotificationSettings Applies optimistic local mutations to a profile object
Layout.* Internal component system All screens Provides Layout.Screen, Layout.Header.*, Layout.Content, Layout.Center
SettingsList.* Internal component system All settings screens Provides Container, Item, Group, LinkItem, PressableItem, Divider, BadgeText
Toggle.* Internal component system All settings and notification screens Provides Group, Item, Radio, Platform, LabelText for checkbox/radio controls
PostFeed Component Feed, ActivityList, ProfileSections/Feed, FeedSection Renders a virtualized, paginated list of posts with interstitials, polling, and error handling
ProfileHeaderShell Component ProfileHeaderStandard, ProfileHeaderLabeler Outer wrapper providing banner, avatar, back button, live status, and moderation alerts
GestureActionView Component (wraps GestureAction) ChatList, Inbox Provides swipe-to-action gesture with animated background reveal
BskyAgent (from @atproto/api) Service SDK All data-fetching hooks Authenticated AT Protocol XRPC client; manages session tokens
#/alf design system Internal module All screens Provides atoms, useTheme, platform(), native(), web(), useBreakpoints(), useGutters()
@lingui/react + @lingui/core i18n library All screens Provides Trans, Plural, msg, useLingui for 40+ locale support

Runtime View

9.1 App Launch & Auth State Resolution

Participants: Root navigator, session state (useSession), BskyAgent, TanStack Query client, Splash screen, Auth flow, Main navigator

Sequence:

  1. Metro/Webpack bundle loads; React Native runtime initializes.
  2. Root component mounts; SessionProvider initializes and reads persisted session data from device storage.
  3. If a valid session exists, BskyAgent is configured with the stored access token.
  4. Root navigator evaluates auth state: if currentAccount is defined and not deactivated/takendown, the Main navigator is rendered; otherwise the Auth flow is rendered.
  5. If the account is deactivated, the Deactivated screen is shown.
  6. If the account is takendown, the Takendown screen is shown.
  7. If no session exists, the Splash screen is shown with "Sign In" and "Create Account" CTAs.
  8. On successful authentication, onboardingDispatch({type: 'start'}) is called if the user is new; otherwise the Main navigator is shown directly.

Error path: If session restoration fails (e.g., expired token), the user is redirected to the login flow. If the AT Protocol PDS is unreachable, the Deactivated or error screen is shown with a retry option.

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

9.2 Feed Load & Infinite Scroll

Participants: PostFeed component, usePostFeedQuery hook, TanStack Query cache, AT Protocol AppView (app.bsky.feed.getTimeline / app.bsky.feed.getFeed), FlatList (via List component)

Sequence:

  1. PostFeed mounts with a FeedDescriptor prop (e.g., "following" or "feedgen|at://...").
  2. usePostFeedQuery(feed, feedParams) fires; TanStack Query checks the cache for RQKEY(feed, feedParams).
  3. If cache miss or stale, an XRPC GET request is sent to the AT Protocol AppView.
  4. Response pages are accumulated in the TanStack Query infinite query cache.
  5. feedItems is computed via useMemo — pages are flattened, interstitials are inserted at configured positions, and blocked/muted authors are filtered.
  6. List renders the first initialNumToRender items (calculated by useInitialNumToRender).
  7. As the user scrolls, onEndReached fires when onEndReachedThreshold is crossed; fetchNextPage() is called.
  8. New pages are appended to the cache; feedItems recomputes; List renders new items.
  9. A setInterval at POLL_FREQ (60 seconds) calls pollLatest() to check for new content; if found, hasNew is set to true and LoadLatestBtn appears.

Error path: Network errors during initial load render PostFeedErrorMessage with a retry button. Errors during fetchNextPage render LoadMoreRetryBtn at the bottom of the list. HTTP 429 from feed generators surfaces a "high traffic" message.

[Diagram: Sequence — Feed Load & Infinite Scroll]

9.3 Background → Foreground Transition

Participants: AppState (React Native), PostFeed, useMessagesEventBus, useFocusEffect

Sequence:

  1. User switches away from the app; AppState transitions to 'background'.
  2. PostFeed's AppState.addEventListener('change') listener fires; polling is paused.
  3. The messages event bus stops requesting poll intervals (the useFocusEffect cleanup fires in ChatList and Inbox).
  4. User returns to the app; AppState transitions to 'active'.
  5. PostFeed's AppState listener fires checkForNew(), which calls pollLatest() to check for new feed content.
  6. ChatList and Inbox re-register their poll interval requests with useMessagesEventBus.
  7. useRefreshOnFocus hooks on focused screens trigger refetch() calls to refresh stale data.

Error path: If the network is unavailable when the app returns to foreground, pollLatest() errors are silently ignored. Data remains stale until the next successful poll or user-initiated pull-to-refresh.

[Diagram: Sequence — Background → Foreground Transition]

9.4 Messaging Real-Time Poll Cycle

Participants: ChatList, Inbox, useMessagesEventBus, useListConvosQuery, AT Protocol chat service

Sequence:

  1. ChatList or Inbox gains focus; useFocusEffect fires.
  2. useMessagesEventBus().requestPollInterval(MESSAGE_SCREEN_POLL_INTERVAL) is called (10-second interval).
  3. The event bus schedules polling at the requested interval.
  4. Every 10 seconds, useListConvosQuery is refetched via the event bus mechanism.
  5. New conversations or unread counts are reflected in the UI.
  6. When the screen loses focus or the app goes to background, the useFocusEffect cleanup calls unsub(), removing the poll interval request.

Error path: Poll errors are caught and logged via logger.error. The UI retains the last successfully fetched state.

[Diagram: Sequence — Messaging Real-Time Poll Cycle]

9.5 Account Creation (Signup) Flow

Participants: Signup state machine (useReducer), BskyAgent, AT Protocol PDS (com.atproto.server.createSession), hCaptcha (via CaptchaWeb / CaptchaWebView.web), useOnboardingDispatch

Sequence:

  1. User navigates to the Signup flow; SignupContext initializes with createInitialState.
  2. User completes StepInfo (email, password, date of birth, invite code).
  3. User completes StepHandle (handle selection with HandleSuggestions).
  4. If serviceDescription.phoneVerificationRequired is true, StepCaptcha is shown; CaptchaWeb (native) or CaptchaWebView.web (web) loads the hCaptcha challenge.
  5. On CAPTCHA completion, the redirect URL is intercepted; code is extracted and stored in pendingSubmit.
  6. useSubmitSignup fires createAccount via useSessionApi(), sending email, handle, password, birthDate, inviteCode, and verificationCode to the AT Protocol PDS.
  7. On success, onboardingDispatch({type: 'start'}) transitions the user to the Onboarding flow.
  8. On failure, the error is classified (invite code error → back to StepInfo; handle error → back to StepHandle; other → back to StepInfo).

Error path: Network errors surface a connectivity message. InvalidInviteCodeError surfaces a specific invite code message. Handle conflicts surface a handle-specific error.

[Diagram: Sequence — Account Creation (Signup) Flow]


Data Flow & State Management

10.1 Data Sources

Source Examples Access Pattern
AT Protocol AppView Feed posts, actor profiles, search results, trending topics, suggested feeds TanStack Query hooks → BskyAgent XRPC GET
AT Protocol PDS User preferences, saved feeds, notification settings, profile records TanStack Query hooks → BskyAgent XRPC GET/PUT
AT Protocol Chat Service Conversations, messages TanStack Query hooks → BskyAgent XRPC GET/POST
Device sensors / OS APIs Contacts (expo-contacts), camera, photo library, location Native module hooks
Push notifications APNs (iOS), FCM (Android) expo-notifications native module
Local device storage Language preferences, theme preferences, search history, dev mode flags useStorage hook (AsyncStorage-backed), persisted module
Native shared preferences Notification sound settings (ExpoBackgroundNotificationHandler) Custom native module (expo-bluesky-swiss-army)
Static bundled assets Onboarding images, splash screens, fonts require() resolved at build time by Metro/Webpack

10.2 State Management Approach

The application uses a layered state management approach:

Layer Technology Scope Examples
Server state TanStack Query v5 Global, cached Feed posts, profiles, preferences, conversations
Session/auth state React Context (SessionProvider) Global currentAccount, accounts, BskyAgent
Shell/UI state React Context (ShellProvider) Global minimalShellMode, colorMode, activeStarterPack
Preferences state React Context + persisted storage Global Language, theme, content filters, notification settings
Wizard/flow state useReducer + React Context Flow-scoped Signup state, Onboarding state, Starter Pack Wizard state
Screen-local state useState Screen-scoped isPTRing, showAccounts, isScrolledDown
Optimistic/shadow state Custom hooks (useProfileShadow) Component-scoped Follow state, like state before server confirmation
Animation state Reanimated SharedValue UI-thread scrollY, transX, iconScale

10.3 Navigation State as Data

Route params represent data flowing between screens. Key patterns observed:

10.4 Persistence Layer

Data Storage Mechanism Encryption Notes
Session tokens (access JWT, refresh JWT) (inferred from useSession hook — verify against #/state/session implementation) (not documented — verify) Managed by SessionProvider; likely Keychain (iOS) / Keystore (Android)
Language preferences useStorage hook (AsyncStorage-backed) None Keyed by account DID
Theme preferences useStorage hook or persisted module None Device-level
Search term history useStorage(account, [did, 'searchTermHistory']) None Up to 6 terms, keyed by account DID
Search account history useStorage(account, [did, 'searchAccountHistory']) None Up to 10 DIDs, keyed by account DID
Notification sound settings ExpoBackgroundNotificationHandler native module None Platform-native key-value (UserDefaults/SharedPreferences)
Dev mode flag useStorage(device, [...]) None Device-level
Policy update NUX state device.set([PolicyUpdate202508], ...) None Device-level storage
Reminders (email confirm snooze) persisted.write('reminders', ...) None AsyncStorage-backed
OTA update channel useApplyPullRequestOTAUpdate None iOS only, dev builds

[Not documented — WHO: Platform/security engineer; WHAT: What storage mechanism is used for session tokens (access JWT, refresh JWT)? Is it Keychain on iOS and Keystore on Android, or AsyncStorage? Is it encrypted?; WHERE: Section 10.4, Persistence Layer table, "Session tokens" row]

10.5 Data Transformation Pipeline

The standard pipeline for AT Protocol data:

AT Protocol XRPC response (JSON)
  → TanStack Query cache (keyed by RQKEY)
  → Custom hook (e.g., usePostFeedQuery, useProfileQuery)
  → useMemo transformation (flatten pages, filter, deduplicate)
  → moderateProfile() / moderation.ui() (apply content moderation)
  → sanitizeDisplayName() / sanitizeHandle() (sanitize for display)
  → Screen component renders

For feed content specifically:

usePostFeedQuery → data.pages → feedItems (useMemo: flatten + interstitial insertion + author filter)
  → List → renderItem → PostFeedItem → RichText + embedded media

For notification settings:

useNotificationSettingsQuery → preferences → PreferenceControls
  → channels (useMemo from preference.list + preference.push)
  → Toggle.Group renders
  → user interaction → useNotificationSettingsUpdateMutation → API write

10.6 Cross-Screen Data Sharing

Mechanism Used For Examples
TanStack Query cache Shared server state across screens Profile data shared between Profile header and Settings; feed data shared between Feed and ProfileSections/Feed
React Context (global) Auth, shell, preferences useSession, useSetMinimalShellMode, useLanguagePrefs
Navigation params Screen-to-screen data transfer Post URI, profile handle, conversation ID, search query
useProfileShadow Optimistic profile state Follow state reflected immediately in ChatList, ProfileHeader, and ActivityNotificationSettings
precacheProfile / precacheConvoQuery Cache warming before navigation ChatList pre-populates profile and conversation cache before navigating to Conversation screen
unstableCacheProfileView Manual cache write Search screen writes profile to cache on profile click
queryClient.invalidateQueries / resetQueries Cross-screen cache invalidation Profile update invalidates POST_FEED_RQKEY_ROOT and postThreadQueryKeyRoot; interest change resets 5 recommendation queries

Cross-Cutting Concerns

11.1 Safe-Area Handling

Safe-area handling is implemented consistently across the application using react-native-safe-area-context. The useSafeAreaInsets() hook is used in:

The Layout.Screen component (used as the root wrapper on virtually all screens) is expected to handle safe area insets system-wide. The react-native-edge-to-edge plugin is configured in app.config.js with enforceNavigationBarContrast: false for Android, enabling edge-to-edge rendering.

11.2 Keyboard Handling

Keyboard handling is not fully consistent across the application:

[Not documented — WHO: React Native developer; WHAT: Is there a global KeyboardAvoidingView wrapper at the navigator level, or is keyboard handling entirely per-screen? WHERE: Section 11.2, Keyboard Handling]

11.3 Gesture Handling System

react-native-gesture-handler is used for swipe gestures in the messaging screens (GestureActionView in ChatList and Inbox) and for the DraggableScroll horizontal scroll component. The GestureDetector and Gesture APIs from react-native-gesture-handler are used in GestureAction.

The webpack configuration explicitly aliases react-native-gesture-handler to false on web ('react-native-gesture-handler': false), causing a build error if it is accidentally imported in web-only code. This is a deliberate architectural guard.

[Not documented — WHO: React Native developer; WHAT: Is GestureHandlerRootView wrapping the root navigator? If not, swipe gestures in GestureActionView may not function correctly on Android.; WHERE: Section 11.3, Gesture Handling System, and Section 14, Risks & Technical Debt]

11.4 Platform-Branching Consistency

Platform branching is not fully consistent across the codebase — multiple strategies coexist (see Section 7.2). This is an architectural smell but appears to be a deliberate pragmatic choice given the codebase's scale.

The most consistent pattern is the use of IS_* constants from #/env for behavioral branching and .web.tsx file splits for complete platform-specific implementations. The platform() / native() / web() utilities from #/alf are used for style-level branching.

Inconsistencies observed:

11.5 Error Handling Strategy

Error handling is per-screen and per-mutation rather than centralized:

No global React Error Boundary is observed in the assessed screens. Error boundaries, if present, are at the navigator or provider level.

11.6 Authentication & Authorization

Authentication is enforced at multiple layers:

  1. Navigator level: The root navigator gates the Main navigator behind session state (currentAccount must be defined and not deactivated/takendown).
  2. Hook level: useSession() provides currentAccount; components return null or redirect if no account is present.
  3. Action level: useRequireAuth() wraps mutating actions (e.g., follow, subscribe) to redirect unauthenticated users to sign-in.
  4. API level: The BskyAgent attaches the access JWT to all XRPC requests; the AT Protocol PDS rejects unauthenticated requests.

The access JWT is managed by BskyAgent and refreshed automatically. The resumeSession function is called after account activation and handle changes to refresh the in-memory session.

App Password restriction: Several operations (account deactivation, handle changes) require a full-privilege session token, not an App Password. The server returns 'Bad token scope' for App Password sessions; the client surfaces this as a specific error message.

11.7 Loading & Empty States

Loading and empty states are handled consistently through a set of shared components:

The pattern of {data && (...)} guards (rendering nothing until data loads) is used in header subtitle areas (e.g., PostLikedBy, PostRepostedBy, ProfileFollowers).

11.8 Logging & Observability

11.9 Performance Patterns

Pattern Usage
FlatList virtualization List component (wrapping Animated.FlatList) used in all feed, search, and list screens
windowSize tuning windowSize={9} (Feed), windowSize={11} (ChatList, Explore), windowSize={platform({android: 11})} (Explore)
maxToRenderPerBatch tuning maxToRenderPerBatch={5} (iOS Feed), maxToRenderPerBatch={1} (Android Feed), maxToRenderPerBatch={platform({android: 10, ios: 20})} (Explore)
removeClippedSubviews removeClippedSubviews={true} in Feed
useInitialNumToRender Custom hook calculating initial render count from screen height; used in Feed, ChatList, Hashtag, KnownFollowers
useMemo for derived data Extensively used for flattening paginated data, computing feedItems, channels, items, profiles
useCallback for handlers Extensively used for onRefresh, onEndReached, renderItem, onPress
React.memo Applied to ChatListItem, PostFeed, List, SearchScreenInner, SuggestedProfileCard
Reanimated UI-thread worklets All scroll-driven animations (banner parallax, avatar scale, status bar shadow) run on the UI thread
TanStack Query caching All server state cached with RQKEY-based keys; staleTime and gcTime configured per query hook
Prefetching shouldPrefetch = IS_NATIVE && isPageAdjacent enables feed query on adjacent tabs; precacheProfile / precacheConvoQuery warm cache before navigation
FlashList Not observed in assessed screens; FlatList (via List) is the primary virtualization primitive
React Compiler babel-plugin-react-compiler with target: '19' is configured globally; provides automatic memoization

11.10 Missing or Not Observed

The following cross-cutting concerns are absent or not observed in the assessed documentation and are architecturally significant:


Architecture Decisions

AD-1: AT Protocol as the Sole Data Layer

AD-2: Expo Bare Workflow with EAS Build

AD-3: TanStack Query for All Server State

AD-4: React Navigation (Stack + Tab) over Expo Router

AD-5: Lingui for Internationalization

AD-6: React Compiler (Babel Plugin) for Automatic Memoization

AD-7: Reanimated Worklets for Scroll-Driven Animation

AD-8: OTA Updates via EAS Updates with Code Signing

AD-9: New Architecture Disabled

AD-10: Platform-Specific File Splits for Web Stubs

AD-11: Custom Native Modules via Expo Modules API

AD-12: Sentry for Crash Reporting (Conditional)


Quality Attributes

Quality Attribute Current State Evidence Risk Level
Performance Good — FlatList virtualization, Reanimated UI-thread animations, TanStack Query caching, React Compiler windowSize, maxToRenderPerBatch, removeClippedSubviews in Feed; useAnimatedStyle worklets in ProfileHeaderShell; RQKEY-based caching throughout Low
Maintainability Moderate — consistent patterns (TanStack Query hooks, #/alf design system, Lingui) but multiple coexisting platform-branching strategies IS_* constants, .web.tsx splits, platform() utility all used for branching; 131 screens assessed with consistent hook patterns Medium
Security Moderate — AT Protocol JWT auth, code-signed OTA updates, sanitizeDisplayName/sanitizeHandle, cleanError for error message sanitization; session token storage mechanism not documented SENTRY_AUTH_TOKEN-conditional Sentry; RSA code signing for OTA; validWebLink() in Policies screen; stateParam CSRF check in CaptchaWeb Medium
Reliability Moderate — per-screen error handling, isNetworkError detection, TanStack Query retry; no global error boundary observed detectKnownError in PostFeedErrorMessage; logger.error throughout; cleanError for user-facing messages Medium
Portability Good — iOS, Android, and web served from single codebase; .web.tsx stubs for native-only features IS_NATIVE, IS_WEB, IS_IOS, IS_ANDROID constants; .web.tsx file splits; webpack alias for react-native-gesture-handler: false on web Low
Usability Good — consistent Layout.* shell, SettingsList.* patterns, Toggle.* controls; 40+ locale support; haptic feedback; safe area handling useSafeAreaInsets in multiple screens; useHaptics in profile and settings; Lingui with 40+ locales; useReducedMotion in animations Low
Internationalization Good — Lingui with 40+ locales; msg macro for extraction; Trans/Plural for rendering; languageName() for locale-aware language display lingui.config.ts defines 40+ locales; bsky-internal/lingui-msg-rule ESLint rule; Plural used in header subtitles Low
Testability Low — no test infrastructure observed in assessed screens; E2E test screen (SharedPreferencesTester) exists but test coverage is not documented testID props on key UI elements (e.g., testID="messagesInboxScreen", testID="nextBtn", testID="saveChangesBtn"); SharedPreferencesTester E2E screen High

Risks & Technical Debt

ID Category Description Severity Recommendation
R-1 Risk GestureHandlerRootView wrapper not confirmed — if the root navigator is not wrapped in GestureHandlerRootView, swipe gestures in GestureActionView (ChatList, Inbox) may silently fail on Android High Verify the root component wraps the navigator in GestureHandlerRootView; add to CI checks
R-2 Risk No global React Error Boundary observed — unhandled render errors in any screen could crash the entire navigator subtree High Add a global error boundary at the root navigator level with a fallback UI and Sentry error reporting
R-3 Risk Session token storage mechanism not documented — if tokens are stored in AsyncStorage (unencrypted) rather than Keychain/Keystore, they are vulnerable to extraction on rooted/jailbroken devices High Verify session token storage in #/state/session; migrate to Keychain (iOS) / Keystore (Android) if not already using them
R-4 Tech Debt SetError action defined in Starter Pack Wizard Action type but has no corresponding case in the reducer switch — dispatching {type: 'SetError'} has no effect Medium Add the SetError case to the reducer or remove the action type from the union
R-5 Tech Debt likeUri variable name in ProfileHeaderLabeler holds the repost count — naming inconsistency between variable name and actual data Low Rename to repostCount or quoteCount as appropriate
R-6 Risk Web stubs that throw (StepFindContactsIntro/index.web.tsx, StepFindContacts/index.web.tsx, AppIconSettings/index.web.tsx) will cause React render errors if reached on web without an error boundary Medium Ensure error boundaries are in place above these screens on web; or redirect before rendering
R-7 Tech Debt No client-side rate limiting or debouncing on notification settings toggles — rapid toggling across 10+ notification settings screens could generate many sequential API calls Low Add debouncing (e.g., 500ms) to useNotificationSettingsUpdateMutation calls in PreferenceControls
R-8 Tech Debt unstableCacheProfileView in Search screen is marked "unstable" — may bypass normal cache validation Low Investigate and stabilize or replace with a standard TanStack Query cache write
R-9 Risk MAX_LABELERS limit hardcoded as "twenty" in CantSubscribePrompt text — must be manually kept in sync with the MAX_LABELERS constant Medium Replace hardcoded string with a dynamic reference to the MAX_LABELERS constant
R-10 Tech Debt // TODO remove double login comment in LoginForm — suggests a redundant session creation call that could cause double session creation Medium Investigate and remove the redundant login call
R-11 Tech Debt onDismiss error in FindContactsSettings uses "Failed to follow all matches" error message text for a dismiss action — copy-paste error Low Fix the error message to accurately describe the dismiss action failure
R-12 Risk PlaceholderCanvas renders a 750×750px off-screen view at all times while mounted — on lower-end devices this consumes GPU/memory resources Low Unmount PlaceholderCanvas when not needed (e.g., after avatar capture completes)
R-13 Tech Debt SuggestedFollows component returns null on Android due to scroll interaction conflicts — this is a known platform limitation without a documented resolution path Medium Document the Android limitation; investigate whether react-native-gesture-handler configuration changes could resolve the conflict
R-14 Risk Log screen (/log) has no authentication or authorization enforcement — any user who navigates to /sys/log can see the full system log including potentially sensitive runtime data High Gate the Log screen behind IS_INTERNAL or an authenticated admin role check; ensure the logger does not capture sensitive data (passwords, tokens)
R-15 Tech Debt NewArchEnabled: false — the application is not using React Native's New Architecture (Fabric, Turbo Modules, JSI) Medium Plan migration to New Architecture as third-party library support matures; track compatibility of key dependencies
R-16 Risk VideoFeed/index.web.tsx returns null — the video feed feature is not implemented on web, but the route may be accessible Low Verify that the video feed route is not exposed on web, or add a proper "not available" UI
R-17 Tech Debt Multiple coexisting platform-branching strategies (IS_* constants, .web.tsx splits, platform() utility, Platform.OS checks) — inconsistency increases cognitive load for developers Medium Establish and document a single preferred platform-branching strategy; migrate inconsistent usages over time
R-18 Risk ParamList typing — CommonNavigatorParams is used for type-safe route params, but not all screens may have fully typed params (e.g., @ts-ignore or any casts) Medium Audit CommonNavigatorParams for completeness; ensure all route params are typed

Glossary

15.1 Domain Terms

Term Definition
AT Protocol Authenticated Transfer Protocol — a decentralized social networking protocol developed by Bluesky. All data operations in this application are AT Protocol XRPC calls.
PDS (Personal Data Server) The AT Protocol server that stores a user's data (posts, follows, profile, preferences). Each user's data lives on their PDS.
AppView The Bluesky-operated AT Protocol service that aggregates and indexes data from across the network, providing search, feed generation, and actor lookup.
DID (Decentralized Identifier) A stable, globally unique identifier for an AT Protocol account (e.g., did:plc:abc123). DIDs do not change when a user changes their handle.
Handle A human-readable username in the AT Protocol (e.g., alice.bsky.social). Handles are mutable; DIDs are stable.
XRPC Cross-platform Remote Procedure Call — the HTTP-based RPC protocol used by the AT Protocol. Methods are defined by Lexicons.
Lexicon An AT Protocol schema definition that specifies the shape of records, queries, and procedures (e.g., app.bsky.feed.post, com.atproto.server.createSession).
Feed Generator A third-party server that implements the AT Protocol feed generator interface, providing custom algorithmic feeds.
Labeler An AT Protocol service that applies content labels to posts and profiles for moderation purposes.
Starter Pack An AT Protocol record that bundles a list of suggested accounts and feeds for new users to follow.
Convo A conversation in the Bluesky chat system, identified by a convoId.
Facet An AT Protocol rich text annotation (link, mention, hashtag) applied to a span of text in a post.
Shadow (profile) A locally-cached optimistic copy of a profile that reflects in-flight mutations (e.g., follow state) before server confirmation.
App Clip An iOS feature that allows users to experience a small part of an app without installing it. Bluesky uses an App Clip for starter pack landing pages.
Ozone The Bluesky moderation service that processes moderation reports and appeals.
Germ DM A third-party direct messaging service integrated into the Bluesky profile header.
NUX New User Experience — an onboarding prompt or card shown to users who have not yet completed a specific onboarding step.
TID Timestamp ID — a time-ordered, lexicographically sortable identifier used as record keys in AT Protocol repositories.
CAR file Content Addressable aRchive — the binary format used to export an AT Protocol repository.
JSONL JSON Lines — a format where each line is a valid JSON object, used for chat data export.

15.2 Architecture Terms

Term Definition
Navigator A React Navigation component that manages a set of screens and their transitions (e.g., Stack navigator, Tab navigator, Drawer navigator).
ParamList A TypeScript type that maps route keys to their parameter shapes, enabling type-safe navigation in React Navigation (e.g., CommonNavigatorParams).
Route key The string identifier for a screen within a navigator (e.g., 'MessagesInbox', 'NotificationSettings').
Stack navigator A React Navigation navigator that presents screens in a stack, with push/pop transitions.
Tab navigator A React Navigation navigator that presents screens as tabs with a tab bar.
Deep link A URL that navigates directly to a specific screen within the application (e.g., https://bsky.app/profile/alice.bsky.social).
Native module A module that bridges JavaScript to platform-native code (Swift/Objective-C on iOS, Kotlin/Java on Android).
Platform branching The practice of writing different code paths for different platforms (iOS, Android, web) within a single codebase.
Metro bundler The JavaScript bundler used by React Native for native builds. Configured in metro.config.js.
Hermes The JavaScript engine used by React Native (and this application). Provides faster startup and lower memory usage than V8.
JSI JavaScript Interface — the C++ layer that enables direct communication between JavaScript and native code without the Bridge. Not yet enabled in this application (newArchEnabled: false).
Fabric React Native's new rendering system (part of the New Architecture). Not yet enabled in this application.
Turbo Module React Native's new native module system (part of the New Architecture). Not yet enabled in this application.
Managed workflow An Expo workflow where Expo manages the native project files. This application uses the bare workflow instead.
Bare workflow An Expo workflow where the developer has full access to the native iOS and Android project files. Used by this application.
EAS Build Expo Application Services Build — the cloud build service used to compile iOS and Android binaries.
EAS Updates Expo Application Services Updates — the OTA update service used to deliver JavaScript bundle updates.
Worklet A Reanimated function that runs on the native UI thread rather than the JavaScript thread, enabling smooth animations.
SharedValue A Reanimated value that can be read and written from both the JavaScript thread and the UI thread. Used for scroll position and animation state.
RQKEY React Query Key — a function that generates a TanStack Query cache key for a specific query (e.g., RQKEY(feed, feedParams)).
FeedDescriptor A string format used by usePostFeedQuery to identify which feed to load (e.g., "following", `"feedgen
#/alf The internal design system module providing atomic style tokens, theme hooks, and platform utilities. Referenced via the #/ TypeScript path alias.
Portal A React pattern (used in SettingsList.Group) that renders children into a different part of the component tree, enabling flexible layout composition.
Mutation queue A pattern (used in useProfileFollowMutationQueue) that serializes rapid user interactions to prevent race conditions in AT Protocol mutations.
IS_NATIVE / IS_WEB / IS_IOS / IS_ANDROID Build-time boolean constants from #/env that indicate the current platform. Used for platform branching throughout the codebase.
cleanError A utility function from #/lib/strings/errors that sanitizes raw error objects into user-displayable strings.
sanitizeDisplayName A utility function that applies AT Protocol moderation decisions to a display name before rendering.
makeRecordUri A utility function from #/lib/strings/url-helpers that constructs an AT Protocol at:// URI from a handle/DID, collection namespace, and record key.