diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-23 15:41:32 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-23 15:41:32 +0000 |
| commit | c54ce061d6d278cce8362d5af085808ca60c239b (patch) | |
| tree | ec967d6195d9f7ec4f061449596611afe3a0950f /src/nostr/builder.rs | |
| parent | e0ad39a489b3398f8208713bf728db0cb11475b0 (diff) | |
| parent | 113928aa84894ea8f65c247d9987527e792b32a9 (diff) | |
feat: announcement purgatory
Extends purgatory to hold repository announcements until git data arrives,
preventing empty repositories from being served to clients.
When an announcement is received, a bare repo is created immediately and the
announcement is held in purgatory. It is only promoted and served once a git
push confirms real content exists. If no push arrives before expiry, the bare
repo is deleted and the announcement is silently discarded.
Key behaviours:
- Soft expiry: announcements are hidden from clients but kept alive while git
pushes are in progress, reviving on successful push
- Expiry is extended when a matching state event or git push is observed
- NIP-09 deletion events remove announcements from purgatory
- Purgatory state (announcements, state events, PR events, expired set) is
persisted to disk on graceful shutdown and restored on startup, with elapsed
downtime subtracted from expiry deadlines
- Purgatory announcements drive StateOnly sync in the sync system so state
events are fetched from listed relays before promotion
- SyncLevel added to RepoSyncIndex to distinguish purgatory repos (StateOnly)
from promoted repos (Full L2+L3 sync)
Diffstat (limited to 'src/nostr/builder.rs')
| -rw-r--r-- | src/nostr/builder.rs | 33 |
1 files changed, 31 insertions, 2 deletions
diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs index 713c129..7a05348 100644 --- a/src/nostr/builder.rs +++ b/src/nostr/builder.rs | |||
| @@ -14,10 +14,11 @@ use nostr_relay_builder::prelude::*; | |||
| 14 | use crate::config::{Config, DatabaseBackend}; | 14 | use crate::config::{Config, DatabaseBackend}; |
| 15 | use crate::nostr::events::RepositoryAnnouncement; | 15 | use crate::nostr::events::RepositoryAnnouncement; |
| 16 | use crate::nostr::policy::{ | 16 | use crate::nostr::policy::{ |
| 17 | AnnouncementPolicy, AnnouncementResult, PolicyContext, PrEventPolicy, ReferenceResult, | 17 | AnnouncementPolicy, AnnouncementResult, DeletionPolicy, PolicyContext, PrEventPolicy, |
| 18 | RelatedEventPolicy, StatePolicy, StateResult, | 18 | ReferenceResult, RelatedEventPolicy, StatePolicy, StateResult, |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | |||
| 21 | /// Type alias for the shared database used by the relay | 22 | /// Type alias for the shared database used by the relay |
| 22 | pub type SharedDatabase = Arc<dyn NostrDatabase>; | 23 | pub type SharedDatabase = Arc<dyn NostrDatabase>; |
| 23 | 24 | ||
| @@ -28,6 +29,7 @@ pub type SharedDatabase = Arc<dyn NostrDatabase>; | |||
| 28 | /// - `StatePolicy` - State event validation + ref alignment | 29 | /// - `StatePolicy` - State event validation + ref alignment |
| 29 | /// - `PrEventPolicy` - PR/PR Update validation | 30 | /// - `PrEventPolicy` - PR/PR Update validation |
| 30 | /// - `RelatedEventPolicy` - Forward/backward reference checking | 31 | /// - `RelatedEventPolicy` - Forward/backward reference checking |
| 32 | /// - `DeletionPolicy` - NIP-09 event deletion request handling | ||
| 31 | /// | 33 | /// |
| 32 | /// Uses stateful database queries to check event relationships. | 34 | /// Uses stateful database queries to check event relationships. |
| 33 | #[derive(Clone)] | 35 | #[derive(Clone)] |
| @@ -37,6 +39,7 @@ pub struct Nip34WritePolicy { | |||
| 37 | state_policy: StatePolicy, | 39 | state_policy: StatePolicy, |
| 38 | pr_event_policy: PrEventPolicy, | 40 | pr_event_policy: PrEventPolicy, |
| 39 | related_event_policy: RelatedEventPolicy, | 41 | related_event_policy: RelatedEventPolicy, |
| 42 | deletion_policy: DeletionPolicy, | ||
| 40 | } | 43 | } |
| 41 | 44 | ||
| 42 | impl std::fmt::Debug for Nip34WritePolicy { | 45 | impl std::fmt::Debug for Nip34WritePolicy { |
| @@ -68,6 +71,7 @@ impl Nip34WritePolicy { | |||
| 68 | state_policy: StatePolicy::new(ctx.clone()), | 71 | state_policy: StatePolicy::new(ctx.clone()), |
| 69 | pr_event_policy: PrEventPolicy::new(ctx.clone()), | 72 | pr_event_policy: PrEventPolicy::new(ctx.clone()), |
| 70 | related_event_policy: RelatedEventPolicy::new(ctx.clone()), | 73 | related_event_policy: RelatedEventPolicy::new(ctx.clone()), |
| 74 | deletion_policy: DeletionPolicy::new(ctx.clone()), | ||
| 71 | ctx, | 75 | ctx, |
| 72 | } | 76 | } |
| 73 | } | 77 | } |
| @@ -205,6 +209,30 @@ impl Nip34WritePolicy { | |||
| 205 | } | 209 | } |
| 206 | } | 210 | } |
| 207 | } | 211 | } |
| 212 | AnnouncementResult::AcceptPurgatory => { | ||
| 213 | // New announcement - add to purgatory | ||
| 214 | match self.announcement_policy.add_to_purgatory(event) { | ||
| 215 | Ok(()) => { | ||
| 216 | tracing::info!( | ||
| 217 | "Accepted announcement to purgatory: {} (waiting for git data)", | ||
| 218 | event_id_str | ||
| 219 | ); | ||
| 220 | |||
| 221 | WritePolicyResult::Reject { | ||
| 222 | status: true, // Client sees OK | ||
| 223 | message: "purgatory: won't be served until git data arrives".into(), | ||
| 224 | } | ||
| 225 | } | ||
| 226 | Err(e) => { | ||
| 227 | tracing::warn!( | ||
| 228 | "Failed to add announcement to purgatory {}: {}", | ||
| 229 | event_id_str, | ||
| 230 | e | ||
| 231 | ); | ||
| 232 | WritePolicyResult::reject(e) | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 208 | AnnouncementResult::AcceptMaintainer => { | 236 | AnnouncementResult::AcceptMaintainer => { |
| 209 | // Parse announcement to get details for logging | 237 | // Parse announcement to get details for logging |
| 210 | match RepositoryAnnouncement::from_event(event.clone()) { | 238 | match RepositoryAnnouncement::from_event(event.clone()) { |
| @@ -621,6 +649,7 @@ impl WritePolicy for Nip34WritePolicy { | |||
| 621 | ); | 649 | ); |
| 622 | WritePolicyResult::Accept | 650 | WritePolicyResult::Accept |
| 623 | } | 651 | } |
| 652 | Kind::EventDeletion => self.deletion_policy.handle(event).await, | ||
| 624 | _ => self.handle_related_event(event, "Event").await, | 653 | _ => self.handle_related_event(event, "Event").await, |
| 625 | } | 654 | } |
| 626 | }) | 655 | }) |