Skip to content

fix(tracker): limit segment reports per peer across REST and WebSocket - #139

Merged
mytrashcan merged 3 commits into
mainfrom
fix/tracker-inventory-rate
Oct 20, 2026
Merged

mytrashcan merged 3 commits into
mainfrom
fix/tracker-inventory-rate

Conversation

@mytrashcan

@mytrashcan mytrashcan commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Problem

Inventory reports are relayed as received to the reporter's neighbors (64 by default), or to every subscriber for legacy clients. Two gaps:

  1. WebSocket: the only limits were 64 KiB frames and 20 messages per second. One connection could make the tracker relay about 1.3 MB/s to each neighbor.
  2. REST: POST /api/v1/broadcasts/:id/peers/:peerId/segments accepted any number of IDs of any length, up to the 1 MB body limit, and relayed them to WebSocket subscribers. That bypassed even the WebSocket per-report limits.

#138 bounds what the store keeps, but not what is relayed.

Change

  • New tracker/src/inventory-limits.ts:
    • segmentIdList: at most 512 IDs, each 1 to 512 characters. The WebSocket hub already enforced this; REST now does too.
    • InventoryRateLimiter: a token bucket of segment-ID characters per broadcast peer, 16 K/s sustained with bursts of 256 K. The burst fits a full re-report after a reconnect. Idle buckets are swept.
  • TrackerServer creates one limiter and passes it to the REST handler and the WebSocket hub, so a peer's budget is shared across both interfaces.
  • Over the rate, the WebSocket closes with 1008 ("Segment report rate limit exceeded") and REST answers 429.
  • Legitimate clients report a few IDs per segment, or tens of KB once on reconnect, so they are far below the limit.
  • API_REFERENCE documents all WebSocket and report limits.

Tests

  • Unit tests for segmentIdList and for InventoryRateLimiter: burst, refill, cap, per-peer isolation, and no charge on refusal.
  • REST: 400 for an oversized ID, 429 once the shared budget is spent (including spending through the limiter directly, as the WebSocket path does), and recovery after refill.
  • End-to-end WebSocket test: the sixth 60 K report closes the socket with 1008.
  • tracker: 67 pass.

Inventory reports are relayed as received to each of the reporter's neighbors (64 by default), and the only limits were 64 KiB frames and 20 messages per second, so one connection could make the tracker relay over a megabyte per second to every neighbor. Each connection now also has a token bucket of segment-ID characters: 16 K per second, bursts of 256 K for a full re-report after reconnecting. Over it, the connection is closed with 1008. Clients report a few IDs per segment, far below this. API_REFERENCE now lists the WebSocket limits.
@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation tests PRs that add or modify tests labels Oct 20, 2026
# Conflicts:
#	CHANGELOG.md
The REST segment report accepted any number of IDs of any length (up to the 1 MB body) and relayed them to WebSocket subscribers, bypassing both the WebSocket report limits and the new rate limit. Segment ID validation and an InventoryRateLimiter keyed by broadcast and peer now live in inventory-limits.ts; TrackerServer passes one limiter to the REST handler and the WebSocket hub, so a peer's reports count once whichever interface it uses. Over the rate, REST answers 429 and the WebSocket is closed with 1008.
@mytrashcan mytrashcan changed the title fix(tracker): limit the rate of reported segment IDs per WebSocket fix(tracker): limit segment reports per peer across REST and WebSocket Oct 20, 2026
@mytrashcan

Copy link
Copy Markdown
Owner Author

Self-review:

  • One InventoryRateLimiter per TrackerServer is shared by REST and WebSocket, keyed by broadcast and peer, so switching interfaces doesn't reset the budget.
  • Refusals charge nothing. Idle buckets are swept every 1024 consumes, after they have refilled.
  • REST now applies the same 512 × 512 report bounds as WebSocket.
  • Legitimate traffic (SDK and Node deltas, load-test replacements of a few KB, capacity benchmark single IDs) stays well under 16 K/s.
  • CI is green, including the Docker peers and all browser E2E jobs.

@mytrashcan
mytrashcan merged commit 7d79f0e into main Oct 20, 2026
16 checks passed
@mytrashcan
mytrashcan deleted the fix/tracker-inventory-rate branch October 20, 2026 00:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation tests PRs that add or modify tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant