fix(sdk): preserve self-mention p tags in message and forum event builders (#4975)

## What users saw

`buzz messages send` silently removed an explicitly supplied
self-mention. The caller passed `--mention <sender-pubkey>` and received
`accepted:true`, but the signed event had no matching `p` tag and
`mention_pubkeys` was empty.

## Why it happened

`nostr` 0.44 strips `p` tags matching the signer's pubkey by default.
The codebase already opts out with `.allow_self_tagging()` for identity
archive and unarchive requests, but the message and forum builders that
accept mentions did not. The library therefore removed the tag during
signing after the CLI had validated the explicit mention.

## What changed

Added `.allow_self_tagging()` to all three event builders that accept
mention tags:

- `build_message` (kind 9)
- `build_forum_post` (kind 45001)
- `build_forum_comment` (kind 45003)

An explicit mention now survives signing even when it matches the
sender.

## How this was tested

Added one regression test per builder. Each test signs with the same key
included in the mention list and asserts that the resulting event
preserves the self-referential `p` tag.

Validation at `cd0f30bca`:

```text
./bin/cargo fmt --all -- --check
cargo test -p buzz-sdk --lib
cargo test -p buzz-cli --lib
cargo clippy -p buzz-sdk -p buzz-cli --all-targets -- -D warnings
```

All 257 `buzz-sdk` tests and all 321 `buzz-cli` tests passed, and
formatting and strict Clippy checks completed successfully.

## Scope and non-goals

- Does not change mention validation, deduplication, or channel-member
checks.
- Does not change `normalize_mention_pubkeys`, which is not used by the
messages-send path.
- Does not add a dropped-mentions output field because the explicit tags
are now preserved.

Closes #4906.

---------

Signed-off-by: Brad Groux <bradgroux@hotmail.com>
Signed-off-by: npub17q2gdupkvswvk5kprwc7plergm4gn295uw6fe4mjyjv53ahuhtnq02jd3f <f01486f036641ccb52c11bb1e0ff2346ea89a8b4e3b49cd772249948f6fcbae6@digitalmeld.communities.buzz.xyz>
Signed-off-by: Brad Groux <3053586+BradGroux@users.noreply.github.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: npub17q2gdupkvswvk5kprwc7plergm4gn295uw6fe4mjyjv53ahuhtnq02jd3f <f01486f036641ccb52c11bb1e0ff2346ea89a8b4e3b49cd772249948f6fcbae6@digitalmeld.communities.buzz.xyz>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
This commit is contained in:
Brad Groux
2026-08-07 13:14:25 -05:00
committed by GitHub
parent c3c39cc263
commit 78c87ae20e

View File

@@ -239,7 +239,9 @@ pub fn build_message(
tags.push(tag(&["broadcast", "1"])?);
}
imeta_tags(media_tags, &mut tags)?;
Ok(EventBuilder::new(Kind::Custom(9), content).tags(tags))
Ok(EventBuilder::new(Kind::Custom(9), content)
.tags(tags)
.allow_self_tagging())
}
/// Build an encrypted agent observer frame (kind 24200).
@@ -290,7 +292,9 @@ pub fn build_forum_post(
let mut tags = vec![tag(&["h", &channel_id.to_string()])?];
mention_tags(mentions, &mut tags)?;
imeta_tags(media_tags, &mut tags)?;
Ok(EventBuilder::new(Kind::Custom(45001), content).tags(tags))
Ok(EventBuilder::new(Kind::Custom(45001), content)
.tags(tags)
.allow_self_tagging())
}
/// Build a forum comment reply (kind 45003).
@@ -306,7 +310,9 @@ pub fn build_forum_comment(
thread_tags(thread_ref, &mut tags)?;
mention_tags(mentions, &mut tags)?;
imeta_tags(media_tags, &mut tags)?;
Ok(EventBuilder::new(Kind::Custom(45003), content).tags(tags))
Ok(EventBuilder::new(Kind::Custom(45003), content)
.tags(tags)
.allow_self_tagging())
}
/// Build a diff/patch message (kind 40008).
@@ -2253,6 +2259,53 @@ mod tests {
assert!(has_tag(&ev, "h", &cid.to_string()));
}
#[test]
fn message_preserves_self_mention_p_tag() {
// nostr 0.44 strips p tags matching the signer by default.
// build_message must opt in via allow_self_tagging() so that
// explicit self-mentions survive signing. See #4906.
let cid = uuid();
let sender = keys();
let self_pk = sender.public_key().to_hex();
let builder = build_message(cid, "self-canary", None, &[&self_pk], false, &[]).unwrap();
let ev = builder.sign_with_keys(&sender).expect("sign");
assert!(
has_tag(&ev, "p", &self_pk),
"self-mention p tag must survive signing"
);
}
#[test]
fn forum_post_preserves_self_mention_p_tag() {
let cid = uuid();
let sender = keys();
let self_pk = sender.public_key().to_hex();
let builder = build_forum_post(cid, "self-canary", &[&self_pk], &[]).unwrap();
let ev = builder.sign_with_keys(&sender).expect("sign");
assert!(
has_tag(&ev, "p", &self_pk),
"self-mention p tag must survive signing"
);
}
#[test]
fn forum_comment_preserves_self_mention_p_tag() {
let cid = uuid();
let sender = keys();
let self_pk = sender.public_key().to_hex();
let root = event_id();
let tr = ThreadRef {
root_event_id: root,
parent_event_id: root,
};
let builder = build_forum_comment(cid, "self-canary", &tr, &[&self_pk], &[]).unwrap();
let ev = builder.sign_with_keys(&sender).expect("sign");
assert!(
has_tag(&ev, "p", &self_pk),
"self-mention p tag must survive signing"
);
}
#[test]
fn agent_observer_frame_happy_path() {
let sender = keys();