upleb.uk

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

summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/explanation/announcements-purgatory-design.md185
1 files changed, 185 insertions, 0 deletions
diff --git a/docs/explanation/announcements-purgatory-design.md b/docs/explanation/announcements-purgatory-design.md
new file mode 100644
index 0000000..c9077d9
--- /dev/null
+++ b/docs/explanation/announcements-purgatory-design.md
@@ -0,0 +1,185 @@
1# Announcements Purgatory Design
2
3## Problem Statement
4
5**Primary problem:** Empty bare git repos mislead clients into thinking we host content.
6
7When an announcement arrives, we must create the bare repo immediately (so git pushes can succeed). But if no git data ever arrives, we serve an empty repo and its announcement indefinitely. Clients see the announcement, try to clone, and get nothing. This is misleading.
8
9**Secondary problem:** Sync downloads refs to deleted repos.
10
11When a repo expires or is cleaned up, sync may still try to download state event refs to it. We need announcements to remain in a holding state until git data proves the repo has content worth serving.
12
13## Solution Overview
14
15New announcements go to **purgatory** instead of being immediately accepted:
16
171. **Announcement arrives** - Create bare repo immediately, add announcement to purgatory
182. **Git data arrives** - Promote announcement from purgatory to active (now served to clients)
193. **No git data before expiry** - Delete bare repo, discard announcement (never served)
20
21This ensures we only serve announcements for repos that actually have content.
22
23## Key Design Decisions
24
25### 1. Bare Repo Created Immediately
26
27**Decision:** Create the bare git repo when announcement enters purgatory.
28
29**Why:** Git pushes may arrive at any time. Without a repo, pushes fail.
30
31**Consequence:** We allocate disk space for repos that may expire unused. Must delete repos on expiry.
32
33### 2. Git Data Triggers Promotion
34
35**Decision:** Git data arrival promotes the announcement to active status.
36
37**Why:** Git data proves the repository has content. State events alone don't prove content exists - they could reference empty repos.
38
39**Where:** Promotion happens in the git receive path after successful push/fetch with data.
40
41### 3. Replacement Announcements Skip Purgatory
42
43**Decision:** Announcements replacing an existing active announcement are accepted immediately.
44
45**Why:** The repository is already proven active with content.
46
47**How:** Check if active announcement exists for `(pubkey, identifier)` before routing to purgatory.
48
49### 4. Expiry Extension (Two Places)
50
51**Decision:** Extend purgatory announcement expiry in two scenarios:
52
53| Trigger | Location | Why |
54|---------|----------|-----|
55| State event arrives | `StatePolicy::process_state_event()` | Repo is actively receiving metadata |
56| Git auth extends state event | `src/git/auth.rs` | Repo is actively receiving git data |
57
58**Why:** Prevents premature expiry during slow sync operations or multi-step pushes.
59
60### 5. State Events Consider Purgatory Announcements
61
62**Decision:** When validating state events, check purgatory announcements for authorization.
63
64**Why:** State events may arrive before git data promotes the announcement. They still need authorization from the announcement's maintainer set.
65
66## Data Structure
67
68```rust
69// Key: (owner pubkey, identifier) - identifier alone is NOT unique
70announcement_purgatory: Arc<DashMap<(PublicKey, String), AnnouncementPurgatoryEntry>>
71
72pub struct AnnouncementPurgatoryEntry {
73 pub event: Event,
74 pub identifier: String,
75 pub owner: PublicKey,
76 pub repo_path: PathBuf,
77 pub created_at: Instant,
78 pub expires_at: Instant,
79}
80```
81
82**Indexed by `(pubkey, identifier)`** because identifier is not unique across different owners.
83
84## Flows
85
86### New Announcement Flow
87
88```
89Announcement arrives
90 |
91 v
92Is there an active announcement for (pubkey, identifier)?
93 |
94 +-- YES --> Accept immediately (replacement)
95 |
96 +-- NO --> Create bare repo
97 Add to purgatory
98 Return OK to client (but don't serve)
99```
100
101### Git Data Arrival Flow
102
103```
104Git push/fetch completes with data
105 |
106 v
107Is there a purgatory announcement for (pubkey, identifier)?
108 |
109 +-- YES --> Promote to active (move to database)
110 | Now served to clients
111 |
112 +-- NO --> Normal processing
113```
114
115### State Event Arrival Flow
116
117```
118State event arrives
119 |
120 v
121Is there an active announcement?
122 |
123 +-- YES --> Normal validation
124 |
125 +-- NO --> Check purgatory for announcement
126 |
127 +-- Found --> Validate against purgatory announcement
128 | Extend purgatory expiry
129 | State event goes to state purgatory
130 |
131 +-- Not found --> Reject or state purgatory
132```
133
134## Edge Cases
135
136| Scenario | Behavior |
137|----------|----------|
138| Git data before announcement | Push fails (no repo exists) |
139| Announcement expires, no git data | Delete bare repo, discard announcement |
140| State expires, announcement in purgatory | Announcement keeps its own expiry |
141| Multiple owners, same identifier | Each tracked separately by `(pubkey, identifier)` |
142| **Newer announcement replaces older (same pubkey)** | Replace purgatory entry, extend expiry |
143| **Newer announcement changes services (unacceptable)** | Clear older announcement from purgatory for that `(pubkey, identifier)` |
144| Deletion event for purgatory announcement | Remove from purgatory, delete bare repo |
145
146## Purgatory Exit Conditions
147
148An announcement leaves purgatory via:
149
150| Exit | Trigger | Action |
151|------|---------|--------|
152| **Promotion** | Git data arrives | Move to database, serve to clients |
153| **Expiry** | Timeout | Delete bare repo, discard |
154| **Deletion** | Kind 5 event | Delete bare repo, discard |
155| **Replacement** | Newer announcement (same pubkey, identifier) | Replace entry |
156| **Service change** | Newer announcement no longer lists our service | Discard old entry |
157
158## Integration Points
159
160| File | Change |
161|------|--------|
162| `src/purgatory/mod.rs` | Add `announcement_purgatory` store |
163| `src/purgatory/types.rs` | Add `AnnouncementPurgatoryEntry` |
164| `src/nostr/policy/announcement.rs` | Route new announcements to purgatory |
165| `src/git/receive.rs` | Promote on git data arrival |
166| `src/git/auth.rs` | Extend purgatory expiry when extending state event expiry |
167| `src/nostr/policy/state.rs` | Check purgatory for authorization |
168
169## Testing
170
171- Announcement to purgatory, git data promotes it
172- Announcement expires without git data (repo deleted)
173- State event extends purgatory expiry
174- Git auth extends purgatory expiry
175- Newer announcement replaces older in purgatory
176- Service change clears purgatory entry
177- `(pubkey, identifier)` indexing with multiple owners
178
179## Risks
180
181| Risk | Mitigation |
182|------|------------|
183| Disk exhaustion from purgatory repos | Short expiry, monitor purgatory size |
184| Race between promotion and expiry | Atomic operations |
185| Sync re-fetching expired events | Track expired event IDs |