Skip to content

feat(relay): accept an owner's presence for its agents (kind 20003) - #8261

Open
baxen wants to merge 2 commits into
mainfrom
baxen/agent-presence
Open

baxen wants to merge 2 commits into
mainfrom
baxen/agent-presence

Conversation

@baxen

@baxen baxen commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

App-hosted agents (buzz-app agents2) run inside the desktop app without a socket per agent, so they can't send their own kind 20001 presence and always read as offline — including to their owner on another device.

This adds kind 20003, an owner's presence for its agents:

  • Owner-signed; content is the status (online / away / offline); each p tag names an agent (1–256 distinct).
  • WebSocket only (rejected on HTTP ingest, like 20001).
  • The relay keeps only the agents the signer owns (users.agent_owner_pubkey) — cached true from observer_owner_cache, the rest resolved with one list_agents_for_owner query. Only positive links are cached, so a just-linked agent isn't hidden for the cache TTL and the observer path isn't poisoned.
  • Sets (180s TTL) or clears each agent's presence in one Redis round trip, the same keys kind 20001 writes, so the presence snapshot reports them with no client change.
  • Not fanned out: live presence is matched by author, and the author here is the owner.

A new kind rather than p tags on 20001, so a relay without this ignores the batch instead of storing it as the owner's own presence.

Testing

  • agent_presence_request_names_distinct_agents_with_a_known_status (unit).
  • presence_storage_postgres_tests::agent_presence_stores_only_owned_agents (Postgres + Redis): only the owner's agent goes online, another owner's agent doesn't, a pre-cached false doesn't hide a linked agent, and offline clears it. All 5 presence Postgres tests pass via scripts/postgres-test-run.sh -p buzz-relay --lib -E 'test(/presence/)'.
  • buzz-pubsub ignored Redis test test_presence_many_sets_with_ttl_and_clears passes.
  • clippy clean. Plain cargo test failures locally were environmental only (buzz-media "role buzz does not exist", mesh_demo 504).
  • Not yet exercised end to end against a local relay + app.

App side: block/buzz-app#862.

🤖 Generated with Claude Code

An app that runs agents without a socket per agent could not say they were
online: presence (kind 20001) is WebSocket-only and stored for the
connection's own key, so app-hosted agents always read as offline.

Kind 20003 is one owner-signed heartbeat for many agents. Content is the
status, each `p` tag names an agent (at most 256). Over WebSocket only, the
relay keeps the agents the signer owns (users.agent_owner_pubkey, through
the existing owner cache, with at most one query per event) and sets or
clears their presence in one Redis round trip, exactly as each agent's own
kind 20001 would, so the presence snapshot reports them unchanged. It is
not fanned out: live presence is matched by author, and the author is the
owner. A new kind, rather than p tags on 20001, keeps relays without this
from storing the batch as the owner's own presence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
@baxen
baxen marked this pull request as ready for review October 11, 2026 18:30
@baxen
baxen requested a review from a team as a code owner October 11, 2026 18:30
@baxen
baxen deployed to codex-review October 11, 2026 18:30 — with GitHub Actions Active
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T18:35:18.522513Z fab3fdd Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Oct 11, 2026
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: 30873ba46b5e08602ae61a2a0592ecedd37f7289...ead260d70e775d1e3a76b9b1600b528d360d5617
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: MEDIUM

The new agent-presence validator permits excessive CPU work before rejecting oversized batches.

Findings

[MEDIUM] Enforce the agent limit before quadratic deduplication

  • Category: Reliability
  • Location: crates/buzz-relay/src/handlers/event.rs:1055 (source)
  • Description: An authenticated client can submit a correctly signed kind 20003 event containing approximately 7,000 distinct p tags within the default 512 KiB WebSocket limit. Each tag performs Vec::contains against all preceding agents, while the 256-agent limit is checked only after the entire loop. The rejected event therefore performs approximately 24 million comparisons synchronously on a Tokio worker. Ownership filtering occurs afterward, so the sender needs no owned agents. The existing quota permits bursts of 50 such events per five seconds per identity.
  • Impact: Repeated oversized batches can consume shared runtime workers and delay unrelated requests across communities.
  • Recommendation: Reject immediately when adding a distinct agent would exceed 256, and use a bounded hash set for deduplication.

Notes

  • Review used read-only source and history inspection. Builds, tests, and performance benchmarks were not run.

Generated by Codex Security Review |
Requested by: @baxen |
Workflow run

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fab3fddb0b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1104 to +1108
let stored = if status == "offline" {
state.pubsub.clear_presence_many(tenant, &owned).await
} else {
state.pubsub.set_presence_many(tenant, &owned, status).await
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fence stale presence batches before writing

Consecutive kind:20003 events are handled by independently spawned tasks (connection.rs:1244-1247), so an older online batch delayed by this ownership lookup can reach Redis after a newer offline batch has cleared the same agent. The resulting stale online lease survives for up to 180 seconds and can incorrectly block starting another agent instance; serialize updates per agent or use an ordering/generation fence before mutating Redis.

AGENTS.md reference: AGENTS.md:L222-L225

Useful? React with 👍 / 👎.

Comment on lines +1089 to +1092
let linked = state
.db
.list_agents_for_owner(community, &owner_bytes)
.await

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict ownership lookup to the requested agents

When any submitted agent is absent from the positive cache, this fetches every agent owned by the signer even though the event can name at most 256. An owner with a large roster—or a client repeatedly including one unowned key, which is deliberately never cached—can therefore force the relay to return and scan the full roster on every admitted heartbeat; query only the requested pubkeys (or otherwise impose a hard bound) instead.

AGENTS.md reference: AGENTS.md:L240-L248

Useful? React with 👍 / 👎.

Comment on lines +73 to +75
let mut pipe = redis::pipe();
for pubkey in pubkeys {
pipe.cmd("SET")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the batched presence update atomic

A normal Redis pipeline is only a round-trip optimization, not an atomic transaction. If the connection fails after Redis processes a prefix of these SET commands, query_async returns an error and the relay rejects the heartbeat even though some named agents were updated and others were not; use an atomic pipeline or Lua operation so this single batch cannot leave a torn presence snapshot.

AGENTS.md reference: AGENTS.md:L250-L255

Useful? React with 👍 / 👎.

Ask Postgres about just the event's uncached agents rather than the
owner's whole roster, and set a batch's presence in one MULTI/EXEC so a
dropped connection cannot leave it half applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
@github-actions github-actions Bot removed the codex-security-review-current The posted Codex security review matches its recorded range. label Oct 11, 2026
@baxen
baxen deployed to codex-review October 11, 2026 19:57 — with GitHub Actions Active
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Oct 11, 2026
@baxen
baxen enabled auto-merge (squash) October 12, 2026 21:14

This branch was successfully deployed

1 active deployment
codex-review — ead260d7 Deployed Oct 11, 2026 by baxen via Run Codex Security Review #7692
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant