# Capability Spec Delta: api-plugin-site (document-site-middleware-integration) **Package**: `@suaveplan/api-plugin-site` **Status**: PROPOSED > Every requirement below carries a `**Verified by:**` line at authoring time. ## ADDED Requirements ### Requirement: Middleware composition recommendations The package SHALL expose `MIDDLEWARE_RECOMMENDATIONS` — a readonly `MiddlewareRecommendation[]` constant describing the recommended rate-limit window, rate-limit ceiling, and audit category per documented site route group (`public:resolve`, `public:read`, `private:write`). The recommendations SHALL be pure data; the site plugin SHALL NOT bind the recommended middleware itself — consumers compose them via the host app's `app.use(prefix, middleware)` Hono pattern. `MiddlewareRecommendation` SHALL be re-exported from the package barrel. #### Scenario: bundle covers all three route groups - **WHEN** a caller imports `MIDDLEWARE_RECOMMENDATIONS` - **THEN** the array contains exactly three entries whose `routeGroup` fields are `"public:resolve"`, `"public:read"`, and `"private:write"` #### Scenario: private:write carries the tightest limit - **WHEN** a caller picks the entry where `routeGroup === "private:write"` - **THEN** its `rateLimit.limit` is strictly less than the entry where `routeGroup === "public:read"` AND its `auditCategory` is `"data.mutation"` **Verified by:** `src/integration/middleware-recommendations.test.ts::"exposes three route groups"` AND `src/integration/middleware-recommendations.test.ts::"private:write is tightest and audited as data.mutation"` --- ### Requirement: Host-app middleware composition pattern is integration-tested The package SHALL ship an integration test under `src/integration/middleware-composition.integration.test.ts` that boots a real Hono app composing (a) `createAuditMiddleware` from `@suaveplan/api-plugin-audit` mounted globally, (b) `rateLimit` from `@suaveplan/api-plugin-rate-limit` mounted on the site prefix, and (c) `createPrivateSiteRoutes` from this package mounted under the same prefix. The test SHALL exercise a `POST /sites` write call and SHALL assert that (1) the site row is persisted (site behaviour unchanged by middleware composition), (2) the response carries at least one `X-RateLimit-*` header (rate-limit observed the request), and (3) exactly one `AuditEvent` is recorded in the in-memory audit sink (audit observed the request). #### Scenario: composed write path preserves site behaviour, surfaces rate-limit headers, and records one audit event - **WHEN** the composed Hono app receives `POST /sites` with a valid body and a forwarded-for IP header - **THEN** the response status is `201`, `res.headers.get("X-RateLimit-Limit")` is non-null, and the `MemoryAuditAdapter` snapshot contains exactly one event whose `details.path === "/sites"` and `details.method === "POST"` **Verified by:** `src/integration/middleware-composition.integration.test.ts::"composes audit + rate-limit + site routes in the documented order"` --- ### Requirement: Package-level integration guide The package SHALL ship `docs/middleware-integration.md` documenting the canonical Hono `app.use(prefix, middleware)` composition order (audit outermost → rate-limit middle → site routes innermost), the rationale for that order (audit must observe rate-limit denials; rate-limit must precede expensive handlers), and a copy-pasteable end-to-end example covering all three plugins. The README SHALL link to this guide from a "Middleware composition" subsection. #### Scenario: guide exists and exceeds 200 words - **WHEN** the docs-check gate inspects `docs/middleware-integration.md` - **THEN** the file exists, contains the words `app.use`, `audit`, `rate-limit`, and `site`, and exceeds 200 words excluding code blocks **Verified by:** `src/integration/middleware-recommendations.test.ts::"docs/middleware-integration.md exists and documents the composition order"` --- ## MODIFIED Requirements ### Requirement: Public surface re-exports The package barrel SHALL re-export the canonical public surface: `SITE_CODES`, `SiteError`, `DEFAULT_CONFIG`, `mergeConfig`, `SiteService`, `DomainResolverService`, `normalizeDomain`, `createDomainResolverMiddleware`, `createPublicSiteRoutes`, `createPrivateSiteRoutes`, `createSitePlugin`, `getSiteInstruments`, `resetSiteInstruments`, `validatePushConfig`, `SiteSchema`, `DomainSchema`, the four `SITE_STATE_*` constants, **and** `MIDDLEWARE_RECOMMENDATIONS` plus the `MiddlewareRecommendation` type. #### Scenario: re-export surface count - **WHEN** the spec-conformance test imports every documented symbol from the package barrel - **THEN** every symbol resolves and the import does not throw **Verified by:** `src/api-plugin-site.spec-conformance.test.ts::"re-exports every documented symbol"` ---