feat: soft-delete for events/channels, enriched API responses, NIP-29 group management#17
Merged
tlongwell-block merged 1 commit intomainfrom Mar 10, 2026
Merged
Conversation
…pletion
Implements soft-delete for events (NIP-29 kind 9005) and channels (kind 9008),
replacing TODO stubs with real logic. Adds REST DELETE endpoints for both.
Changes:
- soft_delete_event/channel: UPDATE SET deleted_at = NOW(6) WHERE deleted_at IS NULL
- DELETE /api/messages/{event_id}: author or owner/admin auth, thread counter decrement
- DELETE /api/channels/{channel_id}: owner-only auth
- get_event_by_id now filters deleted_at IS NULL (+ _including_deleted variant)
- query_events now filters deleted_at IS NULL
- list_channels enriched: member_count, last_message_at, topic, purpose, visibility
- list_messages/get_thread: reaction counts embedded via get_reactions_bulk
- Thread summaries always included in channel history (no longer gated by param)
- NIP-11 supported_nips: [1, 11, 25, 29, 42]
- decrement_reply_count wired through Db, guarded against double-decrement
- VISION.md: channel features marked as implemented
Crossfire reviewed (Claude 8/10, Codex 3/10, GPT-5.2 6/10).
Three critical issues found and fixed:
1. Double-decrement guard on repeated NIP-29 deletes
2. get_event_by_id/query_events now filter soft-deleted rows
3. get_event_by_id_including_deleted added for audit use cases
12 files changed, +403 -22 lines. All quality gates pass.
tlongwell-block
added a commit
that referenced
this pull request
Mar 11, 2026
* origin/main: feat: soft-delete for events/channels, enriched API responses, NIP-29 group management (#17) feat: Channel management, messaging, threads, DMs, reactions, and NIP-29 support (#16) Improve chat scrolling and multiline composer (#14) chore: remove redundant inline comments across all crates (#13) Initial backend revisions, workflow expansion (#5) Add desktop Home feed (#12) Add desktop Playwright e2e harness (#11) Update desktop icon and persist window state (#9) feat: add channel creation flow (#8)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements soft-delete for events and channels, enriches API responses with metadata, and completes NIP-29 group management (kinds 9005/9008). This is the Phase 6 "channel polish" milestone.
12 files changed, +866 -59
What changed
Soft-delete infrastructure
soft_delete_event()/soft_delete_channel()— idempotentUPDATE SET deleted_at = NOW(6) WHERE deleted_at IS NULLsoft_delete_event_and_update_thread()— atomic transaction wrapping delete + thread counter decrements (reply_count,descendant_count), floors at 0insert_event_with_thread_metadata()— atomic transaction wrapping event insert + thread metadata + counter increments (prevents race where concurrent delete between separate inserts corrupts counters)deleted_at IS NULL:query_events,get_event_by_id,is_member,get_members,get_member_role,get_accessible_channel_idsget_event_by_id_including_deleted()variant for audit/compliance use casesREST API endpoints
DELETE /api/messages/{event_id}— author or channel owner/admin auth, effective author resolution for relay-signed messages (real sender in p-tag), atomic delete + counter decrement, fails hard on metadata lookup errorsDELETE /api/channels/{channel_id}— owner-only auth, soft-delete + system message emissionEnriched API responses
GET /api/channels) — now includesmember_count,last_message_at,visibility,topic,purpose,created_by,created_at,updated_at,archived_atGET /api/channels/{id}/messages) — thread summaries always included (not gated by param), bulk reaction counts embedded per messageGET /api/channels/{id}/messages/{id}/thread) — bulk reaction counts for root + all repliesNIP-29 group management completion
supported_nipsupdated to[1, 11, 25, 29, 42]Performance
channels_handlerwith bulk queries:get_member_counts_bulk()andget_last_message_at_bulk()usingQueryBuilderwithseparated()for IN clausesget_reactions_bulk()in message list and thread endpointsreactions_to_json()helper deduplicates serialization logicAuthorization hardening
effective_author()/effective_message_author()— extracts real author from p-tag for relay-signed REST messages (used in delete auth and NIP-29 validation)Quality gates
cargo buildcargo test --libcargo fmt --checkcargo clippy -- -D warningsReview notes
This went through 3 rounds of multi-model crossfire review (Claude, Codex, GPT-5.2, Opus). 13 issues were found and fixed:
deleted_atfilters onget_event_by_id/query_events, NIP-29 auth too permissive (any member could delete), N+1 queries in channel listchannels.deleted_at IS NULL)deleted==false, pre-storage validation missing same-channel check for kind 9005Known deferred items (not blocking)
last_reply_atrecomputation on delete — deleting the latest reply does not recompute the timestamp (can add periodic reconciliation)