You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Rate-limit responses: EWMA backoff is a half-life, an outage is reported as rate-limited, and the message bucket ignores message type #1006
Follow-up from the spike in #1004 (findings). Three wire-correctness defects in one path; each is small.
1. The EWMA backoff is a half-life, and is safe only by accident
rateLimiter.strategy: ewma is the default (resources/default-settings.yaml:221). Under EWMA period is a half-life (src/utils/ewma-rate-limiter.ts:18), so the true time until an event is accepted again is period · log2(R_new / rate). We send period (src/handlers/event-message-handler.ts:453). With rate = 6, period = 60000, R_new = 24 needs 120 s and we say 60 s.
It is not exploitable today because the hash is given a TTL of exactly period ms (src/utils/ewma-rate-limiter.ts:22), so a quiet client's state disappears anyway. That coupling is unstated and would break the moment someone lengthens the EXPIRE to keep state warm.
Fix: return R_new from the Lua script (it is already computed) instead of only 0/1, and compute Math.ceil(period * Math.log2(R_new / rate)) in the handler. Where R_new is unavailable, send no parameter — absence is explicitly allowed and MUST NOT be read as retry-immediately.
Tests that pin today's behaviour: test/unit/utils/ewma-rate-limiter.spec.ts (script returns 0/1), test/unit/handlers/event-message-handler.spec.ts:138 and :153.
2. A limiter outage is reported as rate-limited
src/handlers/event-message-handler.ts:445-447 catches a backend error and return true, so :97 yields no parameter and the client gets rate-limited: slow downwithout a value. Every connected client then backs off at once when the cache returns, and the spec's own example for this condition is error: could not connect to the database.
Fix: error: rate limiter unavailable on that branch. Fail-closed behaviour is unchanged — the event is still rejected.
Test to update: the fail-closed case in test/unit/handlers/event-message-handler.spec.ts.
3. The message bucket is not per message type
src/adapters/web-socket-adapter.ts:291 keys on ${client}:message:${period} and is consulted for every inbound message before parsing (:221), so a REQ can be refused because EVENTs filled the same bucket. The spec says the client should back off the same message type that triggered the limit, and honouring that can still be rejected.
Fix: add the message type to the key. It is built before the message is validated, so it needs the raw buffer or a reordering — that is why this is the maybe of the three. Bucket layout changes, so note it for anyone switching strategies mid-flight.
Activity
fix(rate-limit): the backoff a strategy means, an outage named as one…
fix(rate-limit): send the backoff the strategy means, name an outage …