Development & Automation

Connect an AI Agent to a Buzz Community: Build a Mention Bot.

How to connect an AI agent to a Buzz community and have it answer mentions over WebSocket, split into the official tools and a roll-your-own path.

· Huuush

Connect an AI Agent to a Buzz Community: Build a Mention Bot

Key takeaways

  1. 1Start by looking at the official buzz-cli and buzz-acp, and set up a WebSocket listener when you need direct control.
  2. 2Subscribe to kind 9 messages after NIP-42 authentication, and check both the mention in the body and the target p tag.
  3. 3Treat heartbeat, reconnection, event ID de-duplication, and kind 20001 presence as separate operational concerns.
A person checking an AI agent's community messages on a laptop

If you invited an AI agent to your Buzz community and it doesn't react to mentions, first check channel membership and event subscriptions. Here we walk through connecting an agent you built yourself, like Muse, to a community and setting it up to receive mentions and answer them. Before the way of handling the protocol directly, we introduce the official tools, and we explain the WebSocket approach for when you want to control the details.

Buzz represents messages and many actions as signed Nostr events. Because humans and agents are each identified by a key, a bot can join a channel as a member and exchange messages. But getting into the community and joining each channel are separate steps.

Look at the official agent tools first

If you're connecting for the first time, also look at buzz-cli and buzz-acp in the official Buzz repository. The CLI docs cover commands like reading, searching, and sending messages and managing channels. buzz-acp is a harness that connects supported ACP agents, and the current official guide includes the relay WebSocket connection, channel subscription, mention receiving, agent invocation, and reconnection. You can check the roles of the two tools and your agent's compatibility in the buzz-cli guide and the official buzz-acp docs.

Why does Muse keep a hand-built WebSocket listener?

Honestly, until I searched while preparing this post, I didn't know buzz-cli and buzz-acp existed. When I first set it up the day before, I only knew how to create an account through the app and work with the Nostr protocol directly, so I implemented the listener and thread replies while watching how the relay behaved. I didn't deliberately avoid the official tools.

Even after looking at them, I decided to keep my own listener for now. The current setup works and I've tested thread root and reply handling and reconnection, so the benefit of switching right now wasn't clear. My implementation makes it easy to adjust the event flow directly, and the CLI is handy for one-off tasks like reading, searching, and changing a profile. buzz-acp, on the other hand, turns out to offer persistent subscriptions and mention delivery, unlike what I first thought, so the comparison that "you have to poll every 5 seconds for real-time reception" doesn't hold. The official docs describe relay connections, mention queuing, and reconnection.

So the choice described here doesn't mean building it yourself is faster or better for every team. In my already-working setup, familiarity with the current configuration and control over it mattered more than the cost of switching. If you're starting fresh, look at buzz-acp first, and to compare it with your current setup, it is best to decide by measuring detection delay, reconnection behavior, and operating burden yourself on the same channel and the same agent task.

Prepare the agent account and channel access

The easiest way to start is to create an agent account through the Buzz app's invite flow and have it join the community. If you implement signup yourself, you have to handle key generation, signing, and registration. A private key is a secret that can sign as that account, so don't copy it into chats or logs, and keep it in a secrets store with restricted access.

After joining the community, the agent also has to be able to access the channel where it will receive replies. Public channels support a join-request flow that uses Nostr event kind 9021. Private channels may require an owner or admin to add members. Check the access procedure for the channel you use in the Buzz repository's guide to Nostr events and channels.

Subscribe to channel messages over WebSocket

Buzz channel messages are kind 9 events. When you build the listener yourself, you open a WebSocket connection to the relay, finish NIP-42 challenge-response authentication, and then send a REQ subscription. Limit the subscription scope with the channel's h tag and the message kind, and compare each handled event against the bot's own pubkey so it doesn't process its own replies again.

For mentions you also need to check two signals together. Check that there is @Muse text visible in the body and that the event's p tag points to that agent's public key. The official Buzz example bot also decides mentions based on the body and the recipient tag. It is safer not to assume a notification fires the same way in every relay subscription just because a name appears on screen.

If you keep a connection open for a long time, implement a heartbeat. In one setup behind Cloudflare, I observed connections dropping after about 100 seconds idle and chose a ping every 25 seconds. That is a value and setting observed in that deployment, not a fixed limit for every relay or Cloudflare connection. When you detect a closed connection, reconnect and restore the subscription. Cloudflare's WebSocket docs also describe heartbeat configurations to keep idle connections alive.

Handle reconnection and multiple mentions reliably

If you re-request from a point slightly earlier than the last handled time on reconnect, you can make up for events that arrived while the connection was down. For example, in my setup I subscribed with a 10-second overlap and then removed duplicates by event ID. NIP-01's since filter includes the boundary time, so you can't assume events from the same second are automatically dropped at the protocol boundary. Overlap and de-duplication are a recovery strategy that allows for redelivery.

If you keep the list of handled IDs only in memory, earlier events can be redelivered after a process restart and you may send duplicate replies. Record recent handled IDs to disk or a database, and consider batching, which collects mentions briefly after receiving and handles them in one go. My implementation collected mentions arriving over 5 seconds. That time is a setting to tune to your team's response latency and workload.

If you want low latency, keep a WebSocket listener running instead of querying for new messages with a once-a-minute cron. Cron can serve auxiliary jobs, like checking process health or restarting a listener that stopped.

Tie replies into the same conversation flow

When you reply to a channel message, attach an e tag that points to the original event to mark the thread relationship. If you received the mention inside a thread, reference that thread's root event to keep the context. For a reply that notifies a specific user directly, including both a visible mention and a p tag with the target's public key matches Buzz's bot example.

If you sign events yourself, you must follow the event fields and canonical serialization rules NIP-01 sets. In my implementation of signing Korean-language messages, I solved a relay rejection problem by adjusting the JSON option for non-ASCII characters, but that is a debugging case confirmed in that implementation. Where you can, use the signing path of the verified Buzz CLI or a library, and when you implement it yourself, check the official protocol docs and real relay responses together.

Run the online status indicator separately

In Buzz's architecture, kind 20001 is used for ephemeral presence updates. It is a different feature from storing channel messages and subscribing to mentions. In my setup, I sent an online status over WebSocket every 60 seconds and used a separate watchdog to check the listener process. Set the presence publishing interval and watchdog period to suit your deployment, and don't judge that message reception is healthy from the online indicator alone; check the subscription connection too.

It helps to run a quick diagnosis in order. If there's no response, check channel membership, the WebSocket subscription, NIP-42 authentication, and the mention in the body and the p tag. For duplicate replies, look at how event IDs are stored, and for an offline indicator, look at presence publishing and the listener process. In my team's environment there was a case where a mention was detected about 12 seconds after it was created, but actual response time depends on your connection and processing setup.

For how an agent splits a long-term goal and sub-tasks, see Matt Pocock's Chief of Staff Skill, and for a chat bot's delegation and response structure, see the post on Grok Bot Primary Bot.

You can check changes to the official implementation and protocols at the Buzz repository, NIP-42, and NIP-01. After setup, confirm in a test channel that mention reception, thread replies, and duplicate handling after reconnection work as intended, then move to the real channel.

Frequently asked questions

What does an AI agent need to answer mentions in Buzz?
The agent has to be able to access the community and the target channel, and you need to set up message subscription and mention detection. If you build it yourself, check both the mention text in the body and a p tag that points to the agent's public key.
How can I receive channel mentions in real time?
You can run a listener that connects to the relay over WebSocket, authenticates with NIP-42, and subscribes to kind 9 channel messages. You also have to handle reconnection and heartbeat.
Is kind 9021 used for joining every channel?
No. The Buzz repository describes kind 9021 for the public channel join-request flow. Private channels may need the owner or an admin to add members.
Why might I get the same mention again after reconnecting?
If you resubscribe from an overlapping point in time, events you already handled can be redelivered. Storing event IDs and de-duplicating reduces the chance of answering the same event twice after a restart.

Related articles