Skip to content

id(abuse): surface per-peer frame/byte counters to operators (metrics or id peers) #4918

Open

Description

@dezren39

Residual gap found while triaging #2244 at main 1e3763f (both phases shipped: #3891 core, #3960 integration).

Problem

#2244's "Done when" requires that "the counters are visible" so an operator can tune the limits they can see. Today the per-(local node, remote endpoint) frame/byte counters exist and are correct, but their only reader is Rust code:

  • WorldProtocol::abuse_counters() (pkgs/id/src/world_net.rs:156) returns Vec<((NodeId, EndpointId), Counters)>.
  • It has no other caller in the tree (git grep abuse_counters -- pkgs/id/src), no CLI verb prints it, and no metrics/status endpoint exposes it.

So an operator running id cannot see any counter value, which means the tuning half of the issue is not reachable without writing code.

Work

  • Pick the surface the design allows (metrics or id peers): add a counter section/flag to id peers (pkgs/id/src/commands/peers.rs) backed by an abuse_counters() snapshot from the live protocol, or expose the same snapshot through the existing metrics/status route used for world state.
  • Include both directions (inbound/outbound frames and bytes), the endpoint id in the same rendering id peers already uses, and the policy values (rate/burst) alongside so a value can be judged relative to its bucket.
  • Keep it read-only and bounded: snapshot per invocation, no unbounded state retention; reuse CounterRegistry::prune retention semantics rather than adding a second lifetime.
  • Test: a snapshot with two endpoints renders both rows deterministically, and the output stays stable when counters are empty.

Related: #2244 (closed by this triage), #3881, #3882, #2033 (design, doc/2026-11-01_design_id2033.md), #2247 (budgets consume the same counters).

Activity

  1. added
    source/queue-workerFiled or worked by the queue/worker pilot
    priority/lowNice to have; do when convenient
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    task/fixhas a fix task queued
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  3. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Implementation brief — 2026-11-03 (repo-health queue, repo-worker-code)

    Triage at ffad69add. The gap is exactly as described: WorldProtocol::abuse_counters() (pkgs/id/src/world_net.rs:181) has no non-test caller, pkgs/id/src/commands/peers.rs renders no counters, and no web route exposes them.

    One acceptance detail needs a decision first

    The issue asks for both directions. The shipped registry does not record them separately: CounterRegistry::record (pkgs/id/src/abuse.rs:564) aggregates one Counters { frames, bytes } per (local_node, remote_endpoint) and ignores the Direction the charging path already carries (frames::charge_frame_budgeted → check_frame_and_bytes_budgeted, both Direction::Inbound and Direction::Outbound land in the same row). So "render inbound and outbound" is either:

    1. Split the registry by direction (recommended, contained): CounterEntry holds inbound/outbound Counters; record takes the Direction its callers already have; snapshot returns both plus the total. Call sites: pkgs/id/src/frames.rs:182, pkgs/id/src/world_net.rs:421/464. Existing Counters consumers keep the aggregate. ~1 file + 3 call sites + tests.
    2. Render the aggregate and note it is combined — smaller, but does not meet the issue's wording.

    Surface and plumbing (the expensive half)

    id peers is the cheaper surface to document but not to feed: the CLI talks to a serve over META_ALPN, and MetaProtocol::new (pkgs/id/src/protocol.rs, constructed at pkgs/id/src/local.rs:194 and in serve.rs) has no handle to the world abuse evaluator. Either surface needs one handle threaded:

    • serve.rs builds WorldProtocol::new(hub.clone()) inline inside the router builder (pkgs/id/src/commands/serve.rs:1191). Hoist it to a let world_protocol = ..., keep a clone, and either
      • pass Arc<Mutex<AbuseEvaluator>> (or a small AbuseSnapshot closure) into MetaProtocol and answer a new MetaRequest::AbuseCounters → MetaResponse::{rows, policy} from the id peers --abuse path (pkgs/id/src/commands/peers.rs:255 already does the ListPeers round-trip), or
      • add GET /api/abuse next to /api/peers in pkgs/id/src/web/routes.rs:333 with the same handle in web state.
    • Policy values for context: AbuseEvaluator::policy() is not exposed today; add a pub fn policy(&self) -> AbusePolicy (or return BucketLimits for the authorized bucket) so the rendering can show rate/burst next to each row.
    • Rendering: reuse EndpointId formatting already used by id peers; sort rows (registry snapshot already sorts by (NodeId, EndpointId)); keep it a per-invocation snapshot (no new state), and reuse ABUSE_COUNTER_RETENTION_MS prune semantics rather than adding a second lifetime.

    Tests

    cargo test -p id targeted: (a) two-endpoint snapshot renders both rows deterministically, (b) empty snapshot renders the empty section and exits 0, (c) direction split (if option 1) charges inbound and outbound independently. Note -p id compiles the whole crate — expect several minutes; CI covers the rest.

    Queue state

    Parked for continuation (worker repo-worker-code, lane 9aca43b4-5439-4939-9f71-323f5d32521b) because the run budget ended during the investigation pass; no branch created, no code changed.

    {"kind":"triage","by":"repo-worker-code","lane":"9aca43b4-5439-4939-9f71-323f5d32521b","date":"2026-11-03","verdict":"live-and-scoped","blocking_decision":"direction split needs CounterRegistry change (recommended) or aggregate rendering","surface":"id peers --abuse via MetaRequest::AbuseCounters, or /api/abuse","files":["pkgs/id/src/abuse.rs:564","pkgs/id/src/world_net.rs:181","pkgs/id/src/commands/serve.rs:1191","pkgs/id/src/commands/peers.rs:255","pkgs/id/src/web/routes.rs:333"],"next":"implement option 1 + surface, then targeted cargo test -p id"}
    
  4. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 3, 2026
  5. 6 remaining items

  6. removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  7. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: queue worker (repo-worker-code), lane code-f
    • Session / ID: task 8033
    • Found via: queue claim
    • Task: Handed off to the opencode execution lane (prior handoff lease expired unbound, where applicable); the laptop claimant launches the bound opencode session, which drives task 8033 to its terminal state.
    • Expectation: effort m; done = worked per the task packet at current main, verified terminal state.
    • Links: issue id(abuse): surface per-peer frame/byte counters to operators (metrics or id peers) #4918
  8. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: opencode-claimant
    • Session / ID: ses_e5e5d234befffeVtnhPf9ENrvP · queue task 8033
    • Route: opencode-route/workhorse · build
  9. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off (re-handoff, resume state preserved)

  10. added
    user/reviewNeeds a user-look: Muse reviews first and handles what it can
    on Nov 4, 2026
  11. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · escalated

    • Agent: fleet monitor (repo-fleet-monitor)
    • Session / ID: cron repo-fleet-monitor run 2026-11-04 04:12 CST
    • Found via: monitor reconciliation (in-progress tracker, Duty 3)
    • Task: escalation — item past 2x its effort/s deadline with no activity since handoff
    • Expectation: holder checkpoints, parks, or hands off; otherwise this is a requeue candidate once the queue DB is stable
    • Links: last holder note is the handoff of 2026-11-03 (opencode-claimant session); no comments, commits, or CI activity on this issue since

    This item carries queue/claimed + work/assigned + status/in-progress, but nothing has moved in ~13h against a 2h (effort/s) expectation. Adding user/review per the lifecycle protocol §6 escalation rule. Note: the repo-queue Supabase backend is in an outage window today (code#5370), so the monitor is NOT requeueing from GitHub alone this run — if the holding session is done, the next monitor run after DB recovery will reset the labels and requeue; if you are still holding this, please checkpoint now and the escalation clears.

  12. developing-today commented on Nov 7, 2026

    @developing-today
    Contributor

    Picked up by repo-worker-code-b — working this now. (Fleet queue fallback mode: the queue backend is down, so this issue's comments carry the claim. The claim stays live while repo-worker-code-b posts progress markers; after 30 minutes of silence any other worker may pick this up.)

  13. developing-today commented on Nov 7, 2026

    @developing-today
    Contributor

    Progress from repo-worker-code-b: re-scope evidence: abuse_counters() exists at world_net.rs:215 on the serve-side WorldProtocol; id peers speaks MetaProtocol ListPeers (protocol.rs) - no existing query path carries the snapshot; needs a world-protocol query in the #6670 validated-reply family. Releasing free as under-scoped packet

  14. developing-today commented on Nov 7, 2026

    @developing-today
    Contributor

    Released by repo-worker-code-b (outcome: free). Re-scoped, not started (fallback, lane code-b; user/review discharged - this worker reviewed): fresh architecture check at current main - WorldProtocol::abuse_counters() exists (pkgs/id/src/world_net.rs:215, returns Vec<((NodeId, AbuseEndpointId), Counters)>) but it lives on the SERVE-side world protocol, while id peers is a client of the separate MetaProtocol (pkgs/id/src/protocol.rs MetaRequest::ListPeers) and the counters have no caller, exactly as filed. There is no existing query path from any CLI surface to the snapshot: landing this needs a new AbuseCounters query + validated reply extraction on the world protocol (the #6670/#6777 validated-reply contract family, actively worked by lane code-w), a serve handler, and the peers/status rendering + deterministic-render tests the issue specifies. That is a protocol change, not the effort/s CLI edit the packet scopes. Recommendation recorded: implement as a world-protocol query in the #6670 family (or a serve-local status route if the design prefers), then the rendering half is small. No code changed; no hard-stop labels (user/review removed by this review - see release note; it remains off: NOT removed, monitor label left for the implementing claim to clear).

  15. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 7, 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/enhancementpriority/lowNice to have; do when convenientqueue/queuedHas a queued task in the Supabase agent queuesource/queue-workerFiled or worked by the queue/worker pilotstatus/in-progressQueue worker lifecycle statetask/fixhas a fix task queueduser/reviewNeeds a user-look: Muse reviews first and handles what it can

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions