# Container Diagram

# C4 Level 2 — Container Diagram

> Drop fintech platform container architecture showing all runtime containers, their responsibilities, communication patterns, and the middleware chain that governs every API request.

---

## Container Diagram

```mermaid
C4Container
  title Drop — Container Diagram (C4 Level 2)

  Person(user, "End User", "Norwegian resident 18+, authenticated via BankID")
  Person(merchant, "Merchant", "Business owner receiving QR payments")

  System_Boundary(drop, "Drop Platform") {
    Container(web, "drop-web", "Next.js 15, React 19, Tailwind v4", "Server-side rendered web application. Handles login redirect, dashboard, send money, QR scan, bank accounts, transaction history, notifications, settings, merchant dashboard.")
    Container(api, "drop-api", "Hono v4, Node.js 22", "REST API server. 26+ endpoints under /v1/. BankID OIDC callback, transaction processing, recipient management, merchant registration, GDPR compliance, admin operations.")
    Container(mobile, "drop-mobile", "Expo SDK 54, React Native", "Native mobile app for iOS and Android. BankID auth via expo-web-browser deep linking. AsyncStorage for token persistence. No offline support.")
    ContainerDb(db, "Database", "PostgreSQL 16 (all environments)", "19 tables: 12 core (users, transactions, bank_accounts, sessions, merchants, recipients, etc.) + 7 compliance (audit_log, aml_alerts, str_reports, screening_results, consents, data_access_requests, complaints). Drizzle ORM.")
  }

  System_Ext(bankid, "BankID OIDC", "Norwegian eID provider. OIDC authorize/token/JWKS endpoints for Strong Customer Authentication.")
  System_Ext(sumsub, "Sumsub", "KYC/AML identity verification. WebSDK (web), React Native SDK (mobile), webhooks for status updates.")
  System_Ext(openbanking, "Open Banking APIs", "PSD2 AISP (read balances) and PISP (initiate payments) via licensed provider.")
  System_Ext(sepa, "SEPA/SWIFT Networks", "International payment rails for remittance settlement to 30+ countries.")

  Rel(user, web, "HTTPS", "Browser")
  Rel(user, mobile, "HTTPS", "Native app")
  Rel(merchant, web, "HTTPS", "Merchant dashboard")

  Rel(web, api, "HTTPS REST", "/v1/* endpoints, JSON, Bearer token or httpOnly cookie")
  Rel(mobile, api, "HTTPS REST", "/v1/* endpoints, JSON, Bearer token")

  Rel(api, db, "SQL", "Type-safe queries via Drizzle ORM (src/shared/db/)")
  Rel(api, bankid, "OIDC", "Authorization code flow, JWKS token verification")
  Rel(api, sumsub, "REST + Webhooks", "Applicant creation, document checks, status webhooks")
  Rel(api, openbanking, "PSD2 API", "AISP balance reads, PISP payment initiation with SCA")
  Rel(api, sepa, "ISO 20022", "Remittance settlement via banking partner")
```

---

## Container Responsibilities

| Container | Technology | Responsibilities | Port |
|-----------|-----------|-----------------|------|
| **drop-web** | Next.js 15, React 19, Tailwind v4 | SSR web app, BankID redirect initiation, UI rendering for all 10 screens (Login, Onboarding, Dashboard, SendMoney, BankAccounts, TransactionHistory, ScanQR, Profile, Notifications, MerchantDashboard) | 3000 |
| **drop-api** | Hono v4, Node.js 22 Alpine | REST API, BankID OIDC callback handling, JWT session management, transaction processing, GDPR endpoints, admin operations, audit logging | 3001 |
| **drop-mobile** | Expo SDK 54, React Native | iOS/Android native app, BankID via `expo-web-browser` + deep link (`drop://auth/callback`), AsyncStorage for token persistence, push notifications | N/A |
| **Database** | PostgreSQL 16 (all environments) | 19 tables, foreign keys enforced. Drizzle ORM schema in `src/shared/db/schema.ts`. Local: Docker port 5433. Production: AWS RDS. | 5432 |

---

## Request Lifecycle

```mermaid
sequenceDiagram
    participant Client as Client (Web/Mobile)
    participant CORS as CORS Middleware
    participant ReqID as Request ID Middleware
    participant IP as Client IP Middleware
    participant RL as Rate Limiter
    participant Auth as Auth Middleware
    participant Route as Route Handler
    participant DB as Database
    participant ErrH as Error Handler

    Client->>+CORS: HTTPS Request
    CORS->>CORS: Validate Origin against allowlist
    CORS->>+ReqID: Pass if origin allowed
    ReqID->>ReqID: Extract x-request-id or generate UUID
    ReqID->>ReqID: Set x-request-id response header
    ReqID->>+IP: Forward request
    IP->>IP: Extract IP from x-real-ip / x-forwarded-for
    IP->>IP: Set clientIp context variable

    alt Rate-limited endpoint
        IP->>+RL: Forward to rate limiter
        RL->>DB: SELECT count, reset_at FROM rate_limits WHERE key = ?
        DB-->>RL: Current count
        alt Under limit
            RL->>RL: UPDATE count + 1
            RL->>+Auth: Forward request
        else Over limit
            RL-->>Client: 429 Too Many Requests
        end
    else Non-rate-limited endpoint
        IP->>+Auth: Forward request
    end

    alt Authenticated endpoint
        Auth->>Auth: Extract token (Bearer header or drop_token cookie)
        Auth->>Auth: Verify JWT (jose, HS256/RS256)
        Auth->>DB: SELECT session (check revoked = 0, expires_at > now)
        DB-->>Auth: Session record
        Auth->>DB: SELECT user WHERE id = ? AND deleted_at IS NULL
        DB-->>Auth: User record
        Auth->>Auth: Set user context variable
        Auth->>+Route: Forward authenticated request
    else Public endpoint
        IP->>+Route: Forward directly
    end

    Route->>DB: Business logic queries (parameterized)
    DB-->>Route: Query results
    Route-->>Client: JSON response { data: {...} }

    Note over ErrH: On any unhandled error
    Route-->>ErrH: Error thrown
    ErrH->>ErrH: Log error, capture in Sentry
    ErrH-->>Client: { error: "internal_error", message: "..." }
```

---

## Middleware Chain

The Hono v4 API (`drop-api`) applies middleware in the following order for every request:

| Order | Middleware | Source | Purpose |
|-------|-----------|--------|---------|
| 1 | **CORS** | `hono/cors` in `app.ts:23-30` | Validates `Origin` header against allowlist (`localhost:3000`, `localhost:3001`, `APP_URL`). Sets `credentials: true` for cookie transport. |
| 2 | **Request ID** | `app.ts:33-38` | Reads `x-request-id` header or generates `crypto.randomUUID()`. Sets on context and response header for distributed tracing. |
| 3 | **Client IP** | `app.ts:41-47` | Extracts IP from `x-real-ip` then `x-forwarded-for` (first in chain), falls back to `127.0.0.1`. Stored in context for rate limiting and audit. |
| 4 | **Rate Limiter** | `middleware/rate-limit.ts` | Per-IP rate limiting backed by `rate_limits` DB table. Configurable limit and window per route. Cleans expired entries every 100 calls. |
| 5 | **Auth** | `middleware/auth.ts` | Extracts JWT from `Authorization: Bearer` header or `drop_token` cookie. Verifies signature (jose HS256/RS256), checks session not revoked, loads user record. |
| 6 | **Merchant** | `middleware/auth.ts:21-29` | Standalone middleware that independently verifies auth (calls `extractToken` and `verifyAndGetUser`) and checks `user.role === 'merchant'`. Does NOT extend or chain authMiddleware. Returns 403 if not merchant. |
| 7 | **Global Error Handler** | `middleware/error-handler.ts` | Catches all unhandled errors. HTTPException returns structured JSON with status. Other errors return 500, log to stdout, and capture in Sentry. |

### Rate Limit Configuration

| Endpoint Group | Limit | Window | Source |
|---------------|-------|--------|--------|
| BankID initiate | 10 req | 60s | `routes/auth.ts:19` |
| BankID callback | 10 req | 60s | `routes/auth.ts:43` |
| Remittance | 10 req/60s per-IP + 3 req/60s per-user | 60s | `routes/transactions.ts` |
| QR Payment | 10 req/60s per-IP + 3 req/60s per-user | 60s | `routes/transactions.ts` |
| Exchange rates | 120 req | 60s | `routes/rates.ts` |

---

## Communication Patterns

### Web Client to API

The Next.js web app communicates with the Hono API over HTTPS REST:

- **Authentication:** httpOnly cookie (`drop_token`) set on BankID callback redirect. Cookie attributes: `HttpOnly`, `Path=/`, `Max-Age=604800` (7 days), `SameSite=Lax`.
- **CSRF protection:** CORS origin validation + `SameSite` cookie attribute.
- **Content type:** `application/json` for all request/response bodies.
- **Error envelope:** `{ error: "code", message: "human-readable", details: [...] }`.

### Mobile Client to API

The Expo mobile app uses Bearer token authentication:

- **Token storage:** `AsyncStorage` (React Native encrypted storage).
- **Auth header:** `Authorization: Bearer <jwt>`.
- **BankID flow:** `expo-web-browser` opens BankID authorize URL, redirects back via deep link `drop://auth/callback?code=&state=`.
- **Token refresh:** `POST /v1/auth/refresh` — revokes old sessions, issues new JWT, sets cookie (web) and returns token in body (mobile).

### API to Database

- **Abstraction layer:** `db.ts` provides `query()`, `getOne()`, `run()`, `runIgnore()`, `runUpsert()`, `transaction()`.
- **Driver detection:** `DATABASE_URL` env var present = PostgreSQL via `pg.Pool`, absent = SQLite via `better-sqlite3`.
- **SQL compatibility:** Automatic conversion of SQLite dialect to PostgreSQL (placeholders `?` to `$N`, `datetime('now')` to `CURRENT_TIMESTAMP`, `INSERT OR IGNORE` to `ON CONFLICT DO NOTHING`).
- **Transaction isolation:** SQLite uses `BEGIN/COMMIT/ROLLBACK` on the single connection. PostgreSQL uses pool client with explicit transaction.

### API to External Services

| Service | Protocol | Authentication | Data Flow |
|---------|----------|---------------|-----------|
| BankID OIDC | HTTPS (OpenID Connect) | Client ID + Client Secret | Auth code exchange, JWKS token verification, pid extraction |
| Sumsub KYC | REST + Webhooks | API key + HMAC signature | Applicant creation, document verification, status webhooks |
| Open Banking | PSD2 REST API | OAuth2 (provider-specific) | AISP balance reads (cached in `bank_accounts.balance`), PISP payment initiation |
| SEPA/SWIFT | ISO 20022 (via banking partner) | Banking partner credentials | Remittance settlement to 30+ countries |

---

## Cross-References

- **API endpoints:** [API-REFERENCE.md](../../backend/API-REFERENCE.md) — Full endpoint documentation with request/response examples
- **Database schema:** [DATABASE-SCHEMA.md](../../backend/DATABASE-SCHEMA.md) — All 19 tables with column definitions
- **Authentication:** [AUTHENTICATION.md](../../backend/AUTHENTICATION.md) — BankID OIDC flow, JWT structure, session management
- **Middleware:** [MIDDLEWARE.md](../../backend/MIDDLEWARE.md) — Detailed middleware documentation
- **Security:** [SECURITY-ARCHITECTURE.md](../../security/SECURITY-ARCHITECTURE.md) — Threat model, security headers, input validation
- **Deployment:** [deployment-architecture.md](deployment-architecture.md) — AWS + Cloudflare topology, CI/CD pipeline
- **Feature flags:** [FEATURE-FLAGS.md](../../backend/FEATURE-FLAGS.md) — Runtime feature gating system