• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cameri / nostream / 41248663387
78%

Build:
DEFAULT BRANCH: main
Ran 31 Oct 2026 10:36PM UTC
Jobs 2
Files 319
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

31 Oct 2026 10:32PM UTC coverage: 77.408% (+0.05%) from 77.363%
41248663387

push

github

web-flow
fix(rate-limit): send the backoff the strategy means, name an outage as one, and bucket messages by type (#1011)

Closes #1006. Follow-up to the spike in #1004.

## 1. The EWMA backoff is now the time the limit actually takes to lift

`RateLimit.period` is the decay half-life under `ewma`
(`src/utils/ewma-rate-limiter.ts`), so the time until an event is
accepted again is `period · log2(R_new / rate)` — and we were sending
`period`. With `rate: 6, period: 60000`, a client four times over the
limit needs 120 s and was told 60 s.

The Lua script now returns its score (`tostring`, because a Lua number
reaches the client truncated to an integer) instead of `0`/`1`, and the
limiter computes the wait. A non-numeric or `nil` answer is a protocol
disagreement, not a score of zero, so it throws and the caller fails
closed rather than silently disabling the limiter.

It was safe only by accident before: the hash carried a TTL of exactly
`period`, so a quiet client's state expired anyway. That coupling was
unstated, and lengthening the TTL to keep state warm would have exposed
the wrong number.

## 2. An outage is an error, not a rate limit

`isRateLimited` caught a backend failure and returned the same value as
a tripped limit, so the client got `rate-limited: slow down` with no
parameter — and every connected client backed off at once for a fault on
this side. The branch now answers:

```
["OK", <id>, false, "error: rate limiter unavailable"]
```

Fail-closed behaviour is unchanged: the event is still rejected. Only
the reason is honest.

## 3. The message bucket carries the message type

One bucket per client meant a `REQ` could be refused because its
`EVENT`s had filled it — the wrong message throttled and the wrong one
told to back off. The bucket is now `<client>:message:<TYPE>:<period>`.

The type comes from a bounded prefix of the frame rather than a full
parse, because the limiter runs before validation and must not be the
thing that spends the parse bud... (continued)

4409 of 6227 branches covered (70.8%)

Branch coverage included in aggregate %.

45 of 48 new or added lines in 10 files covered. (93.75%)

9577 of 11841 relevant lines covered (80.88%)

773.41 hits per line

Uncovered Changes

Lines Coverage ∆ File
1
65.38
0.0% src/controllers/admission/get-admission-check-controller.ts
1
50.0
0.0% src/handlers/request-handlers/rate-limiter-middleware.ts
1
94.29
-5.71% src/utils/ewma-rate-limiter.ts
Jobs
ID Job ID Ran Files Coverage
1 Unit - 41248663387.1 31 Oct 2026 10:38PM UTC 319
74.8
GitHub Action Run
2 Integration - 41248663387.2 31 Oct 2026 10:36PM UTC 319
33.42
GitHub Action Run
Source Files on build 41248663387
  • Tree
  • List 319
  • Changed 11
  • Source Changed 10
  • Coverage Changed 5
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • Github Actions Build #41248663387
  • aa7564cb on github
  • Prev Build on main (#41239856642)
  • Next Build on main (#41250598084)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc