upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/nostr/policy
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-01-08 00:41:02 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-01-08 00:41:02 +0000
commit5833c9bdf815699838a0445f750b99b26fd4a3bd (patch)
treebd148e548e5621872615627cdbd88ba577d072ce /src/nostr/policy
parentac3e00a7e102d7ae341f554563646e05aed7edac (diff)
feat(purgatory): track expired events to prevent infinite re-sync loops
Adds expired event tracking to prevent proactive sync from repeatedly fetching and re-adding events that expired from purgatory without finding git data. Key features: - Track expired events for 7 days to prevent re-sync loops - Distinguish synced vs user-submitted events (via socket address) - Allow users to retry expired events (git data might now be available) - Reject synced expired events (prevents infinite loop) - Daily cleanup of expired event records older than 7 days Implementation: - Added expired_events: DashMap<EventId, Instant> to Purgatory - Updated event_ids() to include both purgatory + expired events - Added is_expired(), mark_expired(), cleanup_expired_events() - Updated cleanup() to mark expired events automatically - Added is_synced detection in WritePolicy (localhost:0 = synced) - Policy layer checks is_synced && is_expired() before rejecting Behavior: - Negentropy: Filters expired events before fetching (optimal) - REQ+EOSE: Rejects synced expired events at policy layer - User submissions: Always allowed to retry (skip expired check) Testing: - Added 5 new tests for expired event tracking - All 222 tests passing Fixes the infinite re-sync loop where events without git data would expire, get synced again, expire again, repeat forever.
Diffstat (limited to 'src/nostr/policy')
-rw-r--r--src/nostr/policy/state.rs20
1 files changed, 19 insertions, 1 deletions
diff --git a/src/nostr/policy/state.rs b/src/nostr/policy/state.rs
index 68b1e97..7bbb379 100644
--- a/src/nostr/policy/state.rs
+++ b/src/nostr/policy/state.rs
@@ -43,8 +43,12 @@ impl StatePolicy {
43 43
44 /// Process a state event: validate and align owner repositories 44 /// Process a state event: validate and align owner repositories
45 /// 45 ///
46 /// # Arguments
47 /// * `event` - The state event to process
48 /// * `is_synced` - True if this event came from proactive sync (vs user-submitted)
49 ///
46 /// Returns the true if git data already availale or false if added to purgatory 50 /// Returns the true if git data already availale or false if added to purgatory
47 pub async fn process_state_event(&self, event: &Event) -> Result<WritePolicyResult> { 51 pub async fn process_state_event(&self, event: &Event, is_synced: bool) -> Result<WritePolicyResult> {
48 // Parse state to get HEAD and branch info 52 // Parse state to get HEAD and branch info
49 let state = 53 let state =
50 RepositoryState::from_event(event.clone()).context("Failed to parse state event")?; 54 RepositoryState::from_event(event.clone()).context("Failed to parse state event")?;
@@ -120,6 +124,20 @@ impl StatePolicy {
120 // Event will be saved and broadcast by relay builder 124 // Event will be saved and broadcast by relay builder
121 Ok(WritePolicyResult::Accept) 125 Ok(WritePolicyResult::Accept)
122 } else { 126 } else {
127 // Only reject expired events if they're from sync (not user-submitted)
128 // User-submitted events should be allowed to retry in case git data became available
129 if is_synced && self.ctx.purgatory.is_expired(&event.id) {
130 tracing::debug!(
131 event_id = %event.id,
132 identifier = %state.identifier,
133 "State event previously expired from purgatory (synced), rejecting to prevent re-sync loop"
134 );
135 return Ok(WritePolicyResult::Reject {
136 status: false,
137 message: "invalid: previously expired from purgatory without git data".into(),
138 });
139 }
140
123 // If no git data - add to purgatory 141 // If no git data - add to purgatory
124 // (add_state automatically enqueues for background sync) 142 // (add_state automatically enqueues for background sync)
125 self.ctx 143 self.ctx