Application structure
apps/mobile uses Expo SDK 58, React Native and file-based Expo Router routes. Auth routes sit outside the protected tabs. Home, Todos and Profile demonstrate the shared API; Profile contains Sessions, passkeys, Organizations, notifications and optional subscriptions.
The native design layer centralizes colors, type, spacing and reusable controls. Hooks own mutations, auth transitions, dialogs and toasts. Keep route components focused on composition rather than duplicating transport policy.
src/ follows the backend's feature shape (ADR-0013): routes in src/app only return a screen, each feature lives in one flat src/features/<feature>/ folder (<feature>.screen.tsx, <feature>.hooks.ts, components and models), and cross-cutting code sits in src/platform/ (rpc, auth session, i18n, feedback, theme). Screens and components read data only through hooks. scripts/mobile-feature-boundaries.test.mjs checks the direction rules in pnpm test.
Backend and scope
EXPO_PUBLIC_SERVER_URL selects the backend, with localhost:3005 as the local fallback. A physical device needs a reachable laptop LAN address. Better Auth's Expo client owns credentials; request scope captures User, Session and Organization. Organization transitions cancel stale waits and hide stale query results, including an A-to-B-to-A transition.
RPC calls made in the same tick share one batch request (oRPC BatchLinkPlugin, answered by the Worker's BatchHandlerPlugin), so one session read and one database pool serve a screen's initial queries. A batch only joins calls captured under the same identity, revision and cookie; a scope change aborts it, and an UNAUTHORIZED sub-response inside the batch envelope signs the captured session out exactly like a whole-request 401.
Offline reading is scoped to the observed identity and Organization through src/platform/offline/ (openPrivateSnapshot per feature, one cache folder purged on sign-out). Cached data is not authorization, and a late response from an old identity must not clear or overwrite the new one.
Native capabilities
RevenueCat requires its SDK in the native build and both billing gates. Push boot registers the platform adapter where available, with enrollment, foreground presentation and tap authorization kept separate. Widgets are opt-in through configuration and a feature flag. Biometric app lock protects local presentation, not server permission.
Extend and verify
Use the shared API contracts, then expose behavior through a mobile hook and the existing design components. Test on a development build when adding native modules. TypeScript and Hermes export do not prove device permission dialogs, purchases, background push or store signing.
The Expo SDK compatibility matrix owns native versions. Do not bump native modules merely because npm has a newer release; rebuild the dev client when the native dependency set changes. See libraries and upgrade policy.