# Falah Mobile — System Reference & Auth Architecture > **Last updated:** 28 June 2026 > **Purpose:** Single source of truth for understanding the database architecture, auth chain, password management, and FLH Islamic Finance setup across all FalahOS services. --- ## 1. Auth Chain (How Login Works) ``` Browser ──POST /mobile/api/auth/login──▶ V1 Mobile (Next.js) │ Proxy to UmmahID │ ▼ UmmahID (Node.js) │ SQLite ledger.db │ bcrypt.compare(password, hash) │ ┌─────┴─────┐ ▼ ▼ ✅ 200 ❌ 401 (JWT) (Invalid credentials) ``` ### Detailed flow 1. Browser sends `POST /mobile/api/auth/login` with `{email, password}` to V1 Mobile. 2. V1 Mobile proxies the request to UmmahID service (`falah_ummahid:3000`). 3. UmmahID looks up user in **SQLite** (`/data/ledger.db`), compares password with bcrypt. 4. If valid, UmmahID returns a **JWT** token + user info. 5. V1 Mobile then **finds or creates** a local `User` record in its own SQLite (`/app/data/dev.db`) for app features (wallet balance, gamification, etc.). 6. Browser receives the JWT and uses it for subsequent API calls. --- ## 2. Database Architecture (Three Databases) ### 2.1 UmmahID SQLite — `ledger.db` | Property | Value | |----------|-------| | **Container** | `falah_ummahid` (Swarm service) | | **Path** | `/data/ledger.db` | | **Host bind** | `/root/falah-ummahid-db/ledger.db` | | **Engine** | sql.js (SQLite via Node.js) | | **Auth** | **Yes** — single source of truth for passwords | | **Tables** | `users`, `accounts`, `transactions`, `zakat_*`, `sadaqah_*`, `hibah_records`, `waqf_*`, `forum_*`, `learn_*` | **Password column:** `password_hash` (bcrypt hash) **GOOGLE_CRITICAL:** This is the **only** place where passwords are verified. The self-heal script (`startup-selfheal.js`) is the **sole authority** for the demo user's password. ### 2.2 V1 Mobile SQLite — `dev.db` | Property | Value | |----------|-------| | **Container** | `falah_falah-mobile` (Swarm service) | | **Path** | `/app/data/dev.db` | | **Engine** | Prisma with SQLite provider | | **Auth** | **No** — `passwordHash` field is NULL for all users | | **Tables** | `User`, `Listing`, `Purchase`, `Review`, `LearnCourse`, `ForumPost`, etc. | **Password column:** `passwordHash` (always NULL — not used for auth) **Purpose:** Stores app-level data: user profile, FLH balance cache, gamification (XP, streaks, levels), marketplace, learning progress, etc. ### 2.3 Postgres — `falahdb` (Community V2) | Property | Value | |----------|-------| | **Container** | `falah_postgres` (Swarm service) | | **Connection** | `ummahid-community` container via `DATABASE_URL=postgresql://postgres:@falah_postgres:5432/falahdb` | | **Auth** | **No** — `password_hash` column **removed** (28 June 2026) | | **Tables** | `users`, `accounts`, `zakat_*`, `sadaqah_*`, `hibah_records`, `waqf_*`, `forum_*`, `learn_*` | **Password column:** ❌ DELETED — this was the source of confusion. Auth is delegated to UmmahID SQLite or JWT verification. **Purpose:** FLH Islamic Finance engine (zakat, sadaqah, hibah, waqf), community features (content, qard hasan, learn). --- ## 3. Password Management (The Self-Heal Script) **File:** `/root/falah-ummahid-db/startup-selfheal.js` **Mount:** Bind-mounted from host `/root/falah-ummahid-db/` → container `/data/` ### Behaviour - Runs **every time** the `falah_ummahid` container starts. - Re-hashes the demo password if it doesn't match `DEMO_PASSWORD`. - Ensures `license_tier = 'premium'`. - Creates the demo user + account if missing (100,000 FLH). - **Single source of truth** — nothing else should write to the `users` or `accounts` tables in SQLite. ### Changing the Demo Password ```bash # 1. Edit the self-heal script vim /root/falah-ummahid-db/startup-selfheal.js # Change: DEMO_PASSWORD='***' # 2. Restart the UmmahID service docker service update --force falah_ummahid # 3. Done — no other database needs updating ``` ### What NOT to do - ❌ Do NOT write directly to SQLite `users.password_hash` - ❌ Do NOT add `password_hash` column back to Postgres - ❌ Do NOT run seed scripts that write passwords to Postgres --- ## 4. FLH Islamic Finance Architecture ### 4.1 Endpoints | Category | V1 Mobile Proxy Route | V2 Community Backend Route | |----------|----------------------|---------------------------| | **Summary** | `/mobile/api/flh/summary` → | `/api/v2/flh/summary` | | **Zakat** | `/mobile/api/flh/zakat/*` → | `/api/v2/zakat/*` | | **Sadaqah** | `/mobile/api/flh/sadaqah/*` → | `/api/v2/sadaqah/*` | | **Hibah** | `/mobile/api/flh/hibah/*` → | `/api/v2/hibah/*` | | **Waqf** | `/mobile/api/flh/waqf/*` → | `/api/v2/waqf/*` | | **Cron Health** | — | `/api/v2/flh/cron-health` | ### 4.2 Data Flow ``` V1 Mobile UI ──▶ V1 Mobile API Route (proxy) ──▶ ummahid-community (Postgres) │ JWT auth via: 1. UmmahID API (preferred) 2. Local JWT verify (fallback) ``` ### 4.3 Zakat Agents (18 pre-seeded) Covers 9 countries: UAE, Bangladesh, Egypt, Indonesia, Malaysia, Pakistan, Saudi Arabia, Turkey. ### 4.4 Cron Jobs (6 scripts) Located in `/root/ummahid-community/cron/`: | Script | Schedule | Purpose | |--------|----------|---------| | `00-health.sh` | Every 15 min | Heartbeat check | | `10-sadaqah-process.sh` | Every 6h | Process due recurring sadaqah | | `20-hibah-expiry.sh` | Every 1h | Expire unaccepted hibah gifts | | `30-waqf-yield.sh` | Daily midnight | Accrue waqf yield | | `40-nisab-update.sh` | Daily 6am | Update nisab threshold | | `50-zakat-reconcile.sh` | Weekly Mon 2am | Reconcile zakat pool | --- ## 5. Key Configuration Files | File | Purpose | |------|---------| | `/root/falah-ummahid-db/startup-selfheal.js` | **Self-heal — single source of truth for demo password** | | `/root/falah-mobile/.env.local` | V1 Mobile env vars (`COMMUNITY_URL`, `UMMAHID_URL`) | | `/root/ummahid-community/config/community.env` | Community V2 env vars (`JWT_SECRET`, `UMMAHID_HOST`, `DATABASE_URL`) | | `/root/falah-mobile/src/app/api/flh/zakat/calculate/route.ts` | Zakat calculate proxy (handles `amount=0` as status check) | | `/root/falah-core/docker/services/ummahid-community/pg.js` | Postgres persistence layer for community V2 | --- ## 6. Testing Checklist ### 6.1 Login Verification ```bash curl -X POST -H 'Content-Type: application/json' \ -d '{"email":"demo@falahos.my","password":"demo123"}' \ https://falahos.my/mobile/api/auth/login # Expected: 200 with {"token":"eyJ...", "user":{...}} ``` ### 6.2 FLH Dashboard ```bash # Login first, extract token, then: curl -H "Authorization: Bearer " \ https://falahos.my/mobile/api/flh/summary # Expected: {"success":true,"totalFlhBalance":5000,"pillars":{...}} ``` ### 6.3 Password Consistency Check that `password_hash` does NOT exist in Postgres: ```bash docker exec falah_postgres.1. psql -U postgres -d falahdb \ -c "SELECT column_name FROM information_schema.columns \ WHERE table_name='users' AND column_name='password_hash'" # Expected: 0 rows ``` ### 6.4 Self-Heal Status ```bash docker logs falah_ummahid.1. 2>&1 | grep selfheal # Expected: "✓ Demo user OK — password matches, tier is premium" ``` ### 6.5 V2 Community Health ```bash curl -H "Authorization: Bearer " \ https://falahos.my/api/v2/flh/cron-health # Expected: 200 with health status ``` --- ## 7. Troubleshooting History ### 28 June 2026 — Login Failure & Resolution **Symptom:** `401 Invalid credentials` when logging in with `TestPass123!`. **Root cause:** Three different passwords across three databases: - Self-heal script had `DEMO_PASSWORD='falahdemo2026'` - Postgres had stale hash for `password123` - User expected `TestPass123!` **Fix:** 1. Unified all references to `demo123`. 2. Deleted `password_hash` column from Postgres `users` table. 3. Locked self-heal as single source of truth. 4. Updated all debug scripts, seed files, and test suites. ### Previous — JWT_SECRET Mismatch **Symptom:** Auth worked then failed after container restart. **Root cause:** `falah_ummahid` used 64-char JWT_SECRET, but `ummahid-community` had a different one. **Fix:** Harmonized JWT_SECRET across all services to the 64-char value from `falah_ummahid`. --- > **⚠️ Golden Rule:** Auth = UmmahID SQLite. Postgres has NO password_hash. The self-heal script is the ONLY authority for the demo password. Do not add password storage anywhere else.