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 /grasp-audit/src/client.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 'grasp-audit/src/client.rs')
| -rw-r--r-- | grasp-audit/src/client.rs | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/grasp-audit/src/client.rs b/grasp-audit/src/client.rs index 91a93dc..5c263ad 100644 --- a/grasp-audit/src/client.rs +++ b/grasp-audit/src/client.rs | |||
| @@ -209,6 +209,36 @@ impl AuditClient { | |||
| 209 | Ok(event_id) | 209 | Ok(event_id) |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | /// Send event and note whether it entered purgatory (not served) or was served immediately. | ||
| 213 | /// | ||
| 214 | /// This is a tolerant version of `send_event_expect_purgatory_not_served` that doesn't | ||
| 215 | /// fail if purgatory is not observed. It returns whether purgatory was observed so | ||
| 216 | /// fixtures can proceed regardless of relay implementation status. | ||
| 217 | /// | ||
| 218 | /// Returns (EventId, bool) where bool = true if event was NOT served (purgatory observed). | ||
| 219 | pub async fn send_event_and_note_purgatory(&self, event: Event) -> Result<(EventId, bool)> { | ||
| 220 | if self.config.read_only { | ||
| 221 | return Err(anyhow!("Client is in read-only mode")); | ||
| 222 | } | ||
| 223 | |||
| 224 | let output = self.client.send_event(&event).await?; | ||
| 225 | let event_id = *output.id(); | ||
| 226 | |||
| 227 | // Check if any relay rejected the event and return the error message | ||
| 228 | if !output.failed.is_empty() { | ||
| 229 | let (relay_url, error) = output.failed.iter().next().unwrap(); | ||
| 230 | return Err(anyhow!("Relay {} rejected event: {}", relay_url, error)); | ||
| 231 | } | ||
| 232 | |||
| 233 | // Wait a bit for event to propagate | ||
| 234 | tokio::time::sleep(Duration::from_millis(300)).await; | ||
| 235 | |||
| 236 | // Check if event is served (not in purgatory) or not served (in purgatory) | ||
| 237 | let in_purgatory = !self.is_event_on_relay(event.id).await?; | ||
| 238 | |||
| 239 | Ok((event_id, in_purgatory)) | ||
| 240 | } | ||
| 241 | |||
| 212 | /// check if an event is on the relay | 242 | /// check if an event is on the relay |
| 213 | pub async fn is_event_on_relay(&self, id: EventId) -> Result<bool> { | 243 | pub async fn is_event_on_relay(&self, id: EventId) -> Result<bool> { |
| 214 | Ok(!self | 244 | Ok(!self |