Skip to content

feat(cli): add buzz messages subscribe for streaming channel events - #4674

Open
ashbrener wants to merge 1 commit into
block:mainfrom
ashbrener:feat/cli-messages-subscribe
Open

feat(cli): add buzz messages subscribe for streaming channel events#4674
ashbrener wants to merge 1 commit into
block:mainfrom
ashbrener:feat/cli-messages-subscribe

Conversation

@ashbrener

Copy link
Copy Markdown

Every agent-facing read in buzz-cli is a one-shot HTTP call, so anything that wants to react to a channel has to poll. That is the wrong shape for an agent: five seconds of latency on every exchange, and requests spent on an idle room.

buzz-ws-client already had connect, NIP-42 auth, send_raw and next_event — but no REQ helper and no caller that streams, so the capability existed and nothing in the CLI could reach it.

Surface

buzz messages subscribe --channel <uuid> [--kinds 9,1] [--reconnect-after <secs>]
  -> one JSON event per line on stdout, until the stream stops

BuzzClient::subscribe_events() sits alongside the existing publish_ephemeral_event().

It never returns Ok

Every exit path is a reason the stream stopped. A reader that cannot tell a quiet channel from a dead socket is worse than a poller — it goes silent and looks healthy. Callers get an error they can act on rather than a clean exit they will misread.

Why --reconnect-after tears down a healthy socket

Deliberate, and the part most worth arguing about. A relay can quietly stop matching a long-lived subscription: the socket stays open, the heartbeat keeps arriving, and nothing is delivered. That is indistinguishable from a channel where nobody is talking. Ending a healthy stream on an interval gives a --since sweep a turn to find out which it was. Silent staleness is the failure mode this exists to prevent.

Measured

Against a local relay, send to first line on stdout:

subscribe messages get --since at 5s
median 44 ms 2.0 s
worst 90 ms 4.5 s

Testing

cargo test -p buzz-cli — 317 passed. Clippy clean at -D warnings, cargo fmt --check clean. Exercised against a local relay with BUZZ_REQUIRE_RELAY_MEMBERSHIP=true: subscribe delivers live events, survives peer reconnects, and returns a named error on auth failure, membership rejection, and socket close.

Relationship to other PRs

Split out of #4481, which should not have been shipping a CLI verb inside a skill. #4481 now depends on this and falls back to HTTP polling without it.

Touches the same regions of client.rs and lib.rs as #4479, so the two conflict textually. Whichever lands first, I will rebase the other.

Every agent-facing read in buzz-cli is a one-shot HTTP call, so anything
that wants to react to a channel has to poll. That is the wrong shape for
an agent: a five-second poll is five seconds of latency on every exchange,
and it spends tokens and requests on an idle room.

`buzz-ws-client` already had connect, NIP-42 auth, `send_raw` and
`next_event`, but no REQ helper and no caller that streams, so the
capability existed and nothing could reach it. This adds
`BuzzClient::subscribe_events()` alongside the existing
`publish_ephemeral_event()`, and a `messages subscribe` verb that writes
one JSON event per line to stdout.

It never returns `Ok`. Every exit path is a reason the stream stopped,
because a reader that cannot tell a quiet channel from a dead socket is
worse than a poller — it goes silent and looks healthy.

`--reconnect-after` tears down a healthy socket on an interval. That looks
wasteful, and it is deliberate: a relay can silently stop matching a
long-lived subscription, and a periodic reconnect is the only thing that
gives the caller a chance to notice.

Signed-off-by: Ash Brener <ashley@starlogik.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant