Skip to content

relay: write audit entry for NIP-09 event deletion - #4097

Open
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:claude/issue4034-audit-on-delete-20260801
Open

relay: write audit entry for NIP-09 event deletion#4097
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:claude/issue4034-audit-on-delete-20260801

Conversation

@iroiro147

Copy link
Copy Markdown

Fixes #4034.

What

AuditAction::EventDeleted was defined in crates/buzz-audit/src/action.rs but had zero production call sites. Any NIP-09 event deletion flowed silently — no operator trace, no post-incident forensics.

Change

Wire AuditAction::EventDeleted into all three NIP-09 deletion paths in buzz-relay:

  • handle_delete_event_side_effect (NIP-29 kind-9005 moderation e-tag): audit after soft_delete_event_and_update_thread succeeds. Actor is the kind-9005 signer.
  • handle_standard_deletion_event (NIP-09 kind:5 e-tag loop): audit after each successful soft-delete. Actor is the kind:5 signer. The detail carries the deleted event's kind and channel_id so the entry remains queryable post-tombstone.
  • handle_a_tag_deletion (NIP-09 a-tag coordinate): audit after soft_delete_by_coordinate matches. object_id is the full coordinate string (<kind>:<pubkey>:<d_tag>) which is stable across a replace and resolvable to the deleted row.

New helper

enqueue_event_deleted_audit (in crates/buzz-relay/src/handlers/event.rs) sits next to enqueue_event_created_audit and mirrors its contract:

  • bounded channel
  • propagate backpressure when audit_tx is None (no-op)
  • absorbs any send error into the existing buzz_audit_send_errors_total counter — never panics the deletion flow.

The deletion_path detail field distinguishes the three paths ("moderation_e_tag" / "e_tag" / "a_tag") so operators can split moderation deletions from user self-deletions from coordinate deletions in post-hoc queries.

Why this is safe

  • All three audit calls live after the if !deleted guard, so a no-op (already-deleted or unknown id) does NOT produce a spurious audit entry.
  • No DB-schema or persistence changes; only enqueues to the existing bounded channel.
  • No external API or wire-format changes.

Verification

  • cargo check -p buzz-relay --lib — clean.
  • cargo clippy -p buzz-relay --all-targets -- -D warnings — clean.
  • cargo test -p buzz-relay --lib — 824 pass, 9 fail. The 9 failures are pre-existing PG-dependent tests in api::media / api::admin (they fail identically on main); none of them touch handlers::side_effects or the audit helper.

Intended follow-ups (out of scope for this PR)

  • The relay-side hard_delete_superseded path in crates/buzz-db/src/lib.rs:4768 (replace_parameterized_event) also deletes a row, but it's an upsert, not a NIP-09 deletion. I deliberately did not tag it EventDeleted — if the issue author wants the supersede case audited too, that's a separate change with a different AuditAction (probably a new EventSuperseded discriminator).

Wire AuditAction::EventDeleted into all three NIP-09 deletion paths so
operators gain trace evidence for deletions that currently flow silently:

* handle_delete_event_side_effect (NIP-29 kind-9005 moderation e-tag):
  audit after the soft-delete succeeds. Actor is the kind-9005 signer.
* handle_standard_deletion_event (NIP-09 kind:5 e-tag loop): audit after
  each successful soft_delete_event_and_update_thread. Actor is the
  kind:5 signer. Detail carries the deleted event's kind and channel_id
  so the entry remains queryable post-tombstone.
* handle_a_tag_deletion (NIP-09 a-tag coordinate): audit after a match
  in soft_delete_by_coordinate. Object id is the full coordinate
  <kind>:<pubkey>:<d_tag> which is stable across a replace and resolvable
  by the operator to the deleted row; actor is the kind:5 signer.

Introduce enqueue_event_deleted_audit next to enqueue_event_created_audit,
mirroring its bounded-channel / propagate-backpressure / no-op-when-None
contract. The helper is best-effort (any failure-path is absorbed by the
send error branch, never panics the deletion flow).

deletion_path is recorded in detail ("moderation_e_tag" | "e_tag" |
"a_tag") so post-hoc queries can distinguish moderation deletions from
user self-deletions from coordinate deletions.

Fixes block#4034

Signed-off-by: Sarthak Singh <sarthak.singh@juspay.in>
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.

NIP-09 event deletion never writes an audit log entry (EventDeleted is defined but has no production call site)

1 participant