Skip to content

id(peers): --max-peers is not a hard cap — the crawl inserts a whole ListPeers response unchecked, and the RPC/gossip paths never consult it at all #1765

Open

Description

@dezren39

Problem

id peers --max-peers is documented as a hard cap on total discovered peers, but it is not enforced as one. Three of the four collection paths never consult it, and the fourth (the crawl) checks it only before issuing a query — never while inserting that query's response — so a single ListPeers response can push the result arbitrarily far past the cap.

Evidence (developing-today/code @ 9874fe5, pkgs/id/src/commands/peers.rs unless noted)

  • The contract: module doc :32 — "--max-peers caps total discovered peers (default: 1000)"; PeersOptions.max_peers doc :78 — "Hard cap on total peers discovered."; CLI doc src/cli.rs:1194-1198 — "Maximum total peers to discover. Hard cap to prevent unbounded crawling."
  • cmd_peers initial RPC results are inserted unchecked at :144 (all_peers.insert(peer.node_id, peer.clone())), crawled results unchecked at :159, and gossip results unchecked at :170. max_peers is passed only to crawl_peers (:148-156).
  • discover_via_gossip collects announcements into its own map unchecked at :432 — no cap at all during the collect window.
  • crawl_peers checks all_peers.len() >= max_peers at :296 (loop head) and :313 (before each query), but the response-insertion loop at :322-331 inserts every vacant entry with no cap check. The checks only stop further queries; they never bound what one response adds.

Verification (this run)

Laptop probe ~/git/fleet/probe-2026-10-28-laneA-peers-maxcap/ (cargo, laptop): crawl_peers copied structurally verbatim with the peer query stubbed (seed peer answers one response of 100 fresh peers; ID types reduced to u64):

  • Case 1 — crawl with max_peers=10, depth 1, one 100-peer response → collected 101 peers (10× the "hard cap").
  • Case 2 — the cmd_peers initial-insert loop shape with a 2,000-peer RPC result and the default cap 1000 → all 2,000 retained.

Note the response itself is separately bounded today only by the 1 MiB read in the hand-rolled clients (see #1761, ~20k peers max per response) — so one response can overshoot the default cap ~20×, and --max-peers 50 (a value the CLI tests exercise, cli.rs:2747-2759) provides almost no protection at all. The overshoot lands in memory, in the sorted output, and — for crawl results — in the next frontier.

Why it matters

--max-peers is the user's only stated safeguard against unbounded crawling (per its own CLI help). A malicious or merely large peer can hand back a huge peer list and the client will absorb, store, sort, print, and crawl from all of it regardless of the flag. Users who set a small cap for a quick, bounded probe get unbounded work instead.

Acceptance criteria

  • Total peers returned/printed by id peers never exceeds --max-peers in any mode (RPC, gossip, default, any --depth), or the docs/help are corrected to state the real, weaker guarantee and a true bound is enforced elsewhere — triage pins which.
  • The crawl insertion loop enforces the cap while inserting a response (stops at the cap, and the existing "cap reached" warning fires in that case too).
  • The initial RPC insert, the crawl-result merge, the gossip merge in cmd_peers, and the discover_via_gossip collect loop all respect the same cap.
  • Regression test: a single response larger than the cap yields exactly the cap (extraction of the collection logic for testability is acceptable if the network-facing shape is preserved).
  • Existing peers / CLI tests pass (laptop or CI).

Activity

  1. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Oct 28, 2026
  3. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (new-issue-creator-code-a)
    • Session / ID: new-issue-creator-code-a run 2026-10-28 13:05 CDT
    • Found via: filed this run
    • Task: Filed after verifying on current main (9874fe5) that id peers --max-peers is not enforced at any insertion site; laptop probe (probe-2026-10-28-laneA-peers-maxcap) reproduced a 10x cap overshoot from a single response. Triage packet enqueued to repo-health-code-pilot.
    • Expectation: effort/s — triage pins enforce-everywhere vs doc-correction, then the bounded fix + regression test in the issue's acceptance criteria.
    • Links: evidence + probe details in the issue body
  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  5. 22 remaining items

  6. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state=triaged (work in flight, duplicate-tracking), holder: repo-worker-code (openchamber scheduled lane 02cafe76), queue task 7457, at 2026-11-03T11:02Z.

    Fresh state check — the fix exists but is not on main yet, and it sits in a PR this worker must not touch:

    • PR id(peers): enforce the global --max-peers hard cap at every collection site (#1765) #3750 ("…--max-peers cap…", head queue/4794-1765-max-peers-cap, head 48b4473) is OPEN, authored by dezren39 (Drew-owned per fleet protocol) — no review edits, no merge, no force-push by this worker.
    • Checks on id(peers): enforce the global --max-peers hard cap at every collection site (#1765) #3750: only CodeRabbit — skipping; gh pr checks reports no other runs. mergeable is UNKNOWN. The PR has no CI evidence of record, which is worth a signal on it when it is next picked up — flagging here rather than editing the PR.
    • Current main 8e76c6fa5 still carries the defect exactly as filed: peers.rs:158 inserts an unchecked RPC result set, and the crawl response loop (~:370-378) inserts every vacant entry with no cap check — the all_peers.len() >= max_peers guard at :359 only stops further queries.
    • No live queue task exists for this issue (queue/needs-review + status/needs-review were a label/queue desync; the queue's needs-review count is 0). Labels repaired: state labels cleared, user/review added so the awaiting-review state is visible in the right lane.

    Resume condition: PR #3750 merges (it should close this issue) or ownership is reassigned; on merge, re-verify the cap at :158 and the crawl insert loop on fresh main.

  7. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    state: triaged (coverage pass)
    Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 40 (priority/normal). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
    Found via: coverage pass 2026-11-03

  8. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  9. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  10. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    Triage verdict (task 7720, 2026-11-03): still live on main, fix already in flight — do not reimplement

    Confirmed still live at main 91c3b1938 (pkgs/id/src/commands/peers.rs):

    • :158 — initial RPC results inserted into all_peers unchecked.
    • :173 — crawl results merged unchecked.
    • merge_gossip_peers (:213-224) — inserts with no cap parameter at all.
    • crawl_peers insertion loop (:374) — Vacant entry inserted with no cap check; the cap is consulted only at the loop head (:342) and before each query (:359), exactly as this issue describes.
    • discover_via_gossip collect path never references max_peers.

    But the fix already exists: PR #3750 — "id(peers): enforce the global --max-peers hard cap at every collection site (#1765)", branch queue/4794-1765-max-peers-cap, opened by task 4794 — adds an offer_peer helper enforcing the cap at every insertion site (crawl seeds, crawl response merges, gossip collection, all cmd_peers merges), duplicate-refresh semantics, cap-reached warnings, and regression tests (over-cap single response, zero cap, duplicate-at-cap, bounded seeds).

    Routing: PR #3750 is the canonical fix path for this issue. It is OPEN but stale (last updated 2026-11-01) and its check rollup shows only a CodeRabbit neutral run — no CI verification of record yet. The remaining work is to revive/verify PR #3750 (rebase onto current main, get CI green, disposition any review findings, merge under the standing criteria), NOT to write a second implementation. This triage creates no competing fix task; the PR's own queue binding (task 4794 / fleet monitor PR binding) owns the follow-through.

  11. removed
    user/reviewNeeds a user-look: Muse reviews first and handles what it can
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions