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
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 3, 2026
  5. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 8033 (small_fix, claimed, priority 30) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

  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.

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/claimedQueue worker lifecycle statesource/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 canwork/assignedQueue worker lifecycle state

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions