# North Plane — Gateways

The north plane is the public-facing surface of MW-Core. Each gateway speaks one protocol,
authenticates callers, and converts inbound data into `MwKernel.Context` before handing off
to `mw_router`.

No gateway contains business logic. Gateways are thin translators.

---

## gateway_api — REST/JSON API

**Port:** 4000
**Protocol:** HTTP/1.1 and HTTP/2 (Bandit)
**Auth:** JWT (Bearer) or API Key

### URL Structure
```
/api/v1/transactions          POST, GET (collection)
/api/v1/transactions/:id      GET, PATCH
/api/v1/accounts/:id/balance  GET
/api/v1/files/upload          POST (multipart, triggers async)
/api/v1/jobs/:id              GET (async job status)
/health/live                  GET (no auth — liveness)
/health/ready                 GET (no auth — readiness)
```

### Plug Stack
```elixir
pipeline :api do
  plug :accepts, ["json"]
  plug GatewayApiWeb.Plugs.RequestId    # inject trace_id
  plug GatewayApiWeb.Plugs.CORS
end

pipeline :authenticated do
  plug MwAuth.Plug                      # JWT or ApiKey
  plug MwRouter.RateLimiter             # token bucket per key
end
```

### Response Envelope
```json
{
  "data": { ... },
  "meta": {
    "trace_id": "01HXYZ...",
    "version": "v1",
    "timestamp": "2026-04-26T12:00:00Z"
  }
}
```

Errors:
```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests",
    "retry_after": 30
  },
  "meta": { "trace_id": "..." }
}
```

---

## gateway_ws — WebSocket Channels

**Port:** 4001 (or shared with gateway_api on path `/ws/`)
**Protocol:** WebSocket over HTTP/1.1 upgrade
**Auth:** JWT on socket connect (not per-message)

### Channel Topology
```
UserSocket
├── transactions:*       → TransactionChannel
│     subscribe to individual transaction status
└── notifications:*      → NotificationChannel
      system:system      → operational alerts (admin/operator only)
      jobs:<id>          → async job completion events
```

### Message Format (Phoenix Channel protocol v2)
```json
["join_ref", "ref", "transactions:abc123", "phx_join", {}]
["join_ref", "ref", "transactions:abc123", "phx_reply", {"status": "ok", "response": {...}}]
[null, null, "transactions:abc123", "status_update", {"status": "settled", ...}]
```

### Connection Lifecycle
1. Client upgrades to WebSocket with `?token=<jwt>`
2. `UserSocket.connect/3` verifies JWT — reject with HTTP 403 if invalid
3. Client joins specific channel topics
4. Channel checks resource-level authorization
5. Server pushes events; client may send messages (currently read-only channels)
6. Heartbeat every 30s; timeout at 90s inactivity

---

## gateway_web — LiveView Admin Dashboard

**Port:** 4002 (internal network only — not exposed via ingress)
**Protocol:** HTTP + WebSocket (LiveView)
**Auth:** Session cookie + JWT validation, requires `admin` role

### Live Views

| Route | LiveView | Purpose |
|-------|----------|---------|
| `/admin` | `DashboardLive` | Pipeline KPIs, error rates, latency charts |
| `/admin/pipeline` | `PipelineMonitorLive` | Live message flow per adapter |
| `/admin/routing` | `RouteEditorLive` | CRUD for routing rules |
| `/admin/adapters` | `AdapterHealthLive` | Circuit breaker status |
| `/admin/audit` | `AuditLogLive` | Audit event search/stream |
| `/admin/dlq` | `DlqLive` | Dead letter queue management |

### Real-time Data Sources
- Pipeline metrics: Telemetry events → PubSub → LiveView `handle_info`
- Audit events: `MwAudit.Broadcaster` → PubSub → `AuditLogLive`
- Adapter health: 5s poll via `Process.send_after` in LiveView
- Route changes: optimistic update + PubSub confirmation

---

## gateway_mobile — Mobile API

**Port:** 4000 (path `/m/v1/`)
**Protocol:** HTTP/1.1 and HTTP/2
**Auth:** JWT (short-lived, mobile-issued tokens)

### Design Principles
- **Compact responses:** No hypermedia links, no null fields, amounts in minor currency units
- **Offline-friendly:** Idempotency keys on all mutation endpoints
- **Push-first:** Async results delivered via FCM/APNS, not polling
- **Versioned:** `/m/v1/` locked; `/m/v2/` when breaking changes needed

### Idempotency
```
POST /m/v1/transactions
Headers: Idempotency-Key: <uuid>

If same key sent twice within 24h → return cached response, no duplicate processing
```

Idempotency keys stored in `infra_cache` (ETS, 24h TTL).

### Push Notification Payload
```json
{
  "title": "Transaction Approved",
  "body": "Your payment of $100 was approved.",
  "data": {
    "type": "transaction_update",
    "tx_id": "abc123",
    "status": "approved"
  }
}
```
