# MindLyst Web — Abuse Controls (Rate Limiting + Request Size Limits)

> **Purpose:** Prevent denial-of-wallet / runaway costs on Azure OpenAI-backed routes, and reduce simple abuse vectors on hosted web betas.
> **Scope:** Next.js API routes that call Azure OpenAI:
>
> - `mindlyst-native/web/src/pages/api/triage.ts`
> - `mindlyst-native/web/src/pages/api/brain-chat.ts`
>
> **Last updated:** 2026-02-14

---

## What’s Implemented

### 1. Rate Limiting (Basic)

Implementation: `mindlyst-native/web/src/lib/abuse.ts`

- Algorithm: **in-memory fixed-window** counter
- Key: `scope + clientIp (+ x-user-id when present)`
- Response: `429` with JSON `{ "error": "Rate limit exceeded" }`
- Headers:
  - `X-RateLimit-Limit`
  - `X-RateLimit-Remaining`
  - `X-RateLimit-Reset` (unix seconds)
  - `Retry-After` (seconds, only on `429`)

**Important limitation:** This is **per-process / per-instance**. If you run multiple app instances (or serverless scales out), limits apply per instance and are less effective. For production, move to a shared store (Redis/Upstash) or provider-native rate limiting.

### 2. Request Size Limits

Two layers:

1. **Next.js body parser size limit** per route via `export const config` (rejects oversized requests early).
2. **Field-level guards** (chars / counts) to prevent huge strings or chat histories from hitting the LLM logic.

---

## Default Limits (Configurable By Env Vars)

These are the defaults used by the API routes when env vars are not set:

- Window: `LLM_RATE_LIMIT_WINDOW_MS=60000` (60 seconds)
- `/api/triage`:
  - `TRIAGE_RATE_LIMIT_LIMIT=30` requests/window
  - `TRIAGE_MAX_CONTENT_CHARS=8000`
  - Body parser: `64kb`
- `/api/brain-chat`:
  - `BRAIN_CHAT_RATE_LIMIT_LIMIT=20` requests/window
  - `BRAIN_CHAT_MAX_MESSAGE_CHARS=2000`
  - `BRAIN_CHAT_MAX_HISTORY_MESSAGES=20`
  - `BRAIN_CHAT_MAX_HISTORY_TOTAL_CHARS=10000`
  - Body parser: `64kb`

Global toggles:

- `RATE_LIMIT_ENABLED`:
  - `true` (default) enables rate limiting
  - set to `false` to disable
- `RATE_LIMIT_MAX_ENTRIES`:
  - default `5000`
  - bounds the in-memory map (opportunistic cleanup when exceeded)

---

## Recommended Hosting Notes

- Ensure your host sets `x-forwarded-for` / `x-real-ip` correctly, since client IP is derived from those headers (fallback: `req.socket.remoteAddress`).
- If you don’t have real auth yet, rate limiting helps but does not prevent public write access. For a hosted beta, add an auth gate (e.g., `BETA_API_TOKEN`) before exposing these routes.

---

## Future Hardening (Post-Beta)

- Replace in-memory rate limiting with Redis/Upstash and a token-bucket algorithm.
- Add per-user quotas (requires auth).
- Add SSRF protection to `/api/triage` URL enrichment (block localhost/private IPs, limit redirects, cap response bytes).
- Add structured request validation (zod) and centralize API middleware.
