> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rev14ministries.com/llms.txt
> Use this file to discover all available pages before exploring further.

# System Topology

> Front-to-back support map for the Revelation 14 mobile app, dashboard, website, Supabase backend, and external services.

# 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.

<Info>
  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.
</Info>

## Runtime Map

```mermaid theme={null}
flowchart LR
    Mobile[Flutter mobile app] --> MobileFns[Supabase Edge Functions]
    Dashboard[Next.js dashboard] --> DashboardFn[dashboard Edge Function]
    Website[Vite public website] --> DeepLink[revelation14:// deep links]
    DeepLink --> Mobile

    MobileFns --> DB[(Supabase Postgres)]
    DashboardFn --> DB
    DashboardFn --> Storage[(Supabase Storage: devotions bucket)]
    MobileFns --> Storage

    DB --> Cron[pg_cron + pg_net]
    Cron --> NotifyFn[devotion-release-notify]
    NotifyFn --> FCM[Firebase Cloud Messaging]
    DashboardFn --> FCM
    Mobile --> FirebaseSDK[Firebase SDK]

    MobileFns --> Resend[Resend email]
    MobileFns --> Twilio[Twilio Verify SMS]
    MobileFns --> Cloudinary[Cloudinary profile media]

    LegacyNest[NestJS API - legacy/reference] -. historical routes .-> DB
```

## Component Responsibilities

| Component        | Repository                      | Runtime                                                 | Responsibilities                                                                                                                      | Primary dependencies                                                                          |
| ---------------- | ------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| Mobile app       | `revelation14-mobile-app`       | Flutter                                                 | User login, registration, devotions feed, audio playback, notes, favorites, profile updates, push handling, deep links, offline cache | Supabase Edge Functions, Hive, Firebase Messaging, Crashlytics, Analytics, PostHog, Shorebird |
| Dashboard        | `revelation14-dashboard`        | Next.js on port `4000` locally                          | Admin and content creator login, devotion CRUD, category CRUD, contributor management, stats                                          | `dashboard` Edge Function, Supabase Postgres, Supabase Storage                                |
| Public website   | `rev14ministry-website`         | Vite React                                              | Marketing pages, donation page, web-to-app devotion links                                                                             | Browser routing, `revelation14://` scheme, app store fallbacks                                |
| Supabase backend | `revelation14_backend/supabase` | Supabase Edge Functions, Postgres, Storage, pg\_cron    | API surface, source-of-truth data, storage bucket, scheduled release notifications                                                    | Supabase service role, vault secrets, Firebase service account, Resend, Twilio, Cloudinary    |
| NestJS API       | `revelation14_backend/src`      | NestJS, default `API_PREFIX=api/v1`, Swagger at `/docs` | Legacy/reference REST API and Swagger generation                                                                                      | PostgreSQL, TypeORM, AWS/GCS/Cloudinary, mail/SMS services                                    |

## Critical URLs and Identifiers

| Item                        | Value                                                             |
| --------------------------- | ----------------------------------------------------------------- |
| Supabase project ref        | `rzqklwfhwqmviintncqh`                                            |
| Supabase Functions base URL | `https://rzqklwfhwqmviintncqh.supabase.co/functions/v1`           |
| Dashboard API base URL      | `https://rzqklwfhwqmviintncqh.supabase.co/functions/v1/dashboard` |
| Public website              | `https://rev14ministries.com`                                     |
| App deep-link scheme        | `revelation14://`                                                 |
| Web devotion link pattern   | `https://rev14ministries.com/devotion/:devotionId`                |
| Devotion media bucket       | Supabase Storage bucket `devotions`, public                       |
| NestJS default API prefix   | `api/v1`                                                          |

## 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

| Store                      | Purpose                                         | Key support fields                                                                                         |
| -------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `users`                    | App users, dashboard admins, content creators   | `id`, `email`, `phoneNumber`, `role`, `status`, `isVerified`, `isFirstLogin`, `password`, `profilePicture` |
| `devotions`                | Devotion content shown in dashboard and mobile  | `id`, `title`, `status`, `releaseDate`, `category`, `createdBy`, `attachments`, `coverImage`, `notifiedAt` |
| `devotion_categories`      | Dashboard-managed categories                    | `id`, `categoryName`, `createdBy`                                                                          |
| `devotionCategories`       | Legacy/mobile category function target          | `id`, `categoryName`                                                                                       |
| `notes`                    | User notes                                      | `id`, `title`, `content`, `status`, `createdBy`, `createdAt`, `updatedAt`                                  |
| `email_otps`               | Email verification and password reset codes     | `email`, `code`, `expires_at`                                                                              |
| `device_tokens`            | FCM device tokens                               | `user_id`, `device_id`, `token`, `platform`, `is_active`, `last_used_at`                                   |
| `notification_logs`        | Older `send-notification` audit trail           | `user_id`, `device_token_id`, `devotion_id`, `status`, `error_message`, `sent_at`                          |
| Storage bucket `devotions` | Dashboard-uploaded cover images and audio files | `covers/...`, `audio/...` public URLs                                                                      |

## Authentication Model

There are multiple auth surfaces in the repository. Support should verify the caller before assuming a token format.

| Caller                     | Current route in code                                         | Token behavior                                                | Notes                                                                                                                                                     |
| -------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Dashboard                  | `POST /dashboard/user/dashboard/login`                        | HMAC-SHA256 signed JWT with `Bearer ` prefix, 7-day expiry    | Allows `SYSTEM_ADMIN` and `CONTENT_CREATOR` only. Rejects suspended users.                                                                                |
| Mobile app                 | `POST /user-login` and `POST /user-register`                  | Simple base64 JSON token, no signature                        | Current mobile service code calls these functions. `user-login` can upgrade legacy argon2 password hashes to SHA-256.                                     |
| Consolidated auth function | `POST /auth/login`, `/auth/register`, `/auth/create-password` | HMAC-SHA256 signed JWT with `Bearer ` prefix                  | Present in the backend and referenced by existing docs, but not the primary mobile client path in the current Flutter service code.                       |
| Notes and devotions feed   | `notes-*`, `devotions-paginated`                              | Decodes both signed JWT payloads and simple base64 app tokens | These functions extract `payload.id` and then query with the service role.                                                                                |
| Device tokens              | `device-tokens`                                               | Uses `supabase.auth.getUser()`                                | This expects a Supabase Auth JWT. Current mobile login tokens are app-owned tokens, so token registration can fail with 401 unless deployed code differs. |

<Warning>
  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.
</Warning>

## API Surface Inventory

| Function or route                | Main callers                              | Purpose                                               | Data and side effects                                                                                                                      |
| -------------------------------- | ----------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `dashboard`                      | Next.js dashboard                         | Mirrors legacy REST paths under one Edge Function     | Reads/writes `users`, `devotions`, `devotion_categories`; uploads media to `devotions` bucket; may send immediate FCM on published release |
| `auth`                           | Docs/newer clients                        | Login, register, create password                      | Reads/writes `users`, `email_otps`; issues signed app JWT                                                                                  |
| `user-login`                     | Mobile app                                | Mobile login                                          | Reads `users`; validates SHA-256 or legacy argon2; returns base64 app token                                                                |
| `user-register`                  | Mobile app                                | Mobile registration                                   | Inserts `users`; auto-verifies new users; returns base64 app token                                                                         |
| `password-reset/request`         | Mobile app, dashboard reset flow          | Email or SMS reset request                            | Reads `users`; writes `email_otps`; sends email through Resend or SMS through Twilio Verify                                                |
| `password-reset/verify`          | Mobile app                                | Verify OTP and reset password                         | Verifies `email_otps` or Twilio code; updates `users.password`                                                                             |
| `user`                           | Mobile app profile/account flows          | Verify account, update profile, delete profile        | Reads/writes `users`; deletes user notes on profile delete                                                                                 |
| `user-profile`                   | Mobile app profile photo flow             | Upload profile photo                                  | Uploads image to Cloudinary and writes `users.profilePicture`                                                                              |
| `devotions-paginated`            | Mobile app feed                           | Paginated devotion feed                               | Reads `devotions`; filters by role, `status`, `releaseDate`, date range, category                                                          |
| `devotion-categories`            | Mobile category provider                  | Category list                                         | Reads `devotionCategories`; this differs from dashboard `devotion_categories`                                                              |
| `notes-fetch`                    | Mobile notes                              | Fetch notes                                           | Reads `notes` where `createdBy` equals token user id                                                                                       |
| `notes-save`                     | Mobile notes                              | Create or update notes                                | Inserts/updates `notes` where `createdBy` equals token user id                                                                             |
| `notes-delete`                   | Mobile notes                              | Delete notes                                          | Deletes `notes` where `createdBy` equals token user id                                                                                     |
| `notes-create`                   | Older docs/alternate note flow            | Create note                                           | Inserts `notes`; not the main mobile path currently observed                                                                               |
| `device-tokens`                  | Mobile login and token refresh            | Register, list, delete FCM tokens                     | Reads/writes `device_tokens`; current implementation expects Supabase Auth user                                                            |
| `devotion-release-notify`        | pg\_cron                                  | Scheduled release notification processor              | Reads `devotions`; marks `notifiedAt`; sends broadcast FCM to `device_tokens`; removes invalid tokens                                      |
| `send-notification`              | Older scheduler/manual notification paths | User-targeted push notification                       | Reads `device_tokens`; sends FCM; writes `notification_logs`                                                                               |
| `schedule-release-notifications` | Older scheduler                           | Looks for devotions releasing in a narrow time window | Calls `send-notification`; superseded by `devotion-release-notify` in the latest migration                                                 |
| `version-check`                  | Mobile splash screen                      | App update prompt                                     | Hard-coded latest/minimum versions and store URLs                                                                                          |

## End-to-End Flows

### 1. Dashboard Login and Publishing

```mermaid theme={null}
sequenceDiagram
    participant Admin as Dashboard user
    participant Web as Next.js dashboard
    participant Fn as dashboard Edge Function
    participant DB as Supabase Postgres
    participant Storage as Supabase Storage
    participant FCM as Firebase Cloud Messaging

    Admin->>Web: Login
    Web->>Fn: POST /user/dashboard/login
    Fn->>DB: Read users by email
    Fn-->>Web: Dashboard JWT
    Admin->>Web: Create or update devotion
    Web->>Fn: POST /post/create or PUT /post/update/:id
    Fn->>Storage: Upload cover/audio
    Fn->>DB: Insert or update devotions
    alt Published and releaseDate has passed
        Fn->>DB: Set notifiedAt
        Fn->>FCM: Send new devotion push
    end
```

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

```mermaid theme={null}
sequenceDiagram
    participant Cron as pg_cron
    participant Fn as devotion-release-notify
    participant DB as Supabase Postgres
    participant FCM as Firebase Cloud Messaging
    participant App as Mobile app

    Cron->>Fn: POST every 15 minutes with service role key
    Fn->>DB: Select PUBLISHED devotions where releaseDate has passed and notifiedAt is null
    Fn->>DB: Set notifiedAt before sending
    Fn->>DB: Read device_tokens
    Fn->>FCM: Send push notifications
    FCM->>App: Push with type=devotion_release and devotion_id
    App->>App: Navigate to /devotion/:id on tap
```

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

```mermaid theme={null}
sequenceDiagram
    participant App as Flutter app
    participant Cache as Hive cache
    participant Fn as devotions-paginated
    participant DB as Supabase Postgres

    App->>Cache: Load cached devotions when no filter is active
    App->>Fn: GET /devotions-paginated?page=1&limit=20
    Fn->>DB: Read devotions with role/date/category filters
    Fn-->>App: Devotions + pagination
    App->>Cache: Cache unfiltered pages
    alt Function or network fails
        App->>Fn: Fallback GET /post/all
        App->>Cache: Fallback to cached devotions if needed
    end
```

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

```mermaid theme={null}
flowchart LR
    App[Mobile or dashboard] --> Request[password-reset/request]
    Request --> Users[(users)]
    Request --> EmailOtps[(email_otps)]
    Request --> Resend[Resend email]
    Request --> Twilio[Twilio Verify SMS]
    App --> Verify[password-reset/verify]
    Verify --> EmailOtps
    Verify --> Twilio
    Verify --> Users
```

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.

### 7. Public Website Deep Links

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

| Symptom                                | First places to check                                                                              | Likely owner                    |
| -------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------- |
| Dashboard login fails                  | `users.email`, `users.password`, `role`, `status`, `isVerified`, `dashboard` logs                  | Supabase backend/dashboard      |
| Dashboard list is empty                | `dashboard` logs, `devotions.createdBy`, dashboard token role                                      | Supabase backend                |
| Dashboard publish or update fails      | `dashboard` logs, `devotions` bucket, multipart field names, file size                             | Supabase backend/dashboard      |
| Mobile feed empty                      | `devotions-paginated` logs, token payload, `devotions.status`, `releaseDate`, category filter      | Supabase backend/mobile         |
| Mobile shows stale devotions           | Hive cache, network errors, fallback to `/post/all`, app refresh behavior                          | Mobile app                      |
| Push notifications not received        | Device permission, `device_tokens`, `notifiedAt`, `devotion-release-notify` logs, Firebase secrets | Mobile/Supabase/Firebase        |
| Device token registration fails        | `device-tokens` logs, token type mismatch, Supabase Auth expectation                               | Supabase backend/mobile         |
| Password reset email missing           | `email_otps`, `password-reset` logs, Resend key/from address/domain                                | Supabase backend/Resend         |
| SMS reset missing                      | `users.phoneNumber`, Twilio Verify service logs/secrets                                            | Supabase backend/Twilio         |
| Notes missing                          | Token user id, `notes.createdBy`, `notes-fetch` logs                                               | Supabase backend/mobile         |
| Deep link opens wrong or stale details | Website mock data, app route handler, feed visibility of devotion id                               | Website/mobile                  |
| Update prompt wrong                    | `version-check` hard-coded config                                                                  | Supabase backend/mobile release |
| 401 before Edge Function logs          | `verify_jwt` setting, anon key, token type                                                         | Supabase configuration          |

## Operational Checks

Use these checks before escalating across teams.

```sql Devotions most relevant to feed and notification issues theme={null}
select
  id,
  title,
  status,
  "releaseDate",
  "notifiedAt",
  "createdBy",
  category
from devotions
order by "releaseDate" desc nulls last
limit 20;
```

```sql User login and role checks theme={null}
select
  id,
  email,
  "phoneNumber",
  role,
  status,
  "isVerified",
  "isFirstLogin"
from users
where lower(email) = lower('<email>');
```

```sql Notes owner check theme={null}
select id, title, "createdBy", "createdAt", "updatedAt"
from notes
where "createdBy" = '<user-id>'
order by "updatedAt" desc
limit 20;
```

```sql Device token and notification checks theme={null}
select id, user_id, platform, is_active, last_used_at
from device_tokens
where user_id = '<user-id>';
```

```sql Scheduled release notification job theme={null}
select jobid, jobname, schedule, active
from cron.job
where jobname = 'devotion-release-notify';
```

```bash Edge Function smoke test theme={null}
curl -i "https://rzqklwfhwqmviintncqh.supabase.co/functions/v1/devotions-paginated?page=1&limit=1" \
  -H "Authorization: Bearer <app-token>" \
  -H "apikey: <supabase-anon-key>"
```

## Required Secrets and Configuration

| Area               | Required values                                                                     |
| ------------------ | ----------------------------------------------------------------------------------- |
| Supabase functions | `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, anon key for public callers            |
| Cron HTTP calls    | Vault secrets `supabase_url` and `supabase_service_role_key`                        |
| Dashboard tokens   | `DASHBOARD_JWT_SECRET` or service-role fallback                                     |
| Firebase push      | `FIREBASE_PROJECT_ID`, `FIREBASE_CLIENT_EMAIL`, `FIREBASE_PRIVATE_KEY`              |
| Email reset        | `RESEND_API_KEY`, valid sender domain/address                                       |
| SMS reset          | `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_VERIFY_SERVICE_SID`              |
| Profile images     | `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, `CLOUDINARY_API_SECRET`              |
| Dashboard frontend | `NEXT_PUBLIC_SUPABASE_URL`, optional Cloudinary public upload envs                  |
| Mobile app         | Supabase URL and anon key are currently compiled into service code                  |
| Legacy NestJS      | `PG*` or `DATABASE_*`, `APP_PORT`/`PORT`, `API_PREFIX`, storage/mail/SMS/OAuth envs |

## Known Integration Risks

| Risk                                                                                   | Support impact                                                                                                                                       |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Multiple mobile auth functions exist (`user-login`/`user-register` and `auth`)         | Existing docs and current mobile code may reference different routes and token formats. Always check the client service file for the reported build. |
| Mobile tokens and dashboard tokens differ                                              | Some functions decode both token styles; others do not. A 401 can be a token-format issue rather than bad credentials.                               |
| `device-tokens` expects Supabase Auth while mobile sends app tokens                    | Push registration can fail even after successful app login. Check logs for `Unauthorized` from `supabase.auth.getUser()`.                            |
| Category table names differ                                                            | Dashboard uses `devotion_categories`; `devotion-categories` reads `devotionCategories`. Category incidents should verify both tables.                |
| Website devotion details are mocked                                                    | A website deep-link preview can be stale or missing even when the app/backend data is correct.                                                       |
| `version-check` is hard-coded and appears stale relative to the mobile package version | App update prompts may not reflect store releases until the function is updated and redeployed.                                                      |
| Legacy NestJS API still exists in repo                                                 | Do not assume NestJS is production source of truth unless DNS/deployment confirms requests still route there.                                        |

## Code Anchors

| Area                                    | File                                                                                  |
| --------------------------------------- | ------------------------------------------------------------------------------------- |
| Dashboard API client                    | `revelation14-dashboard/src/lib/axios.ts`                                             |
| Dashboard devotion service              | `revelation14-dashboard/src/services/devotion.service.ts`                             |
| Mobile base API URL                     | `revelation14-mobile-app/lib/Services/api_service.dart`                               |
| Mobile auth service                     | `revelation14-mobile-app/lib/Services/auth/auth_service.dart`                         |
| Mobile devotion service                 | `revelation14-mobile-app/lib/Services/devotion/devotion_service.dart`                 |
| Mobile feed provider and cache fallback | `revelation14-mobile-app/lib/providers/devotion_provider.dart`                        |
| Mobile push handler                     | `revelation14-mobile-app/lib/Services/firebase_messaging_service.dart`                |
| Public website deep-link redirect       | `rev14ministry-website/src/components/DevotionRedirect.tsx`                           |
| Dashboard Edge Function                 | `revelation14_backend/supabase/functions/dashboard/index.ts`                          |
| Shared dashboard auth helpers           | `revelation14_backend/supabase/functions/_shared/auth.ts`                             |
| Shared FCM helpers                      | `revelation14_backend/supabase/functions/_shared/fcm.ts`                              |
| Current notification cron function      | `revelation14_backend/supabase/functions/devotion-release-notify/index.ts`            |
| Native dashboard migration              | `revelation14_backend/supabase/migrations/20260612010000_dashboard_native.sql`        |
| Legacy cron decommission migration      | `revelation14_backend/supabase/migrations/20260615000000_decommission_legacy_api.sql` |
