rate_limits.py
python
| 1 | """Shared rate-limiter instance for MuseHub. |
| 2 | |
| 3 | Importing ``limiter`` here (rather than from ``main``) breaks the |
| 4 | circular-import chain: ``main`` registers the limiter with the app, |
| 5 | route modules import it from here, and ``main`` imports it from here too. |
| 6 | |
| 7 | Limits are read from :class:`~musehub.config.Settings` so they can be |
| 8 | overridden via environment variables without touching code: |
| 9 | |
| 10 | MCP_RATE_LIMIT_HUMAN (default: 60/minute) |
| 11 | MCP_RATE_LIMIT_AGENT (default: 600/minute) |
| 12 | MCP_RATE_LIMIT_ANONYMOUS (default: 20/minute) |
| 13 | ASSET_RATE_LIMIT_PER_IP (default: 120/minute) |
| 14 | |
| 15 | Wire endpoints (push/fetch) use a conservative fixed limit. |
| 16 | Auth endpoints use a fixed per-IP limit to slow credential-stuffing. |
| 17 | """ |
| 18 | from __future__ import annotations |
| 19 | |
| 20 | from slowapi import Limiter |
| 21 | from slowapi.util import get_remote_address |
| 22 | |
| 23 | from musehub.config import settings |
| 24 | |
| 25 | limiter = Limiter(key_func=get_remote_address) |
| 26 | |
| 27 | # ── Convenience limit strings (resolved at module load from config) ────────── |
| 28 | |
| 29 | # Wire protocol — push is expensive (disk + DB); cap tightly per IP. |
| 30 | WIRE_PUSH_LIMIT: str = "30/minute" |
| 31 | # Fetch/refs are cheaper but can still exhaust DB; moderately capped. |
| 32 | WIRE_FETCH_LIMIT: str = "120/minute" |
| 33 | # MCP POST endpoint — different caps by caller type; use the most |
| 34 | # permissive (agent) as the global cap; per-tool limits added later. |
| 35 | MCP_LIMIT: str = settings.mcp_rate_limit_agent |
| 36 | # Auth endpoints — protect against credential stuffing. |
| 37 | AUTH_LIMIT: str = "20/minute" |
| 38 | # Search — can trigger Qdrant; cap to protect the embedding service. |
| 39 | SEARCH_LIMIT: str = "60/minute" |