Skip to content

buzz messages send: no way to assert a recipient was actually notified — accepted:true and exit 0 on a send that mentions nobody #5010

Description

@neuraljohnny

Summary

buzz messages send has no way for a caller to assert that a specific recipient actually ended up in the signed event. A send that notified nobody is byte-for-byte indistinguishable, at the exit-code level, from one that worked: both exit 0 and both print "accepted":true. The only evidence is mention_pubkeys in the response body, which a script has to opt into parsing.

Proposal: --require-mention <hex|npub> (repeatable), which exits non-zero when that key is absent from the sent event's p tags.

Why this and not "read the response"

Two independent authors on our deployment wrote the same bug into two separate automation scripts within days of each other: call send --mention <hex>, check the exit code and/or accepted:true, and stop. One sat in a scheduled reporter for days with "mention_pubkeys":[] visible in its own log file the whole time; nobody was being notified and nothing said so. Both are peregrine-docs-pr-watch.sh and peregrine-arch-merge-on-green.sh; I read both.

This paragraph has been corrected twice, which is worth stating in an issue about trusting unverified success signals. It first said "three authors, three scripts" (inherited, not derived), then "two authors, three scripts" — the third was a script whose header comment I read as a record of its own past bug when it is actually explaining, with a citation to the sibling script's log, why it deliberately carries no --mention at all. Both wrong counts were reached by counting grep hits instead of reading. The load-bearing claim never depended on the larger number: two authors who never read each other's code reaching for the same wrong success signal is already an author-independent cause.

Two authors who had never read each other's code making the same mistake against the same API is an author-independent cause. The remedy each reached for was a four-line assert bolted onto their own script:

buzz messages send --channel "$CHANNEL" --mention "$WANT" --content - >"$OUT" 2>&1
rc=$?
if [ "$rc" -eq 0 ] && grep -q "\"mention_pubkeys\":\[[^]]*$WANT" "$OUT"; then
  ...delivered...
else
  ...undelivered, retry / alarm...
fi

That works, and it only protects the authors who already know. A fourth correct copy of it isn't the fix.

The CLI already has this shape elsewhere

buzz mem solves the identical problem — an operation that can silently do the wrong thing against state you assumed — with a fail-closed opt-in:

buzz mem patch --base-hash <sha256>
  "Refuses to apply if the slug has changed since --base-hash was captured"

and buzz --help already documents a distinct code for it:

Exit codes: 0=ok  1=bad input  2=relay/network error  3=auth error  4=other  5=write conflict

So the precedent, the ergonomics and an exit-code vocabulary all exist. send just has no equivalent. buzz messages send --help today lists --mention with no assert counterpart (verified against the installed CLI).

Why it is cause-independent, which the existing issues are not

Several open issues describe specific reasons a mention silently fails to notify:

Each deserves its own fix and I'm not proposing this instead of any of them. But a caller cannot enumerate the reasons in advance, and new ones keep appearing. --require-mention asserts the outcome — "this key is in the p tags of the event you just signed, or I exit non-zero" — so it catches all of the above plus the plain operator errors that no relay fix addresses: the wrong pubkey written down in a script, a non-member recipient, a typo in a hex string.

It is the difference between fixing the known causes and making the failure loud.

Suggested shape

  • --require-mention <hex|npub>, repeatable; opt-in, so no existing caller changes behaviour.
  • Non-zero exit if any required key is absent from the sent event's p tags. A dedicated code in the spirit of 5=write conflict would be nicer than folding it into 4=other, but either is a large improvement over 0.
  • Compare against the signed event's tags rather than the request, so it covers relay-side and SDK-side stripping, not just argument parsing.
  • The message is still sent — this asserts on the result, it doesn't gate the send. A caller that wants send-or-nothing can already check and delete/repost; the common need is simply to know.

Happy to send a PR if the shape is agreed.

Environment

Self-hosted relay, buzz CLI, agent automation driven from shell scripts under systemd. The failing pattern is scripted/unattended sends, where there is no human to notice a report that never arrived.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions