Skip to content

Broker: thread-safe rate limiters + stale-key eviction#118

Merged
adamcohenhillel merged 1 commit into
mainfrom
fix/broker-rate-limiter-safety
Jul 5, 2026
Merged

Broker: thread-safe rate limiters + stale-key eviction#118
adamcohenhillel merged 1 commit into
mainfrom
fix/broker-rate-limiter-safety

Conversation

@adamcohenhillel

Copy link
Copy Markdown
Contributor

Summary

  • Guard RateLimiter.allow() and ByteRateLimiter.allow() with a per-instance threading.Lock so concurrent requests under ThreadingHTTPServer cannot race past the limit checks.
  • Opportunistically evict stale limiter keys (empty deques or newest event older than the window) at most once per minute inside allow(), preventing unbounded memory growth without background threads.
  • Compare bearer tokens against every configured session token with hmac.compare_digest (no short-circuit on match position) instead of a non-constant-time in set lookup.

Closes #82

Test plan

  • ./scripts/check.sh passes
  • python3 -m py_compile services/model-broker/openphone_model_broker.py
  • Inline sanity check: 50 threads x 100 allow() calls against a 30/min limit admit exactly 30 events
  • Inline sanity check: stale keys evicted from both limiters after the window elapses; fresh keys retained

🤖 Generated with Claude Code

RateLimiter and ByteRateLimiter mutated shared deques without a lock
under ThreadingHTTPServer, letting concurrent requests exceed limits,
and their key dicts grew without bound. Guard allow() with a per-limiter
lock, opportunistically evict stale keys, and compare session tokens
with hmac.compare_digest to avoid timing leaks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openphone-docs Ready Ready Preview, Comment Jul 4, 2026 2:18am

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f484dd475

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

for session_token in self.config.session_tokens:
# Check every configured token so the comparison time does not
# depend on where (or whether) the match occurs.
token_matches |= hmac.compare_digest(token, session_token)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject non-ASCII bearer tokens without crashing

When any static session token is configured, a malformed Authorization: Bearer ... value containing non-ASCII bytes now reaches hmac.compare_digest(token, session_token), which raises TypeError for non-ASCII str inputs. That turns an unauthenticated request to /v1/responses or /v1/audio/transcriptions into an unhandled handler exception/connection drop with no audit event instead of the previous 401 path; validate or encode the bearer token before comparing so bad tokens are rejected normally.

Useful? React with 👍 / 👎.

return False
events.append(now)
return True
with self._lock:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Capture limiter time after acquiring the lock

Because now and cutoff are captured before entering this newly added lock, a request that waits here while another thread is evicting/checking a large limiter map can make its decision using an already-stale window. If entries expire during that wait, they remain counted for this request and can incorrectly return 429 or byte-limit a valid request; the same pre-lock timestamp pattern exists in ByteRateLimiter.allow().

Useful? React with 👍 / 👎.

@adamcohenhillel adamcohenhillel merged commit e90c7c9 into main Jul 5, 2026
3 checks passed
@adamcohenhillel adamcohenhillel deleted the fix/broker-rate-limiter-safety branch July 6, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Broker: thread-safe rate limiters + stale-key eviction

1 participant