← Back to PRs

#23418: Pairing: add persistent sender and IP backoff controls

by bmendonca3 open 2026-02-22 09:19 View on GitHub →
size: M
## Summary Describe the problem and fix in 2–5 bullets: - Problem: pairing request creation had no sender/IP attempt backoff, so repeated unknown-sender traffic could churn pairing writes and logs. - Why it matters: pairing endpoints are an abuse surface; repeated attempts should be throttled with persistent state. - What changed: added persistent backoff state in pairing store for `sender` and optional `sourceIp`, with normalized IP handling (including IPv4-mapped IPv6), retry windows, and stale-entry pruning. - What did NOT change (scope boundary): approval semantics, allowFrom behavior, code format, or pairing channel policy decisions. ## Change Type (select all) - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Docs - [x] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [x] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [ ] Integrations - [x] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - Closes # - Related # ## User-visible / Behavior Changes Rapid repeated pairing request attempts can now be throttled (`created=false`, `throttled=true`, `retryAfterMs`) for the same sender and for the same source IP when provided. ## Security Impact (required) - New permissions/capabilities? (`No`) - Secrets/tokens handling changed? (`No`) - New/changed network calls? (`No`) - Command/tool execution surface changed? (`No`) - Data access scope changed? (`No`) - If any `Yes`, explain risk + mitigation: ## Repro + Verification ### Environment - OS: macOS - Runtime/container: Node 22 + pnpm - Model/provider: N/A - Integration/channel (if any): Pairing store + inbound access control - Relevant config (redacted): default pairing policy ### Steps 1. Create pairing request for sender A. 2. Clear/approve request and immediately request again for sender A. 3. Observe throttled response; then advance past retry and request again. ### Expected - Immediate repeat attempts are throttled; later attempts after backoff are accepted. ### Actual - Matches expected behavior. ## Evidence Attach at least one: - [x] Failing test/log before + passing after - [ ] Trace/log snippets - [ ] Screenshot/recording - [ ] Perf numbers (if relevant) ## Human Verification (required) What you personally verified (not just CI), and how: - Verified scenarios: sender backoff, IP backoff across senders, TTL expiry flow, pairing CLI + inbound access-control compatibility. - Edge cases checked: IPv4/IPv4-mapped IPv6 key normalization and idempotent behavior for existing pending requests. - What you did **not** verify: full live channel matrix (Discord/Telegram/Slack runtime flows). ## Compatibility / Migration - Backward compatible? (`Yes`) - Config/env changes? (`No`) - Migration needed? (`No`) - If yes, exact upgrade steps: ## Failure Recovery (if this breaks) - How to disable/revert this change quickly: revert this PR. - Files/config to restore: `src/pairing/pairing-store.ts` and related tests. - Known bad symptoms reviewers should watch for: pairing requests unexpectedly throttled when retries happen too quickly. ## Risks and Mitigations - Risk: over-aggressive backoff could suppress legitimate rapid reattempts. - Mitigation: short initial window, bounded max delay, and reset/prune windows. <!-- greptile_comment --> <h3>Greptile Summary</h3> Added persistent backoff controls for pairing request creation to prevent abuse from repeated unknown-sender traffic. The implementation tracks sender and IP-based attempts separately, applies exponential backoff with bounded delays, normalizes IPv4-mapped IPv6 addresses to their IPv4 equivalents, and prunes stale entries after 24 hours. **Key changes:** - Sender backoff: tracks attempts per sender ID within account scope, starts at 15s and doubles up to 15min - IP backoff: tracks attempts per source IP across different senders (when `sourceIp` is provided) - IPv4-mapped IPv6 normalization: `::ffff:203.0.113.10` and `203.0.113.10` are treated as the same IP - Reset window: backoff resets if 10+ minutes pass between attempts - Retention: stale backoff entries are pruned after 24 hours **Test coverage:** - Tests verify sender backoff after approval + immediate reattempt - Tests verify IP backoff across different senders with IPv4-mapped IPv6 normalization - Tests verify TTL expiry for pending requests <h3>Confidence Score: 4/5</h3> - This PR is safe to merge with low risk - The implementation is well-structured with proper exponential backoff logic, IP normalization, and stale entry pruning. Test coverage includes key scenarios (sender backoff, IP backoff, IPv4-mapped IPv6 normalization). The backoff parameters are reasonable (15s base, 15min max, 10min reset window, 24h retention). The feature is additive and backward compatible - existing pairing flows continue to work unchanged. - No files require special attention <sub>Last reviewed commit: 7125652</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs