Skip to content

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

Description

@frodoHost

Summary

AuditAction::EventDeleted is defined but has no production call site anywhere in the codebase. Neither of the two paths that delete/tombstone an event (the standard NIP-09 soft-delete, or the hard-delete-on-supersede path for parameterized-replaceable events) ever writes an audit log entry. This relay currently cannot produce an audit trail for any event deletion.

Where

  • crates/buzz-audit/src/action.rs (~lines 8-31) defines AuditAction with 11 variants including EventDeleted (event_deleted in storage). Searching the whole tree, it appears in exactly two places: the enum definition itself, and one #[cfg(test)] unit test (crates/buzz-audit/src/service.rs, verify_detects_tampering_within_a_community) that uses it only as a generic sample action to exercise hash-chain tamper detection — not a real deletion code path.

  • NIP-09 deletion dispatch is in crates/buzz-relay/src/handlers/side_effects.rs:

    • handle_delete_event_side_effect (standard e-tag deletion) calls soft_delete_event_and_update_thread.
    • The a-tag (addressable/parameterized-replaceable) NIP-09 deletion path also calls soft_delete_event_and_update_thread.

    Neither call path touches buzz_audit — there's no audit_tx.send(...) anywhere near either handler. Contrast with the two paths that do audit correctly on the same pattern:

    • crates/buzz-relay/src/handlers/event.rs (enqueue_event_created_audit) sends AuditAction::EventCreated after every successful event write.
    • crates/buzz-relay/src/api/media.rs sends AuditAction::MediaUploaded after a successful Blossom upload (explicitly commented as following "the same pattern as event audit").

    No equivalent enqueue_event_deleted_audit exists.

  • Both deletion mechanisms confirmed:

    • Soft delete / tombstone (the normal user-facing NIP-09 delete, both e-tag and a-tag): soft_delete_event_and_update_thread and the lower-level delete_event/delete_by_kind_pubkey_dtag in crates/buzz-db/src/event.rs do UPDATE events SET deleted_at = NOW() WHERE ... AND deleted_at IS NULL. There is no DELETE FROM events anywhere in that file.
    • Hard delete for parameterized-replaceable event supersession, gated behind a hard_delete_superseded flag: replace_parameterized_event in crates/buzz-db/src/lib.rs issues DELETE FROM events WHERE community_id = $1 AND kind = $2 AND pubkey = $3 AND d_tag = $4 AND deleted_at IS NULL plus a companion DELETE FROM event_mentions.

    Neither path calls into buzz_audit anywhere in crates/buzz-db or in the crates/buzz-relay/src/handlers/side_effects.rs caller.

Reproduction

A regression test reproducing this against real Postgres (standard_deletion_writes_no_audit_entry, crates/buzz-relay) is here, passing in CI: frodoHost#1

Related doc gap

ARCHITECTURE.md (~line 503) lists only 10 audit actions and omits MediaUploaded (which does exist and is wired up correctly) — worth fixing alongside this, and worth noting there that EventDeleted is defined-but-dead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions