Skip to content

fix(sdk,desktop): keep self-targeted p tags on membership and PR status events - #3384

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

fix(sdk,desktop): keep self-targeted p tags on membership and PR status events#3384
shrimalmadhur wants to merge 1 commit into
block:mainfrom
shrimalmadhur:shrimalmadhur/fix-self-tagging-add-member

Conversation

@shrimalmadhur

@shrimalmadhur shrimalmadhur commented Jul 28, 2026

Copy link
Copy Markdown

Summary

nostr 0.44's EventBuilder silently strips any p tag whose value matches the signing pubkey unless the builder opts in via .allow_self_tagging(). Buzz treats p tags as protocol routing data, not a self-notify privacy scrub, so several builders whose target can legitimately be the signer were shipping events with the target missing.

Two concrete failures:

  • kind 9000/9001 self-add and self-remove are unreachable. These are first-class relay paths — side_effects.rs reads // Self-add: always allowed regardless of policy, pinned by test_nip29_put_user_self_add_bypasses_policy ("an agent can always add itself"). But neither the SDK nor the desktop builder could express them: the p tag naming the signer was stripped, so the relay saw no target and rejected with invalid: missing p tag. The misleading error made this look like an authorization failure.
  • Every PR lifecycle event silently lost its owner. build_merged_status_event / build_pull_request_status_event derive owner from the signing key and then p-tag it, so owner == signer by construction and kinds 1630–1633 always shipped without ["p", owner]. No error — readers index those events by #p as recipients (projectPullRequests.mjs), so the owner just quietly vanished from the participant set.

Root cause is historical: .allow_self_tagging() arrived with NIP-IA (#733) and was later added to the kind:9000 e2e test (#3017), but never to the shipped builders. Every self-targeted test hand-rolls its own EventBuilder to work around it.

Related issue

None found — searched existing issues and PRs; nothing covers the self-tag stripping behavior.

Testing

cargo fmt --all --check and clippy (-D warnings, both buzz-sdk and the desktop Tauri crate) are clean. 243/243 buzz-sdk and 1820/1820 desktop Tauri unit tests pass. The ~9 buzz-relay failures reproduce identically on a stashed clean tree — pre-existing, infra-dependent media/admin tests, unrelated to this change.

The five added regression tests are the point of the change: existing tests sign with a key distinct from the target, so they passed either way and this survived CI. The new ones sign with the target's own key and assert the p tag survives. Each was confirmed to fail before the fix and pass after:

self `p` tag was stripped at signing: [["h", "11111111-1111-4111-8111-111111111111"], ["role", "bot"]]
kind 1631 lost the owner `p` tag: [Tag(["e", …]), Tag(["a", …]), Tag(["p", "bbbb…"]), …]

No UI change, so no screenshots. Verification is at the wire-form level — the live relay e2e suite needs Postgres/Redis and was not run.

Not addressed here

The same hazard remains in ~15 other p-emitting builders, deliberately scoped out because they change discovery/notification semantics and deserve their own review: note-to-self DMs (build_dm_open — currently unimplementable, fails as pubkeys must contain at least 1 other participant), the git SDK builders (issue/status/PR/patch, where the CLI defaults recipients to the repo owner), the desktop sign_event Tauri passthrough, and self-mentions/self-follows.

There is no single signing chokepoint that would fix this globally — buzz-cli has one (client.rs:588), but the desktop signs in ~50 scattered places. A lint or shared signing helper is probably worth considering so the next builder doesn't reintroduce it.

Co-Authored-By: SageOx <ox@sageox.ai>

nostr 0.44's `EventBuilder` silently strips any `p` tag whose value matches
the signing pubkey unless the builder opts in via `.allow_self_tagging()`.
Buzz treats `p` tags as protocol routing data, not a self-notify privacy
scrub, so several builders whose target can legitimately be the signer were
shipping events with the target missing.

Two concrete failures:

- kind 9000/9001 self-add and self-remove are first-class relay paths
  ("Self-add: always allowed regardless of policy", pinned by
  `test_nip29_put_user_self_add_bypasses_policy`), but neither SDK nor
  desktop builder could express them — the relay saw no target and rejected
  with `invalid: missing p tag`.
- `build_merged_status_event` / `build_pull_request_status_event` derive
  `owner` from the signing key and then p-tag it, so *every* PR lifecycle
  event (kinds 1630-1633) shipped without its owner `p` tag. This one is
  silent: readers index those events by `#p` as `recipients`, so the owner
  just quietly vanished from the participant set.

Existing tests sign with a key distinct from the target, so they passed
either way. The added regression tests sign with the target's own key and
assert the `p` tag survives; each was confirmed to fail before the fix.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Madhur Shrimal <642275+shrimalmadhur@users.noreply.github.com>
@shrimalmadhur
shrimalmadhur requested a review from a team as a code owner July 28, 2026 20:21
@shrimalmadhur shrimalmadhur changed the title fix: preserve self-targeted p tags on membership and PR status events fix(sdk,desktop): keep self-targeted p tags on membership and PR status events Jul 28, 2026
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