Skip to content

id: investigate: comms abuse controls — blocks, rate limits, and per-counterparty budgets #2032

Closed

Description

@dezren39

LIST 8. ID comms must carry many small packets (keypresses, live Google-Docs-style interaction) without spam/DoS. Investigate the built-in control set (large installs may add WAFs/load balancers, but these ship in the box): IP block; one-button common block lists; rate limiting; Retry-After semantics; permanent blocks by public IP, by ID/public key, or by topic/tag; configurable budgets — max MB per window across the whole app, per IP, or per ID, and message/MB caps for a SPECIFIC counterparty, not just any counterparty. Deliverable: control inventory with defaults proposed; 'we don't need everything — start along this path.'

Program: Drew's 2026-10-28 directive (full text: hatch-personal memory/topics/id-program-2026-10-28.md).
Working pattern: parallel investigations, then the named synthesis issue merges them into a full design. Route guidance: Workhorse by default; Specialist/Summit for synthesis and ramification-heavy work. A result that says "done" is not the endpoint — the orchestrator reviews it and may commission another round. Deliverables: a markdown design/investigation doc (mermaid/SVG diagrams welcome) linked from this issue, plus follow-up issues for anything buildable. Build a proof of concept where the design calls for one, but do NOT build the permanent feature on top of an unreviewed PoC.

Activity

  1. added
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Parent epic: #2056 — ID platform expansion (2026-10-28 directive).

  4. added
    priority/highImportant and time-sensitive
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 29, 2026
  5. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Top priority — ID program wave-1. Drew directed this be picked up in the next runs (2026-10-29): enqueued directly as triage task 3347 in repo-health-code-pilot at queue priority 20 (only breakage-class work at priority 10 outranks it). Wave-1 is the ungated investigations and designs; syntheses and PoCs stay gated on their named inputs. Investigation route: Workhorse.

  6. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 29, 2026
  7. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor
    event: picked_up
    task: 3347
    worker: repo-worker-code
    lane: openchamber-scheduled-lane
    queue: repo-health-code-pilot
    at: 2026-10-29T10:41:00Z
    

    Picked up as wave-1 triage task 3347 (priority 20). Plan: verify the premise against the current id comms implementation, write the control inventory + proposed defaults as a doc under doc/, link it here, and route buildable follow-ons in the same pass. Investigation route: Workhorse.

  8. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Investigation complete — control inventory, defaults, and the start path

    Full record: doc/2026-10-29T10-55-00Z_investigation_comms_abuse_controls/ (PR #2243, docs-only).

    Verdict: ID has no abuse-control layer. It has five unrelated local limiters — frame cap 1 MiB (frames.rs:20), world chat 50 lines / 10 s per participant (world/mod.rs:53-55), WorldLimits, web save 1 per 5 s per file name (web/mod.rs:86-132), directory-auth resend 1/min + nonce limits (directory_auth.rs:24-29) — none configurable, none keyed on a counterparty, none returning Retry-After. Every control Drew listed except raw frame size is absent, and there is no byte accounting per peer, so "max MB per window" cannot be enforced even in principle today.

    Why the shape is different than expected: ID's comms are Iroh/QUIC (RPC frames, docs gossip, MessagePack collab WebSocket, world frames). There are no HTTP headers for Retry-After, no socket per peer for iptables, and a relayed peer's IP is not visible at all. So:

    • L1 — frame-boundary token buckets per (local node, remote endpoint key): 60 frames/s burst 180, 512 KiB/s burst 4 MiB for authorized peers; 10 frames/s burst 30 before authorization; 4 connections/peer. Refusals carry {code, retry_after_ms} inside ID's own frame (HTTP routes get real 429 + Retry-After).
    • L2 — one operator block list, three key kinds: public key (reliable; stops serving/subscribing/rendering), public IP (listener-only, best-effort, useless when relayed), topic/tag (scoped, composes with PullPolicy).
    • L3 — budgets: app-wide 32 MiB/s in / 64 MiB/s out, per peer 100 MiB/h, per IP 250 MiB/h, plus per-specific-counterparty override rows — the issue's key distinction from a generic per-peer cap.
    • L4 — IP deny at the listener + list import. No shipped "common block list": IP lists rot in hours and relayed peers evade them; the honest version is one-click "block this peer" plus import of a list the operator already trusts.

    Trust-model composition (#2030): blocks never reject a signed object — they change serving, subscribing and rendering, so graphs do not diverge. Rate limits and budgets reject work, not content. Only unauthorized is ever about the object.

    Start along this path (P0/P1/P2): P0 = per-peer counters + token bucket + one structured refusal type, and block-a-peer-by-public-key with the web UI button (the control an operator reaches for during an incident). P1 = budgets once counters exist, and 429/Retry-After hygiene on id serve (which also closes #1766 and the intent behind #1713). P2 = listener IP deny set, tag/topic-scoped blocks, block-list import. Not now: shipped block lists, federation, reputation scoring.

    Routed follow-ons (filed from this investigation, feeding #2033): per-peer accounting + refusal shape; operator block list + UI; budgets. Open questions for the design are §7 of the record (bucket keying under key rotation, relay-attested addresses, per-group block lists, frame-vs-byte budget units, refuse-and-keep vs refuse-and-close).

    Nothing here is a permanent feature built on an unreviewed PoC — it is an investigation with defaults and a build order, for #2033 to synthesise.

  9. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Follow-ons filed from this investigation: #2244 (P0 — per-peer frame/byte accounting + structured rate-limit refusal, the prerequisite for all budgets), #2245 (P0 — operator block list by public key with one-button block in the web UI), #2247 (P1 — budgets incl. per-counterparty overrides). Existing gaps already filed and linked rather than duplicated: #1766 (unthrottled register handler), #1713 (SaveRateLimiter never evicts).

  10. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor
    event: done
    task: 3347
    worker: repo-worker-code
    lane: openchamber-scheduled-lane
    at: 2026-10-29T10:52:00Z
    outcome: pr_opened
    

    Investigation complete. Record: doc/2026-10-29T10-55-00Z_investigation_comms_abuse_controls/ via PR #2243 (docs-only). Summary comment above. Follow-ons routed: #2244, #2245, #2247; synthesis input posted to #2033. Labels cleared — nothing in flight.

  11. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Correction pass on the record (second commit on PR #2243) after a full sweep of the tree — four claims in my first summary were too strong, and one gap was missing entirely:

    • 429 does exist on two web paths (web/world_ws.rs:209, web/routes.rs:1212); what is absent is Retry-After and any delay value in ID's own frames. Zero matches for retry_after in src/ and web/src/.
    • An allow-list exists — AccessPolicy / .iroh-allowed / --allow-node (access.rs:57-105), for writes only. It is the inverse of a block list, not a block list.
    • id-server-config.toml is parsed but dead: ServerConfig::parse/::load have no caller and id serve has no --config. Any [abuse] table also needs the config file wired up for the first time.
    • Two per-sender caps do exist (WaitingLimits::per_author = 256, MAX_PENDING_FRIEND_REQUESTS = 20), both count-based and hard-coded.
    • New P0 gap found: the collaboration WebSocket (web/collab.rs:632) sets no max_message_size and nothing counts STEPS/CURSOR — a live-document session is currently the cheapest way to flood a peer. It is now P0 in the build order.
    • Two gates are designed and unwired (blob_gate, PullGate) and are the natural homes for block policy; §8 now cites the prior design records that already asked for per-author caps and rate limits (2026-10-11 groups/events delegation, 2026-10-10 directory sync, 2026-03-22 peer discovery).
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)priority/highImportant and time-sensitivestatus:done

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions