# 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 <token>" \
  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.<id> 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.<id> 2>&1 | grep selfheal
# Expected: "✓ Demo user OK — password matches, tier is premium"
```

### 6.5 V2 Community Health

```bash
curl -H "Authorization: Bearer <token>" \
  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.
