WebSocket + login throttle slice
Daloy closes the remaining expanded leftover items. The theme is narrow but practical: authentication entry points, upload boundaries, and WebSocket upgrades now have first-party helpers instead of copy-pasted local policy.
1. wsRateLimit()
wsRateLimit() adapts the existing rateLimit() shared-bucket primitive to the WebSocket upgrade boundary. Put the samegroupId on HTTP login routes and the WebSocket session route so an attacker cannot dodge the bucket by switching transports.
2. loginThrottle()
loginThrottle() is the built-in preset for credential-entry routes. It combines a shared hard limit with a short progressive delay before the hard 429 response. By default it does not trust proxy IP headers; pass a keyGenerator or opt in to trustProxyHeaders: true only behind a trusted proxy.
3. rotateSession()
rotateSession() watches session privilege fields and calls session.regenerate() after the handler when those fields change. It skips itself when the handler already regenerated the session, so explicit login flows keep their exact behavior.
4. Upload MIME and magic-byte guards
fileField() already enforced maxBytes and MIME allowlists. Add magicBytes: true to derive known signatures from accept, or pass custom signatures for private formats. The OpenAPI generator emits x-magic-bytes alongsidex-accept and x-max-bytes.
5. requirePayloadAuth
OpenAPI security scheme builders accept requirePayloadAuth: true for schemes such as webhook signatures that must authenticate the request body. A route using that scheme cannot set auth.payload: false; Daloy throws at route registration. The public OpenAPI document uses x-daloy-require-payload-auth rather than leaking a non-spec field.
6. WebSocket safe defaults
app.ws() now normalizes safe runtime defaults for Node and Bun: close on excessive outbound backpressure, a 1 MiB backpressure limit, compression off by default, a non-zero idle timeout, and a 1 MiB inbound payload cap. In production under secureDefaults,perMessageDeflate: true is refused. Daloy also refuses amaxPayloadLengthlarger than a route body schema's declared maximum when the schema exposes one.