Skip to content

feat(id): configurable abuse budgets and per-counterparty overrides (#2247) - #4338

Merged
dezren39 merged 1 commit into
mainfrom
queue/5936-2247-abuse-budgets
Nov 2, 2026
Merged

dezren39 merged 1 commit into
mainfrom
queue/5936-2247-abuse-budgets

Conversation

@dezren39

@dezren39 dezren39 commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Implements #2247 (P1 of #2032/#2033), on top of the merged P0-A integration (#3882, PR #3960 — verified merged 2026-11-02T06:35:09Z before starting; issue #2244's integration is on main via that PR).

What

  • abuse.rs — BudgetPolicy: rolling-window budgets (sliding-window approximation, O(1) state per scope) at app-wide inbound/outbound, per-endpoint, and per-direct-IP scope, plus per-counterparty override rows that replace the per-endpoint defaults for one specific peer (cap or raise one peer without touching the global budget). Defaults are the id(comms): budgets — app-wide, per-IP, per-ID, and per-counterparty overrides (P1 of #2032) #2247 values: app in 32 MiB/s + 20 GiB/h, out 64 MiB/s + 40 GiB/h; per peer 100 MiB/h, burst 8 MiB/min; per IP 250 MiB/h.
  • Evaluator: AbuseEvaluator::with_budgets / check_budget / check_frame_and_bytes_budgeted — atomic with the existing token buckets (nothing is charged unless every check admits). Refusals are the shared BudgetExhausted Refusal with a bounded retry_after_ms, never a disconnect. budget_usage() is the observability surface; prune_budget_state() bounds retained state. Evaluators with no budget policy configured behave exactly as before (world chat included).
  • frames.rs — charge_frame_budgeted seam. The frame seam observes no IP, so the per-IP scope is charged only by callers that directly observe an IP — a relayed peer's IP is never inferred.
  • world_net.rs — serve wiring: WorldProtocol enforces BudgetPolicy::default() (or with_budgets(...)) at its inbound/outbound frame seams; budget refusals ride the existing backpressure / stream-shed paths — the peer connection stays up. WorldProtocol::budget_usage() exposes the counters.
  • server_config — [budgets] in id-server-config.toml: per-knob overrides of the code defaults plus [[budgets.counterparty]] rows (peer = a declared user key or a 64-hex public key, bytes_per_window, window_seconds, optional messages_per_window). Zero values and duplicate peers are refused at parse time. ServerConfig::budgets() returns the typed BudgetPolicy.

Known remainder (honest scope note)

ServerConfig as a whole is parsed-but-not-yet-loaded by the serve command (pre-existing gap noted in the 2026-10-29 comms investigation, it affects every table, not just budgets). This PR wires budgets into the serving protocol object with code defaults and provides the parsed policy accessor; loading the config file in cmd_serve is the remaining step for the whole config, best done once for all tables rather than budgets-only.

Tests

New unit tests (CI runs them): budget defaults match #2247; per-peer exhaustion + bounded retry + refused charges not recorded; override precedence in both directions; message caps; direct-IP sharing across endpoints vs relayed traffic skipping the IP scope; app-wide scope spanning peers with direction separation; window-roll recovery + pruning; atomic budgeted frame charge; unconfigured evaluator unchanged; config defaults/partial override/counterparty resolution (user key + raw hex)/duplicate, zero, and unknown-peer refusals.

Queue: task 5936 (repo-health-code-pilot), lane C. PR #3834 was not touched.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

…2247)

P1 of #2032/#2033 on top of the merged P0-A integration (#3882, PR #3960):

- abuse.rs: BudgetPolicy with rolling-window (sliding-window
  approximation) BudgetLimits at app-wide inbound/outbound,
  per-endpoint, and per-direct-IP scope, plus counterparty override
  rows that replace the per-endpoint defaults for one peer. Defaults
  are the #2247 values (app in 32 MiB/s + 20 GiB/h, out 64 MiB/s +
  40 GiB/h, per peer 100 MiB/h burst 8 MiB/min, per IP 250 MiB/h).
  AbuseEvaluator::with_budgets / check_budget /
  check_frame_and_bytes_budgeted charge atomically with the token
  buckets; refusals are the shared BudgetExhausted Refusal with a
  bounded retry. budget_usage() is the observability surface and
  prune_budget_state() bounds retained state. Evaluators without a
  configured budget policy behave exactly as before.
- frames.rs: charge_frame_budgeted seam; no IP is observed at the
  frame seam, so the per-IP scope is only charged by callers that
  directly observe an IP - a relayed peer IP is never inferred.
- world_net.rs: WorldProtocol enforces BudgetPolicy::default()
  (or with_budgets(...)) at its inbound/outbound frame seams;
  budget refusals ride the existing backpressure/stream-shed paths,
  never a connection close.
- server_config: [budgets] table in id-server-config.toml with
  per-knob overrides of the code defaults and [[budgets.counterparty]]
  rows (peer = declared user key or 64-hex public key), validated
  (zero values and duplicate peers refused). ServerConfig::budgets()
  returns the typed BudgetPolicy.

Tests: budget defaults, per-peer exhaustion + bounded retry,
override precedence both directions, message caps, direct-IP
sharing vs relayed skip, app-wide scope/direction separation,
window-roll recovery, atomic budgeted frame charge, unconfigured
evaluator, and config parsing/defaults/errors.
@dezren39
dezren39 requested a review from a team as a code owner November 2, 2026 11:26
@coderabbitai

coderabbitai Bot commented Nov 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 42 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4109db0d-c21f-4009-838f-582b7d52136c
Commits

Reviewing files that changed from the base of the PR and between 734e8c5 and f25156a.

Files selected for processing: 5
  • pkgs/id/src/abuse.rs
  • pkgs/id/src/frames.rs
  • pkgs/id/src/server_config/mod.rs
  • pkgs/id/src/server_config/parse.rs
  • pkgs/id/src/world_net.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T11:30:18.386956Z f25156a PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: f25156afae

ℹ️ 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".

Comment thread pkgs/id/src/world_net.rs
Comment on lines 356 to +362
let retry_after_ms = match abuse.lock() {
Ok(mut abuse) => match charge_frame(
Ok(mut abuse) => match crate::frames::charge_frame_budgeted(
&mut abuse,
endpoint,
is_authorized,
payload_len,
Direction::Inbound,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Account for budget backpressure in module-upload deadlines

On a fast Iroh module upload, the newly enforced default per-peer budget exhausts its 8 MiB/min allowance after roughly 4 MiB of hex-encoded Wasm. charge_inbound_frame then waits for the budget to recover, but install_module_session in pkgs/id/src/world_session/mod.rs:1909–1911 still times out each io.recv() after 30 seconds. The budget-induced pause can exceed that deadline, so otherwise valid uploads below the supported 16 MiB maximum fail with timed out receiving module. Coordinate the upload deadline with limiter-induced backpressure so enabling these defaults does not break supported uploads.

Useful? React with 👍 / 👎.

Comment thread pkgs/id/src/abuse.rs
Comment on lines +903 to +905
BudgetUsage {
bytes: counter.estimate_bytes(now_ms),
messages: counter.estimate_messages(now_ms),

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 Roll budget counters before reporting current usage

When a scope has received no further charges, budget_usage(now_ms) estimates its counters without first rolling them to now_ms. estimate_bytes and estimate_messages always include the stored current-window counts, so those charges remain visible even after both windows have expired. For example, charging 100 bytes at time zero and requesting usage two hours later still reports 100 bytes for every default window. This also affects WorldProtocol::budget_usage(), making idle scopes appear to retain consumed budget. Roll a copy of each counter before calculating the snapshot.

Useful? React with 👍 / 👎.

@dezren39
dezren39 merged commit 7cbfc4f into main Nov 2, 2026
2 of 4 checks passed
@dezren39
dezren39 deleted the queue/5936-2247-abuse-budgets branch November 2, 2026 15:28
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.

1 participant