# OpenSpec Change: enterprise-api-plugin-rate-limit - **Change ID**: enterprise-api-plugin-rate-limit - **Package**: `@suaveplan/api-plugin-rate-limit` - **Category**: api/data - **Target path**: `api/data/api-plugin-rate-limit` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/api-plugin-rate-limit.md` --- ## Summary Deliver `@suaveplan/api-plugin-rate-limit` on the genesis branch from scratch at enterprise quality. Rate-limiting plugin that wraps `@suaveplan/rate-limiter` and exposes it as a `@suaveplan/api` plugin service. Registers a `RateLimiterService` accessible by other plugins, provides per-route Hono middleware factory, configurable strategies (fixed-window, sliding-window, token-bucket, leaky-bucket) per endpoint, key extraction (IP, user-id, API-key, custom header), per-tenant override support, admin endpoints for viewing/resetting rate-limit state, and OTel metrics. Routes under `/admin/rate-limits`. Events: `rate-limit:exceeded`, `rate-limit:reset`. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/api` (`workspace:^`) | `definePlugin` | | `@suaveplan/api-shared` (`workspace:^`) | Helpers | | `@suaveplan/api-plugin-auth` (`workspace:^`) | `requireAuth`, `requireRole` | | `@suaveplan/rate-limiter` (`workspace:^`) | Rate-limiter core | | `@suaveplan/events` (`workspace:^`) | Event bus | | `@suaveplan/error` (`workspace:^`) | `RateLimitPluginError` | | `hono` + `@hono/zod-openapi` + `zod` | Routes | | `@opentelemetry/api` | OTel | --- ## Public API surface ```ts function createRateLimitPlugin(config?: RateLimitPluginConfig): PluginDescriptor // Registered service: interface RateLimiterService { middleware(options: RouteRateLimitOptions): MiddlewareHandler check(key: string, limit: number, windowMs: number): Promise reset(key: string): Promise getState(key: string): Promise } interface RateLimitPluginConfig { defaultWindowMs?: number defaultLimit?: number strategy?: 'fixed-window' | 'sliding-window' | 'token-bucket' | 'leaky-bucket' keyExtractor?: (c: Context) => string } ``` ### Error codes ```ts type RateLimitPluginErrorCode = | "RATE_LIMIT_EXCEEDED" | "RATE_LIMIT_KEY_MISSING" | "RATE_LIMIT_CONFIG_INVALID" ``` --- ## File layout ```text api/data/api-plugin-rate-limit/ package.json tsconfig.json vitest.config.ts src/ index.ts events.ts types/ errors/ services/rate-limiter/ middleware/rate-limit/ routes/admin/ tests/integration.test.ts ``` --- ## Impact Prerequisites: `@suaveplan/api`, `@suaveplan/api-plugin-auth`, `@suaveplan/rate-limiter`, `@suaveplan/events`.