Tier 1 – Critical Auth
AuthLimiter, RegisterLimiter, MFAVerifyLimiter, AdminRecoveryLimiter — protects login and MFA endpoints from brute force.
middleware/rate_limit.go wires Fiber limiters backed by Redis. Each limiter uses utils.ClientIP(c) as the key so IPv4 and IPv6 clients share the same budgeting behaviour.
Tier 1 – Critical Auth
AuthLimiter, RegisterLimiter, MFAVerifyLimiter, AdminRecoveryLimiter — protects login and MFA endpoints from brute force.
Tier 2 – Share Links
ShareLinkPublicLimiter, ShareLinkCreateLimiter — guards anonymous share-link traffic and token creation.
Tier 3 – Heavy Ops
SearchLimiter, ImportExportLimiter, BulkImportLimiter, AttachmentUploadLimiter — expensive operations touching PostgreSQL or large payloads.
Tier 4 – CRUD & Collaboration
StandardCRUDLimiter, CollaborationLimiter — day-to-day note operations and collaborator management.
Tier 5 – Lightweight
LightweightLimiter — status checks such as GET /auth/mfa/status or user settings fetches.
Tier 1: 10 requests / 5 minutes (registration 5 / 15 min, admin recovery 3 / 15 min)Tier 2: 20 requests / 5 minutes (public share) and 10 / 15 minutes (create)Tier 3: 30 requests / minute (search), 10 / 5 minutes (import/export), 5 / 15 minutes (bulk import)Tier 4: 100 requests / minute (CRUD), 50 / minute (collaboration)Tier 5: 300 requests / minute (status/endpoints)Adjust numbers in middleware/rate_limit.go if your deployment demands different ceilings. All limiters share the same Redis connection used for sessions.
POST /auth/login, POST /auth/password/reset-*, and POST /auth/mfa/* all use Tier 1 limiters.
GET/POST/PUT/DELETE /notes*, tags, folders, templates use Tier 4.
Upload/download routes (/notes/:id/attachments) use the Tier 3 AttachmentUploadLimiter.
/search, /notes/import, /notes/:id/export, /notes/bulk-import map to Tier 3 limiters.
POST /notes/:id/share-links → Tier 2 (create), GET /share/:token → Tier 2 (public).
Limiters reuse the app Redis client. When Redis is unavailable every limiter returns HTTP 429 with "error": "Too many requests..." until the connection stabilizes.
storage := redisstorage.NewFromConnection(rdb)limiter.New(limiter.Config{Max: 10,Expiration: 5 * time.Minute,Storage: storage,})Deployments with sharded Redis should point REDIS_URL at a single node or use a proxy that supports INCR across the cluster; the limiter storage expects atomic increments.
middleware/rate_limit.go to consider user_id and attach multiple keys (e.g., ip:user).if config.Environment == "development".if cfg.Environment == "development" {return func(c *fiber.Ctx) error { return c.Next() }}