Repository navigation
Description
Summary
The shared abuse evaluator's per-peer concurrent connection cap is implemented and unit-tested but never wired into any transport. AbusePolicy::max_connections_per_peer (default 4) and AbuseEvaluator::open_connection / close_connection exist only for abuse.rs's own tests: no production code path calls them. The P0-A integration (PR #3960, for #3882) wired frame/byte charging into WorldProtocol, but WorldProtocol::accept never registers a connection with the evaluator — so a single remote endpoint can open an unbounded number of QUIC connections to the world protocol, each allowed its own 16 sessions, and nothing ever refuses the 5th, 50th, or 500th connection.
Evidence (verified this run, 2026-11-02, main @762f32af)
pkgs/id/src/abuse.rs:237—AbusePolicy.max_connections_per_peer;:255— default value 4;:625— a test pins the default.pkgs/id/src/abuse.rs:433-460—open_connectionenforces the cap (returns aRateLimitedrefusal at the cap) andclose_connectionreleases it; unit tests at ~:661-687 exercise cap/refusal/release.- Caller search across the whole crate (run independently on the sandbox clone @762f32af and on the laptop clone @12ea94d17, same result):
grep -rn "open_connection|close_connection" pkgs/id/src --include="*.rs"returns no callers outsideabuse.rs(definition + its own tests only). File md5s:abuse.rs52884455f31e10e13e0da898053fc872,world_net.rs4692a53fb89c3600be435f0e8adad7e1. pkgs/id/src/world_net.rs:129-158—WorldProtocol::acceptprunes abuse state, then loops onconn.accept_bi()with only a per-connection session semaphore (MAX_SESSIONS_PER_CONNECTION = 16, :54, :136). It clones the shared evaluator into each session for frame/byte charging but never callsopen_connection/close_connection, and iroh invokesacceptonce per incoming connection with no per-remote accounting anywhere else in the handler.- Contrast: the SSH transport does bound connections globally (
pkgs/id/src/world_ssh.rs:71MAX_CONNECTIONS: usize = 64), so the world QUIC transport is the unbounded one — and its bound was supposed to be per-peer, via the evaluator.
Why it matters
The evaluator's module doc states the design premise (#2244 P0-A): an endpoint's allowance is a property of the endpoint, "never multiplied by opening more connections to it", and the evaluator owns "the concurrent connection cap per peer". Frame/byte buckets are indeed shared per endpoint, but connections themselves are the resource a flooding peer spends on the host: each accepted connection carries an accept task, a JoinSet, QUIC connection state, and up to 16 session tasks with reader tasks — all before any frame is charged (a peer that opens connections and sends nothing is never charged at all). The cap that was designed, implemented, and tested for exactly this is dead code in production, so the pre-auth connection-flood surface the #2032/#2033 design set out to close is still open on the world protocol.
Distinct from #3927 (oversize requests get a temporary refusal from check_bytes/check_frame — a refusal-contract issue inside the evaluator) and from #4004/#3991 (chat-presence state bounds). This is about the evaluator's connection-count contract having no consumer.
Acceptance criteria
-
WorldProtocol::accept(and any other transport the integration charges frames on) registers each incoming connection with the shared evaluator (open_connectionkeyed by the remote endpoint) and releases it (close_connection) when the connection ends, on all exit paths. - A connection refused by the cap is rejected/closed without spawning session tasks, and the refusal is logged/counted so operators can see cap hits.
- Regression test: one endpoint opening
max_connections_per_peer + 1concurrent connections to aWorldProtocolsees the extra connection refused, while a second endpoint is unaffected; after one connection closes, a new one is admitted. - If the intended scope is deliberately narrower (e.g. the cap is meant only for a future transport), the policy field and evaluator doc say so explicitly instead of presenting a cap no transport enforces.
Related: #2244 (parent), #3881 (core, closed — introduced the cap), #3882 / PR #3960 (integration, closed/merged — wired frame/byte charging but not the connection cap), #2033 (design).
Activity
dezren39 commentedon Nov 2, 2026
🤖 Agent note · filed
8 remaining items
dezren39 commentedon Nov 2, 2026
Queue task 6389 overlap check: fresh diff of open PR #3896 (updated 2026-11-02 19:27Z) changes
WorldClientand adds the client-sideWorldSessionscoordinator/tests inworld_net.rs; it does not changeWorldProtocol::acceptor callAbuseEvaluator::open_connection/close_connection. It does not implement #4227's server-side per-peer connection cap, so this fix remains distinct. No hard-stop labels on #4227.dezren39 commentedon Nov 2, 2026
agent-note:v1
event: pr_opened
task: 6389 (repo-health-code-pilot; worker repo-worker-code; OpenChamber scheduled lane aea12c82-c5af-4e5a-9b66-f8e360f87177)
result: PR #4573 is OPEN at
657e6fd535f1e7e683ff5fb36f5275ade01fb9b4; exact implementation and cap regression test are in the PR. Targeted tests passed on the merged base: abuse 19/19 and world_net 21/21.ci:
basic: OpenCode metered-provider guardis QUEUED..github/workflows/id-check.ymlhas no pull_request trigger, so the affected-crate PR matrix is not present; coverage gap is noted in the PR description.next: Recheck checks within the five-minute window; park if still pending.
dezren39 commentedon Nov 2, 2026
agent-note:v1
event: parked
task: 6389 (repo-health-code-pilot; worker repo-worker-code; OpenChamber scheduled lane aea12c82-c5af-4e5a-9b66-f8e360f87177)
pr: #4573 OPEN at 657e6fd. Tests on merged main passed: abuse 19/19; world_net 21/21.
reason:
basic: OpenCode metered-provider guardremains QUEUED after active polling. The policy-required basic proxy and id matrix checks are absent becausenix-ci.ymlandid-check.ymllackpull_requesttriggers; this is documented on PR #4573.resume: After the 20-minute park, freshly recheck #4227, PR #4573 and checks. Do not repeat tests unless the head changes.
labels: parked; work/assigned retained.
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done