Skip to main content
The mobile app uses Expo Router files under apps/mobile/app. apps/mobile/src/stacks/main.tsx declares the root stack, while apps/mobile/app/_layout.tsx mounts providers, deep-link handling, AccessGate, and that stack.

Route groups

The tab bar exposes home, AI tools, activity, and profile. The AI tools tab links directly to /(modal)/ai-chat. The insights file exists but its tab has href: null.

Route constants

Import app route constants from the app-local path:
ROUTES currently defines public setup/auth routes, unlock, developer routes, onboarding routes, home/AI tools/activity, and modal routes. STACKS defines root screen names used by MainStack.
Keep constants synchronized with route files. ROUTES.GENERATOR points to /(app)/(tabs)/generator, but no generator.tsx exists. ROUTES.ONBOARDING_PUSH_PERMISSION_PROMPT points to /(onboarding)/steps/notification-permission, but the actual prompt is ROUTES.NOTIFICATION_PERMISSION_MODAL at /(modal)/notification-permission. The existing insights and tab profile routes do not have ROUTES entries.

AccessGate order

AccessGate reads useSegments(), usePathname(), auth state, per-user security setup, app-local onboarding state, and security lock state. After auth finishes loading, it evaluates these gates in order:
  1. An unauthenticated user may remain on public welcome, sign-in, sign-up, or forgot-password, in (dev), or in (unlock). Other routes redirect to ROUTES.WELCOME.
  2. An authenticated user without verified email redirects to ROUTES.VERIFY_EMAIL.
  3. A verified user without a PIN redirects to ROUTES.SETUP_PIN.
  4. A user who has not completed biometric setup redirects to ROUTES.SETUP_BIOMETRICS. Skipping biometrics still marks setup complete.
  5. An incomplete welcome step redirects to ROUTES.ONBOARDING_WELCOME.
  6. An incomplete push explanation redirects to ROUTES.ONBOARDING_PERMISSIONS.
  7. An incomplete system permission prompt redirects to ROUTES.NOTIFICATION_PERMISSION_MODAL.
  8. Completed users are moved out of onboarding or the permission modal to ROUTES.HOME.
  9. A lockable, locked session redirects to ROUTES.UNLOCK.
  10. Unknown authenticated locations redirect to ROUTES.HOME; app, modal, dev, onboarding, unlock, and root locations are recognized.
Locking requires flags.enableAppLock, authentication, a user ID, completed PIN setup, and a route outside public, onboarding, and unlock groups. Foreground and background delays come from amisiConfig.security. flags.enableOnboarding is not consulted by the current gate. The three app-local onboarding completion values always participate. AuthDeepLinkHandler subscribes to React Native Linking URL events and calls Linking.getInitialURL() for cold starts. It accepts only URLs whose query has mode=verifyEmail and a non-empty oobCode. It then calls applyActionCode(oobCode) and reloadUser(). A verified session navigates with router.replace('/'); otherwise it navigates to ROUTES.VERIFY_EMAIL. Parsing and application failures do not navigate. Firebase email links are configured in apps/mobile/app/_layout.tsx from EXPO_PUBLIC_FIREBASE_EMAIL_CONTINUE_URL and optional EXPO_PUBLIC_FIREBASE_AUTH_LINK_DOMAIN.

Lock and route restoration

useAppLockManager calls lockUserSession(userId, pathname) after foreground inactivity or a long-enough background interval. The store persists the pathname as that user’s pending unlock route. AccessGate also writes the current pathname before replacing a locked route with ROUTES.UNLOCK. The unlock screen calls unlockUserSession(userId) after a valid six-digit PIN or successful biometrics. On the next gate pass, AccessGate restores lockState.pendingRoute, consumes the persisted route, and falls back to ROUTES.HOME when no route exists. The root GestureHandlerRootView calls notifyAppInteraction on every touch start so active touches reset the foreground inactivity timer.