Skip to content

fix(relay): allow deleting archived channels - #7569

Open
hyein-cbio wants to merge 1 commit into
block:mainfrom
hyein-cbio:hyein-cbio/jawfish
Open

hyein-cbio wants to merge 1 commit into
block:mainfrom
hyein-cbio:hyein-cbio/jawfish

Conversation

@hyein-cbio

@hyein-cbio hyein-cbio commented Sep 10, 2026 •

Copy link
Copy Markdown

Summary

Allow authorized NIP-29 delete-group events (kind:9008) on archived channels. Huddle hang-up and the TTL reaper archive ephemeral backing channels; the relay currently rejects their deletion with invalid: channel is archived before the owner check and soft-delete handler can run.

Both pre-storage archive gates (ingest and validate_admin_event) now use one shared predicate:

matches!(kind, 9002 | 9008) || is_self_departure(event)

This adds the delete-group exception while preserving the current main contracts:

  • Metadata edits (9002): all pass these archive gates. The atomic DB write decides whether an edit is allowed under the channel row lock, after duplicate detection, as introduced by fix(relay): apply channel setting changes atomically and lock every membership writer #8516. Reintroducing an archived=false pre-check would incorrectly reject racing retransmissions instead of returning the duplicate result. Fresh name-only or archived=true edits on archived channels remain rejected by that DB write.
  • Delete-group (9008): passes the archive gates, but still requires owner authorization (or the owning human of an owner-role agent). The existing handler performs the soft delete; this PR does not change it.
  • Self-departure: preserves feat(relay): add announce channels with a join_role setting #8143: 9022, or 9001 whose target p tag names its author. Membership and last-owner checks still apply.

Other events remain subject to the archive write-lock. The ingest gate's existing target_denial handling is unchanged.

Rebased onto block/buzz main at 20ba95ed8906b2b7ae1819bdddadf82c09778097. The PR remains one DCO-signed commit touching only ingest.rs, side_effects.rs, and e2e_relay.rs.

Related issue

Same bug as #2954, also reported in #7596. Earlier PRs addressing it: #2988 and #4669. This is not a new diagnosis.

Compared with #2988 and #4669

All three address the same two archive gates and touch the same three files. The comparison below describes the versions inspected, not a claim that an older PR cannot be updated.

Aspect #2988 #4669 This PR
Gate structure Separate inline delete exceptions at both gates Shared helper Shared helper
Unit coverage No new predicate unit tests Delete, unarchive vs archive, reject 9000 Delete; all 9002 variants; both self-departure forms; reject 9000, removal of another member, 9005, and kind 1
Live-path regression Strongest: archive/unarchive/re-archive, blocked rename, non-owner denial, owner delete, post-delete disappearance Owner archive-then-delete acceptance Owner archive-then-delete acceptance; unchanged by this rebase
Current upstream semantics Predates #8143 / #8516 Predates #8143 / #8516 Preserves self-departure and atomic 9002 validation after duplicate detection

#2988's E2E is stronger than ours: this PR's E2E does not independently prove non-owner authorization, post-delete disappearance, or the rename write-lock. Both earlier PRs also use named kind constants; this helper retains numeric kinds, consistent with nearby admin handling. Our advantages are the shared gate predicate, the broader unit table, and compatibility with the current upstream lifecycle rules—not a different fix for the underlying bug.

Testing

Rust 1.95.0; all commands below ran with SQLX_OFFLINE=true:

  • cargo fmt --all --check — passed.
  • cargo test -p buzz-relay --lib -- side_effects::tests — 10 passed, 0 failed.
  • cargo clippy -p buzz-relay --all-targets -- -D warnings — passed, no warnings.
  • cargo clippy -p buzz-test-client --all-targets -- -D warnings — passed, no warnings.
  • Mutation check: temporarily removing 9008 from the production helper made archived_channel_gate_allows_delete_group fail with expected kind 9008 to pass. Restoring it returned the suite to 10 passed.
  • The table retains the self-targeted 9001 p tag with .allow_self_tagging(), so it exercises the real self-departure predicate.
  • git diff --check origin/main..HEAD — passed.

Live relay and human verification (completed)

Validated commit eb7a71785f2ebd54acbb4329d6b57e2ecbc73404 against an isolated local relay at ws://127.0.0.1:17571, backed by disposable Postgres 17, Redis 7, and S3-compatible storage. No production data or credentials were used.

  • test_owner_can_delete_archived_channel — 1 passed. A direct SQL query also confirmed that the channel had both archived_at and deleted_at set.
  • The same deletion test against unmodified main (20ba95ed) failed with invalid: channel is archived, reproducing the original defect.
  • Upstream's test_nip29_departure_wire_archived_channel_allows_self_leave — 1 passed against the PR relay.
  • Additional live CLI checks passed: archived-channel name edits and ordinary messages stayed rejected; a non-owner's delete failed with only owner can delete group; the owner could delete without unarchiving. SQL confirmed the soft delete.
  • Human confirmation: the author independently ran an interactive CLI create → archive → delete workflow against the PR relay and reported the database result archived=t, deleted=t.

Known limitation: test_unarchive_emits_member_added_notification timed out waiting for the membership notification on both the PR relay and unmodified main in separate local databases. This is not an all-E2E-green claim; the failure was reproduced on the baseline. The full repository CI suite was not run locally.

The committed deletion E2E remains unchanged and #[ignore], because it needs a live relay; it does not duplicate the upstream self-departure E2E.

To exercise the deletion flow against a configured local test relay:

SQLX_OFFLINE=true cargo test -p buzz-test-client --test e2e_relay test_owner_can_delete_archived_channel -- --ignored --exact

Expected: the owner can archive the test channel and then submit 9008 without unarchiving first; both events are accepted. No UI changes or screenshots. Local test services have been cleaned up.

@hyein-cbio
hyein-cbio requested a review from a team as a code owner September 10, 2026 21:03
@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 20ba95ed8906b2b7ae1819bdddadf82c09778097...eb7a71785f2ebd54acbb4329d6b57e2ecbc73404.
A new review must complete for this exact range. When manual authorization
is required, a user with write access must comment exactly
@buzz-security-review eb7a71785f2ebd54acbb4329d6b57e2ecbc73404 to authorize a new review.
Any previous review applies only to its recorded range.

Archive is a write-lock, not a tombstone. Owners could unarchive
(kind:9002 archived=false) but not delete (kind:9008), so ephemeral
huddle backing channels archived on hang-up or by the TTL reaper
could not be removed.

Signed-off-by: Hyein Cho <hyein.cho.cbio@gmail.com>
@hyein-cbio
hyein-cbio marked this pull request as draft October 22, 2026 22:15
@hyein-cbio
hyein-cbio marked this pull request as ready for review October 22, 2026 22:28

This branch has not been deployed

No deployments
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