Files
ngit-grasp/fc4d-rust-nostr-deletion-broadcast-behavior.md
T

9.0 KiB

rust-nostr: Deletion Request Broadcast Behavior Investigation

ID: fc4d

Status: Investigation Needed
Priority: Low (Non-Urgent)
Complexity: Medium (requires rust-nostr codebase investigation)
Type: Potential Upstream PR

Problem Statement

Deletion requests (kind 5) should ALWAYS be broadcast to subscribers with matching filters, regardless of whether the relay honors the deletion.

When ngit-grasp runs in "deletion request disrespector" mode (deletion_request_disrespector = true), it accepts kind 5 (NIP-09 deletion request) events but intentionally does NOT process them (doesn't delete the target events). However, the deletion request events themselves MUST be:

  1. Stored in the database (for historical record)
  2. Broadcast to active WebSocket subscribers with matching filters (standard relay behavior)

Current uncertainty: We need to verify that rust-nostr's nostr-relay-builder actually broadcasts kind 5 events to active subscribers when WritePolicyResult::Accept is returned from the WritePolicy.

Expected behavior: Deletion requests are events like any other. Whether a relay chooses to honor the deletion is a separate concern from whether it broadcasts the deletion request event to subscribers.

Why This Matters

Archive relay use case:

  • Archive relays preserve deleted content (prevent "left-pad" scenarios)
  • Deletion requests are legitimate events that clients may want to track
  • Clients with filters matching kind 5 should receive these events
  • This is standard relay behavior: accepted events → saved + broadcast

Current implementation:

// src/nostr/builder.rs:759-766
DeletionResult::AcceptIgnore => {
    tracing::debug!(
        event_id = %event_id_str,
        author = %event.pubkey,
        "Accepted deletion request but ignoring (disrespector mode)"
    );
    WritePolicyResult::Accept  // ← Does this trigger broadcast?
}

Investigation Tasks

Phase 1: Verify Current Behavior

  • Test current ngit-grasp behavior:

    • Start relay in disrespector mode
    • Connect WebSocket client with filter: {"kinds": [5]}
    • Submit kind 5 deletion request
    • Verify: Is event broadcast to subscriber?
    • Verify: Is event stored in database?
  • Document findings:

    • If YES (broadcasts): Document that current behavior is correct
    • If NO (doesn't broadcast): Document the gap

Phase 2: Investigate rust-nostr Source Code

  • Examine nostr-relay-builder event flow:

    • Trace WritePolicyResult::Accept through relay code
    • Find where events are saved to database
    • Find where events are broadcast to subscribers
    • Determine if kind 5 has special handling that bypasses broadcast
  • Check for automatic NIP-09 processing:

    • Verify if relay layer (not just database layer) filters kind 5
    • Check if there's a "don't broadcast deletion requests" policy
    • Document any hardcoded NIP-09 behavior

Phase 3: Determine If PR Is Needed

Based on investigation findings:

Scenario A: Current behavior is correct (broadcasts kind 5)

  • Document that no PR is needed
  • Update ngit-grasp docs to confirm broadcast behavior
  • Close this issue as "working as designed"

Scenario B: rust-nostr doesn't broadcast kind 5 events

  • Determine root cause (intentional vs bug)
  • Design PR proposal (see below)
  • Discuss with rust-nostr maintainers
  • Implement and submit PR

Proposed PR (If Needed)

Option 1: Fix Bug (if it's unintentional)

If rust-nostr is accidentally not broadcasting kind 5 events when WritePolicyResult::Accept is returned:

Change: Ensure kind 5 events follow standard event flow (save + broadcast)

Rationale:

  • NIP-09 says relays "MAY" honor deletions, not "MUST"
  • Accepting but not processing is legitimate (archive relays)
  • Accepted events should always be broadcast to matching subscribers
  • This is standard relay behavior regardless of event kind

Option 2: Fix Intentional Behavior (if it's by design)

If rust-nostr intentionally doesn't broadcast kind 5 events:

Change: Remove special-casing of kind 5 events - they should follow standard event flow

Rationale:

  • Deletion requests are events, not commands - they should be broadcast like any other event
  • Separation of concerns: Broadcasting events vs honoring deletions are separate decisions
  • Standard relay behavior: All accepted events should be broadcast to matching subscribers
  • No configuration needed: There's no legitimate reason to suppress deletion request broadcasts
  • Transparency: Clients should be able to see deletion requests regardless of relay policy

Strong position: We should NOT add a configuration option to suppress broadcasts. Deletion requests are events and should always be broadcast. The relay's choice to honor or ignore the deletion is orthogonal to broadcasting the event.

Option 3: No Change Needed (if configurable)

If rust-nostr already has a way to control this behavior:

  • Document the configuration option
  • Update ngit-grasp to use it correctly
  • No upstream PR needed

NIP-09 Specification Context

From NIP-09:

Relays MAY choose to honor deletion requests or not.

Implications:

  1. Accepting kind 5 without processing is legitimate
  2. Clients should be able to see deletion requests (for transparency)
  3. Broadcast behavior should be consistent with other event kinds
  4. Archive relays need full event history including deletion requests

Use cases for seeing deletion requests:

  • Audit trails (who deleted what, when)
  • Research and analysis (deletion patterns)
  • Client-side deletion handling (some clients may honor deletions locally)
  • Transparency (users can see what was requested to be deleted)

ngit-grasp implementation:

  • Issue: b905-deletion-request-support.md
  • Implementation: Phase 1-4 complete (holding database, recovery, graph algorithm)
  • Disrespector mode: Working (stores events, doesn't process deletions)
  • Question: Are stored events being broadcast?

rust-nostr investigation:

  • Previous finding: Database layer automatically processes kind 5 (deletes events)
  • Workaround: WritePolicy intercepts before database layer
  • New question: Does relay layer broadcast accepted kind 5 events?

Success Criteria

  1. ✅ Verified behavior: We know whether kind 5 events are broadcast in disrespector mode
  2. ✅ Documented findings: Clear documentation of rust-nostr behavior
  3. ✅ Decision made: PR needed vs no PR needed
  4. ✅ If PR needed: Proposal written and discussed with maintainers

Timeline

Priority: Low (non-urgent)

  • Not blocking ngit-grasp Phase 4-7 implementation
  • Can be investigated after deletion support is complete
  • Good candidate for post-implementation cleanup

Suggested timing:

  • After Phase 7 (ngit-grasp deletion support complete)
  • When we have working disrespector mode to test against
  • Before announcing archive relay capabilities publicly

Progress

2026-01-15 [Session 10:00]

  • Created issue: Based on user question about deletion request broadcast behavior
  • Context: User correctly identified that "deletion request" implies optional processing
  • Question raised: Are kind 5 events broadcast to WebSocket subscribers in disrespector mode?
  • Pattern: ngit-grasp directly saves events to database, which triggers broadcast elsewhere
  • Uncertainty: Need to verify this pattern applies to kind 5 events
  • Next: Investigate current behavior with test client, then examine rust-nostr source

2026-01-15 [Session 10:15]

  • Position clarified: Deletion requests should ALWAYS be broadcast
  • Rationale: Deletion requests are events, not commands - broadcasting is separate from honoring
  • Updated issue: Strengthened stance against configuration options for suppressing broadcasts
  • Key principle: Separation of concerns - broadcasting events vs processing deletions are orthogonal
  • Next: Test current behavior, then prepare PR if needed (no configuration option, just fix)

Notes

Key insight from user:

"The clue is in the name 'request'" - NIP-09 deletion requests are optional, not mandatory

Standard relay pattern:

Event arrives → WritePolicy.admit_event() → WritePolicyResult::Accept
                                          ↓
                              Database.save_event() → Broadcast to subscribers

Question: Does this pattern apply to kind 5 events, or do they have special handling?

Why this matters:

  • Archive relays need transparency (show all events including deletion requests)
  • Clients may want to track deletion requests for audit/analysis
  • Standard relay behavior: accepted events are broadcast
  • Consistency: kind 5 shouldn't be special-cased unless necessary

References

  • NIP-09 Spec: /persistent/dcdev/clones/nips/09.md
  • ngit-grasp deletion support: b905-deletion-request-support.md
  • rust-nostr investigation: Previous findings in b905 Phase 6 notes
  • nostr-relay-builder: ~/.cargo/git/.../nostr-relay-builder/