Skip to main content

Front-to-Back System Topology

Last verified from repository code on July 21, 2026. Use this page when Support needs to identify which system owns a symptom, which logs or tables to inspect first, and which client path is involved.
The current target architecture makes Supabase the source of truth. The NestJS API still exists in the repository, but the latest migrations decommission legacy pull-sync jobs and route dashboard writes directly to Supabase.

Runtime Map

Component Responsibilities

Critical URLs and Identifiers

Source of Truth

Supabase Postgres is the primary data store for production flows. The migration 20260612010000_dashboard_native.sql moved dashboard-owned data into Supabase by adding dashboard user fields, creating devotion_categories, creating the public devotions storage bucket, adding devotions.notifiedAt, and scheduling devotion-release-notify every 15 minutes. The migration 20260615000000_decommission_legacy_api.sql unschedules legacy pull-sync jobs, including daily-devotions-sync, daily-devotions-sync-5am, daily-devotions-sync-730pm, daily-devotions-sync-morning, daily-devotions-sync-evening, and devotion-release-scheduler.

Primary Data Stores

Authentication Model

There are multiple auth surfaces in the repository. Support should verify the caller before assuming a token format.
If a request returns 401 and no function log appears, check Supabase gateway verify_jwt settings in supabase/config.toml and the token type the client is sending. Some functions rely on custom app tokens while others expect Supabase Auth tokens or the anon key.

API Surface Inventory

End-to-End Flows

1. Dashboard Login and Publishing

Support checks:
  • Confirm the dashboard token belongs to a users row with role SYSTEM_ADMIN or CONTENT_CREATOR.
  • Confirm the user status is ACTIVE.
  • Confirm uploaded media exists under the public devotions storage bucket.
  • For notification issues after publish, inspect devotions.notifiedAt, device_tokens, Firebase secrets, and dashboard Edge Function logs.

2. Scheduled Devotion Release Notifications

Support checks:
  • devotion-release-notify only accepts Authorization: Bearer <SUPABASE_SERVICE_ROLE_KEY>.
  • The cron job name is devotion-release-notify and should run every 15 minutes.
  • A devotion with notifiedAt already set will not send again.
  • FCM failures usually point to missing Firebase secrets, invalid device tokens, or no rows in device_tokens.

3. Mobile Feed Loading

Support checks:
  • Standard users only see status='PUBLISHED' devotions where releaseDate is null or in the past.
  • Content creators only see their own devotions in devotions-paginated.
  • If a user still sees old content during an outage, the app may be showing Hive-cached devotions.
  • Category filters are server-side for devotions-paginated; a category table mismatch can make category names or filters look wrong.

4. Notes

Mobile notes use notes-fetch, notes-save, and notes-delete. Each function decodes the app token, extracts payload.id, and scopes database operations to notes.createdBy. Later migrations disabled notes RLS, so the function-level createdBy filter is the effective ownership control. Support checks:
  • Confirm the token decodes to the expected user id.
  • Confirm the row exists in notes with matching createdBy.
  • If only one note fails to delete or update, verify the id and owner, not just the endpoint.

5. Password Reset

Support checks:
  • Email reset requires a matching users.email, a row in email_otps, and a working RESEND_API_KEY.
  • SMS reset requires a matching users.phoneNumber and Twilio secrets: TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_VERIFY_SERVICE_SID.
  • Passwords are stored as SHA-256 hashes in Supabase-native functions.

6. Profile Updates and Photos

Profile text updates use the user function. Profile photo upload uses user-profile, uploads to Cloudinary, and writes the returned URL into users.profilePicture. Support checks:
  • Mobile sends X-User-ID for profile update/photo/delete flows.
  • Photo upload failures usually point to CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET, multipart parsing, or a missing profilePicture form field.
The website route /devotion/:devotionId currently does not query live Supabase data. DevotionRedirect.tsx uses a local mock object for known devotion details, then redirects to revelation14://devotion?id=:devotionId after a countdown. If the app is installed, the Flutter route handler fetches devotions, finds the matching id, selects that devotion, and opens playback/detail context. If the app is not installed, the website offers app store fallbacks. Support checks:
  • A web deep link can display mock details even if Supabase content has changed.
  • If the app opens but cannot find the devotion, confirm the devotion is returned by the mobile feed rules.
  • If the browser stays on the website, confirm OS app-link/deep-link configuration and whether the app is installed.

8. Version Checking

The mobile splash screen calls POST /version-check with current_version and platform. The current function contains hard-coded version values. The Flutter app manifest is at 4.0.3+35, while the function currently reports 3.3.4 as latest and 3.3.0 as minimum. Treat version prompt issues as backend configuration/code issues first.

Symptom Triage Matrix

Operational Checks

Use these checks before escalating across teams.
Devotions most relevant to feed and notification issues
User login and role checks
Notes owner check
Device token and notification checks
Scheduled release notification job
Edge Function smoke test

Required Secrets and Configuration

Known Integration Risks

Code Anchors