From 2161e3c0a8169a85111cd6dc01ffe2b0fed1493f Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 10 Apr 2026 18:20:23 +0000 Subject: fix: purgatory replacement announcements leaked into DB without git data When a replacement 30617 announcement arrived for an entry already in purgatory (e.g. the same event fetched from a second relay during sync, or a user re-submitting a slightly updated announcement), the policy returned Accept instead of AcceptPurgatory. This caused the event to be saved to the database immediately, bypassing the purgatory gate, without the corresponding git data or state events ever arriving. Fix: return AcceptPurgatory when replacing a purgatory entry so the updated event stays in purgatory until git data arrives. The purgatory entry is still updated with the newer event via replace_purgatory_announcement before the return. --- CHANGELOG.md | 2 ++ src/nostr/policy/announcement.rs | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 633ddbf..7a6498b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Replacement announcements (kind 30617) for a purgatory entry were being saved to the database immediately, bypassing the purgatory gate. When a second copy of the same announcement arrived (e.g. via sync from another relay) while the original was still in purgatory awaiting git data, the policy returned `Accept` instead of `AcceptPurgatory`, causing the event to be stored without the corresponding git data or state events ever arriving. The fix returns `AcceptPurgatory` for replacements of purgatory entries so the updated event is held in purgatory until git data arrives. + - Repository identifiers containing characters that require percent-encoding in URLs (e.g. spaces, emoji) are now accepted and served correctly. NIP-01 places no restriction on `d` tag values and NIP-34 only recommends kebab-case without mandating it, so rejecting non-kebab identifiers was overly strict. Identifiers are stored verbatim on disk and percent-encoded when used in URLs, per the `nostr://` clone URL spec formalised in [NIP-34 PR #2312](https://github.com/nostr-protocol/nips/pull/2312) and the GRASP-01 HTTP path spec. The landing page clone URL now also correctly percent-encodes the identifier. ### Changed diff --git a/src/nostr/policy/announcement.rs b/src/nostr/policy/announcement.rs index aba5181..b474fbb 100644 --- a/src/nostr/policy/announcement.rs +++ b/src/nostr/policy/announcement.rs @@ -137,8 +137,10 @@ impl AnnouncementPolicy { "Replacement announcement (purgatory) - replacing purgatory entry" ); self.replace_purgatory_announcement(event, &announcement); - // Return Accept (not AcceptPurgatory) - this is a replacement, not new - return validation_result; + // Return AcceptPurgatory - git data hasn't arrived yet so the + // announcement must NOT be saved to the database. The purgatory + // entry has already been updated above with the newer event. + return AnnouncementResult::AcceptPurgatory; } // No existing announcement - route to purgatory -- cgit v1.2.3