Skip to content

fix(sdk): allow self-tagging in build_add_member so self-targeted kind 9000 keeps its p tag - #4484

Open
Joi wants to merge 1 commit into
block:mainfrom
Joi:fix/sdk-add-member-allow-self-tagging
Open

fix(sdk): allow self-tagging in build_add_member so self-targeted kind 9000 keeps its p tag#4484
Joi wants to merge 1 commit into
block:mainfrom
Joi:fix/sdk-add-member-allow-self-tagging

Conversation

@Joi

@Joi Joi commented Aug 3, 2026

Copy link
Copy Markdown

Summary

buzz_sdk::builders::build_add_member builds kind:9000 without .allow_self_tagging(), so a self-targeted add-member (actor == target — e.g. an owner adjusting their own role, or an admin agent adding itself with an explicit role) silently loses its ["p", target] tag at signing time: nostr 0.44 strips p tags that match the signer's own pubkey by default (verified against the resolved nostr-0.44.7, builder.rs:435-449). The event then reaches the relay without a p tag and is rejected by handlers/side_effects.rs with invalid: missing p tag.

The codebase already recognizes this exact interaction in two places:

  • The NIP-IA builders apply .allow_self_tagging() for their self paths, with a comment documenting the nostr 0.44 scrub (crates/buzz-sdk/src/builders.rsbuild_archive_identity_request / build_unarchive_identity_request).
  • The e2e suite's self-targeted PUT_USER probe hand-builds its kind:9000 with .allow_self_tagging() instead of using build_add_member, with a comment noting the builder would otherwise drop the tag (crates/buzz-test-client/tests/e2e_relay.rs, "The probe: owner_b sends a bare self-targeted PUT_USER").

This PR applies the same one-line fix to build_add_member itself, so SDK/CLI callers get the correct wire form. Non-self adds are unaffected (allow_self_tagging only changes behavior when a p tag matches the signer).

Observed in production: a relay-admin agent running the CLI's add-member against its own pubkey (to set an explicit bot role) was rejected with invalid: missing p tag; adding a different pubkey with the same CLI worked fine.

Note: build_remove_member (9001) and build_dm_add_member (41011) share the same shape and would also scrub a self p tag — and the relay does support guarded self-removal (handle_remove_user: self-remove allowed unless the actor is the last owner). I left them unchanged only to keep this PR scoped to the observed add-member failure — happy to extend it to the whole class if you prefer.

Related issue

None found (searched open issues/PRs for allow_self_tagging / "missing p tag" / add-member self).

Testing

  • New unit test add_member_self_target_keeps_p_tag pins the fixed wire form (signs a self-targeted build_add_member and asserts the p and role tags survive), mirroring the existing unarchive_request_layout_self_path pin. Verified red without the fix: it fails on exactly assertion failed: has_tag(&ev, "p", &self_pk).
  • Existing add_member_with_role / add_member_without_role tests still pass (non-self path unchanged). cargo test -p buzz-sdk green (253 tests).
  • Rust legs of just ci run locally and green: cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings, and the full test-unit package set (buzz-core, buzz-auth, buzz-voice, buzz-cli, buzz-db --lib, buzz-conformance, buzz-push-gateway, buzz-backend-kubernetes) — 0 failures. The desktop/web/mobile legs weren't run locally (toolchains not installed here); the change is confined to crates/buzz-sdk, so deferring those to PR CI.
  • Verified against live relays running block/buzz @ a5dbdf5, both self-add paths:
    • Privileged path (signer is channel owner): self-targeted channels add-member --role admin is accepted and channels members shows the signer's pubkey at the requested role.
    • Unprivileged path (signer is a plain channel member): the rejection changes from the pre-fix invalid: missing p tag (wire-form, before the relay can even evaluate the request) to invalid: only owners/admins may change an active member's role (authorization) — i.e. the p tag now reaches the relay and the request is judged on its merits.

…d 9000 keeps its p tag

nostr 0.44 strips p tags matching the signer's own pubkey at signing time,
so a self-targeted add-member (actor == target, e.g. an owner adjusting
their own role or an admin agent adding itself with an explicit role) went
on the wire without its p tag and was rejected by the relay with
"missing p tag".

The NIP-IA builders already apply .allow_self_tagging() for their self
paths with a comment documenting the scrub, and the e2e self-targeted
PUT_USER probe hand-builds its kind 9000 with .allow_self_tagging()
instead of using build_add_member. This applies the same fix to
build_add_member itself. Non-self adds are unaffected.

Adds add_member_self_target_keeps_p_tag pinning the fixed wire form,
mirroring unarchive_request_layout_self_path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joi Ito <joi@ito.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