diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-12 22:37:49 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-12 22:37:49 +0000 |
| commit | ab3337d0a3bf25c282f4d48e5d6150cf291f41e5 (patch) | |
| tree | 25175a55d9fc90e804fcea93455b437ec9e2f791 | |
| parent | c8ab2c9c294ae9401ff542d0eecc6606b7908412 (diff) | |
docs: updates to deletion design based on blacklists
| -rw-r--r-- | docs/explanation/deletion-requests.md | 193 |
1 files changed, 189 insertions, 4 deletions
diff --git a/docs/explanation/deletion-requests.md b/docs/explanation/deletion-requests.md index 5d6284b..7660774 100644 --- a/docs/explanation/deletion-requests.md +++ b/docs/explanation/deletion-requests.md | |||
| @@ -68,10 +68,28 @@ The deletion system uses three separate data stores: | |||
| 68 | 2. **Holding Database:** Recovery mechanism, prevents accidental permanent deletion | 68 | 2. **Holding Database:** Recovery mechanism, prevents accidental permanent deletion |
| 69 | 3. **Archive Filesystem:** Git data backup, compressed storage | 69 | 3. **Archive Filesystem:** Git data backup, compressed storage |
| 70 | 70 | ||
| 71 | ### Holding Database Operations | ||
| 72 | |||
| 73 | **Automatic Operations:** | ||
| 74 | - **Move to holding:** NIP-09 deletions, blacklist deletions | ||
| 75 | - **Automatic recovery:** Re-publishing after NIP-09 deletion (within retention window) | ||
| 76 | - **Expiry cleanup:** Daily background task removes entries older than retention period | ||
| 77 | |||
| 78 | **Manual Operations:** | ||
| 79 | - **Manual ejection:** Operator force-deletes before retention expires | ||
| 80 | - Use case: Large repos consuming excessive storage | ||
| 81 | - Use case: Confirmed malware requiring immediate permanent deletion | ||
| 82 | - Mechanism: CLI command or admin tool (design in Phase 6) | ||
| 83 | - Logged for audit trail | ||
| 84 | - **Manual restoration:** Operator restores blacklisted repo after removal from blacklist | ||
| 85 | - Future: May support automatic restoration | ||
| 86 | |||
| 71 | ## Deletion Flow | 87 | ## Deletion Flow |
| 72 | 88 | ||
| 73 | ### Standard Mode (Respects Deletions) | 89 | ### Standard Mode (Respects Deletions) |
| 74 | 90 | ||
| 91 | #### NIP-09 Deletion Requests | ||
| 92 | |||
| 75 | ``` | 93 | ``` |
| 76 | 1. Kind 5 deletion request arrives | 94 | 1. Kind 5 deletion request arrives |
| 77 | ↓ | 95 | ↓ |
| @@ -95,6 +113,34 @@ The deletion system uses three separate data stores: | |||
| 95 | - Delete corresponding archive files | 113 | - Delete corresponding archive files |
| 96 | ``` | 114 | ``` |
| 97 | 115 | ||
| 116 | #### Blacklist-Triggered Deletion | ||
| 117 | |||
| 118 | When a repository is added to `NGIT_REPOSITORY_BLACKLIST`, the same deletion flow applies: | ||
| 119 | |||
| 120 | ``` | ||
| 121 | 1. Startup: Scan main DB for repos matching blacklist | ||
| 122 | ↓ | ||
| 123 | 2. For each matching repository: | ||
| 124 | - Query dependent events (same cascade logic) | ||
| 125 | - Archive git repository to .archive/<npub>/<identifier>-<timestamp>.tar.gz | ||
| 126 | - Mark metadata as "blacklist-triggered" (not NIP-09) | ||
| 127 | - Move events to holding database | ||
| 128 | - Delete from main database | ||
| 129 | ↓ | ||
| 130 | 3. Background task (daily): | ||
| 131 | - Check holding database for expired entries | ||
| 132 | - Delete events older than retention period | ||
| 133 | - Delete corresponding archive files | ||
| 134 | ``` | ||
| 135 | |||
| 136 | **Key Differences from NIP-09:** | ||
| 137 | - No author validation required (operator decision) | ||
| 138 | - Triggered on startup, not by event arrival | ||
| 139 | - Metadata marks deletion as blacklist-triggered | ||
| 140 | - `deletion_request_disrespector` does NOT prevent blacklist deletion (see below) | ||
| 141 | |||
| 142 | **Future:** When dynamic blacklist updates are supported, deletion will trigger immediately on config change instead of waiting for restart. | ||
| 143 | |||
| 98 | ### Archival Mode (Disrespector) | 144 | ### Archival Mode (Disrespector) |
| 99 | 145 | ||
| 100 | When `deletion_request_disrespector = true`: | 146 | When `deletion_request_disrespector = true`: |
| @@ -111,12 +157,22 @@ When `deletion_request_disrespector = true`: | |||
| 111 | Result: Archival relay preserves all content | 157 | Result: Archival relay preserves all content |
| 112 | ``` | 158 | ``` |
| 113 | 159 | ||
| 160 | **Important:** Disrespector mode ONLY affects NIP-09 user-initiated deletions. It does NOT prevent blacklist-triggered deletions. | ||
| 161 | |||
| 162 | **Rationale:** | ||
| 163 | - NIP-09 deletions are user agency decisions (left-pad protection needed) | ||
| 164 | - Blacklist deletions are operator moderation decisions (spam/malware/abuse) | ||
| 165 | - Archival relays still need ability to moderate malicious content | ||
| 166 | - Different policy goals: preservation vs. safety | ||
| 167 | |||
| 114 | **Implementation Note:** We need to verify that `nostr-relay-builder` doesn't automatically process deletion requests at the relay library level. If it does, we'll need to override or disable this behavior when disrespector mode is enabled. This will be investigated in Phase 6. | 168 | **Implementation Note:** We need to verify that `nostr-relay-builder` doesn't automatically process deletion requests at the relay library level. If it does, we'll need to override or disable this behavior when disrespector mode is enabled. This will be investigated in Phase 6. |
| 115 | 169 | ||
| 116 | ## Recovery Mechanism | 170 | ## Recovery Mechanism |
| 117 | 171 | ||
| 118 | The holding database enables **accidental deletion recovery**: | 172 | The holding database enables **accidental deletion recovery**: |
| 119 | 173 | ||
| 174 | ### Automatic Recovery (NIP-09 Deletions) | ||
| 175 | |||
| 120 | ``` | 176 | ``` |
| 121 | Scenario: Owner deletes repository, then changes their mind | 177 | Scenario: Owner deletes repository, then changes their mind |
| 122 | 178 | ||
| @@ -139,9 +195,102 @@ Scenario: Owner deletes repository, then changes their mind | |||
| 139 | - Return: "New repository created" | 195 | - Return: "New repository created" |
| 140 | ``` | 196 | ``` |
| 141 | 197 | ||
| 198 | ### Blacklist Recovery | ||
| 199 | |||
| 200 | When a repository is removed from the blacklist: | ||
| 201 | |||
| 202 | **Option 1: Manual Restoration (Initial Implementation)** | ||
| 203 | - Operator removes from blacklist config | ||
| 204 | - Operator manually restores from holding DB if desired | ||
| 205 | - Provides explicit control over recovery decisions | ||
| 206 | |||
| 207 | **Option 2: Automatic Restoration (Future Enhancement)** | ||
| 208 | - On startup, detect repos in holding area no longer blacklisted | ||
| 209 | - Automatically restore to main DB if within retention period | ||
| 210 | - Requires careful design to prevent unwanted restorations | ||
| 211 | |||
| 212 | **Decision:** Start with manual restoration, evaluate automatic restoration in Phase 6. | ||
| 213 | |||
| 214 | ### Manual Ejection from Holding Area | ||
| 215 | |||
| 216 | Operators need ability to **force-delete** items from holding area before retention period expires: | ||
| 217 | |||
| 218 | **Use Cases:** | ||
| 219 | 1. Large repositories consuming excessive storage | ||
| 220 | 2. Confirmed malware/abuse that shouldn't be recoverable | ||
| 221 | 3. Legal/compliance requirements for immediate permanent deletion | ||
| 222 | |||
| 223 | **Mechanism (TBD in Phase 6):** | ||
| 224 | - Admin CLI command: `ngit-grasp eject <npub>/<identifier>` | ||
| 225 | - Or database operation with proper tooling | ||
| 226 | - Immediately delete from holding DB and archive filesystem | ||
| 227 | - Log operation for audit trail | ||
| 228 | - Metric: `ngit_manual_ejections_total` | ||
| 229 | |||
| 230 | **Safety Considerations:** | ||
| 231 | - Manual ejection is permanent (no undo) | ||
| 232 | - Should require confirmation for safety | ||
| 233 | - Should log npub, identifier, reason, operator | ||
| 234 | - Consider retention policy override vs immediate deletion | ||
| 235 | |||
| 236 | ## Blacklist Deletion Integration | ||
| 237 | |||
| 238 | ### Overview | ||
| 239 | |||
| 240 | Blacklist-triggered deletions use the **same infrastructure** as NIP-09 deletion requests: | ||
| 241 | - Same holding database for 90-day retention | ||
| 242 | - Same git archive mechanism | ||
| 243 | - Same cascade deletion logic | ||
| 244 | - Same recovery capabilities (if unblacklisted) | ||
| 245 | |||
| 246 | ### Key Differences from NIP-09 | ||
| 247 | |||
| 248 | | Aspect | NIP-09 Deletion | Blacklist Deletion | | ||
| 249 | |--------|----------------|-------------------| | ||
| 250 | | **Trigger** | Kind 5 event arrives | Startup scan of main DB | | ||
| 251 | | **Author validation** | Required (pubkey match) | Not applicable (operator decision) | | ||
| 252 | | **Disrespector mode** | Prevents deletion | Does NOT prevent deletion | | ||
| 253 | | **Purpose** | User agency | Moderation/safety | | ||
| 254 | | **Recovery** | Automatic (re-publish) | Manual (operator decision) | | ||
| 255 | | **Metadata** | Links to Kind 5 event | Marks "blacklist-triggered" | | ||
| 256 | |||
| 257 | ### Why Disrespector Doesn't Prevent Blacklist Deletion | ||
| 258 | |||
| 259 | **Design Decision:** The `deletion_request_disrespector` configuration ONLY affects NIP-09 user-initiated deletions. It does NOT prevent blacklist-triggered deletions. | ||
| 260 | |||
| 261 | **Rationale:** | ||
| 262 | 1. **Different Policy Goals:** | ||
| 263 | - NIP-09 = User agency (prevent left-pad) | ||
| 264 | - Blacklist = Operator safety (prevent spam/malware/abuse) | ||
| 265 | |||
| 266 | 2. **Archival Relays Need Moderation:** | ||
| 267 | - Archive mode preserves valuable deleted content | ||
| 268 | - But still must handle malicious content | ||
| 269 | - Spam, malware, abuse require operator intervention | ||
| 270 | |||
| 271 | 3. **Separate Concerns:** | ||
| 272 | - Disrespector = "Don't honor user deletion requests" | ||
| 273 | - Blacklist = "Don't accept these specific repos regardless of source" | ||
| 274 | |||
| 275 | ### Detection and Timing | ||
| 276 | |||
| 277 | **Initial Implementation (Startup Scan):** | ||
| 278 | ``` | ||
| 279 | 1. Relay starts up | ||
| 280 | 2. Load blacklist configuration | ||
| 281 | 3. Scan main database for matching repos | ||
| 282 | 4. For each match: archive → holding DB → delete from main | ||
| 283 | 5. Continue normal operation | ||
| 284 | ``` | ||
| 285 | |||
| 286 | **Future Enhancement (Dynamic Updates):** | ||
| 287 | - Watch for configuration file changes | ||
| 288 | - Trigger deletion immediately on blacklist addition | ||
| 289 | - Requires careful design to avoid race conditions | ||
| 290 | |||
| 142 | ## Cascade Deletion Strategy | 291 | ## Cascade Deletion Strategy |
| 143 | 292 | ||
| 144 | When a repository announcement is deleted, we cascade delete **all dependent events**: | 293 | When a repository announcement is deleted (NIP-09 or blacklist), we cascade delete **all dependent events**: |
| 145 | 294 | ||
| 146 | ### Rationale | 295 | ### Rationale |
| 147 | 296 | ||
| @@ -244,7 +393,9 @@ When npub1alice deletes her announcement: | |||
| 244 | **Env:** `NGIT_DELETION_REQUEST_DISRESPECTOR` | 393 | **Env:** `NGIT_DELETION_REQUEST_DISRESPECTOR` |
| 245 | 394 | ||
| 246 | **Description:** | 395 | **Description:** |
| 247 | When `true`, relay ignores deletion requests and acts as an archival server. Critical for preventing left-pad scenarios by ensuring at least some relays preserve deleted content. | 396 | When `true`, relay ignores **NIP-09 deletion requests** and acts as an archival server. Critical for preventing left-pad scenarios by ensuring at least some relays preserve deleted content. |
| 397 | |||
| 398 | **IMPORTANT:** This setting does NOT prevent blacklist-triggered deletions. Blacklist is for operator moderation (spam/malware/abuse), which archival relays still need. | ||
| 248 | 399 | ||
| 249 | **Use Cases:** | 400 | **Use Cases:** |
| 250 | - Community archival relays | 401 | - Community archival relays |
| @@ -335,6 +486,10 @@ When implementation is complete, the following documentation will be updated: | |||
| 335 | - Large-scale testing | 486 | - Large-scale testing |
| 336 | - Race condition investigation | 487 | - Race condition investigation |
| 337 | - Lock strategy finalization | 488 | - Lock strategy finalization |
| 489 | - **Blacklist deletion behavior** (startup scanning, cascade logic, metadata) | ||
| 490 | - **Blacklist + disrespector interaction** (disrespector doesn't prevent blacklist deletion) | ||
| 491 | - **Manual ejection mechanism** (CLI command, safety, logging) | ||
| 492 | - **Blacklist recovery flow** (manual vs automatic restoration) | ||
| 338 | 493 | ||
| 339 | ## Security Considerations | 494 | ## Security Considerations |
| 340 | 495 | ||
| @@ -357,23 +512,32 @@ When implementation is complete, the following documentation will be updated: | |||
| 357 | - Mitigation: Background cleanup enforces retention limits | 512 | - Mitigation: Background cleanup enforces retention limits |
| 358 | - Mitigation: Compressed tar.gz archives | 513 | - Mitigation: Compressed tar.gz archives |
| 359 | - Mitigation: Configurable retention period | 514 | - Mitigation: Configurable retention period |
| 515 | - Mitigation: Manual ejection mechanism for emergency storage relief | ||
| 360 | 516 | ||
| 361 | **Recovery Abuse:** | 517 | **Recovery Abuse:** |
| 362 | - Mitigation: Recovery only within retention window | 518 | - Mitigation: Recovery only within retention window |
| 363 | - Mitigation: Must be original owner (pubkey match) | 519 | - Mitigation: Must be original owner (pubkey match) |
| 364 | - Mitigation: Normal announcement validation applies | 520 | - Mitigation: Normal announcement validation applies |
| 365 | 521 | ||
| 522 | **Blacklist Bypass:** | ||
| 523 | - Mitigation: Blacklist checked on startup (retroactive deletion) | ||
| 524 | - Mitigation: Blacklist checked during announcement validation (prevents new) | ||
| 525 | - Mitigation: Blacklist deletion not affected by disrespector mode | ||
| 526 | - Note: Manual ejection available for confirmed abuse | ||
| 527 | |||
| 366 | ## Monitoring & Metrics | 528 | ## Monitoring & Metrics |
| 367 | 529 | ||
| 368 | **Prometheus Metrics (Planned):** | 530 | **Prometheus Metrics (Planned):** |
| 369 | - `ngit_deletion_requests_total` - Count of deletion requests received | 531 | - `ngit_deletion_requests_total` - Count of NIP-09 deletion requests received |
| 370 | - `ngit_deletion_requests_processed` - Count actually processed (disrespector mode = 0) | 532 | - `ngit_deletion_requests_processed` - Count actually processed (disrespector mode = 0) |
| 533 | - `ngit_blacklist_deletions_total` - Count of blacklist-triggered deletions | ||
| 371 | - `ngit_holding_database_events` - Current event count in holding DB | 534 | - `ngit_holding_database_events` - Current event count in holding DB |
| 372 | - `ngit_holding_database_size_bytes` - Holding DB disk usage | 535 | - `ngit_holding_database_size_bytes` - Holding DB disk usage |
| 373 | - `ngit_archive_files_total` - Count of archive tar.gz files | 536 | - `ngit_archive_files_total` - Count of archive tar.gz files |
| 374 | - `ngit_archive_size_bytes` - Total archive disk usage | 537 | - `ngit_archive_size_bytes` - Total archive disk usage |
| 375 | - `ngit_recoveries_total` - Count of successful recoveries | 538 | - `ngit_recoveries_total` - Count of successful automatic recoveries |
| 376 | - `ngit_permanent_deletions_total` - Count of events permanently deleted (post-retention) | 539 | - `ngit_permanent_deletions_total` - Count of events permanently deleted (post-retention) |
| 540 | - `ngit_manual_ejections_total` - Count of operator-initiated ejections from holding area | ||
| 377 | 541 | ||
| 378 | ## Testing Strategy | 542 | ## Testing Strategy |
| 379 | 543 | ||
| @@ -421,6 +585,27 @@ Coordinate between archival relays to ensure redundant preservation of deleted c | |||
| 421 | ### Recovery Notifications | 585 | ### Recovery Notifications |
| 422 | Notify repository owner when content is recovered from holding database, allowing them to confirm or re-delete. | 586 | Notify repository owner when content is recovered from holding database, allowing them to confirm or re-delete. |
| 423 | 587 | ||
| 588 | ### Dynamic Blacklist Updates | ||
| 589 | Currently blacklist changes only take effect on startup. Future enhancement: | ||
| 590 | - Monitor configuration file for changes | ||
| 591 | - Apply blacklist additions immediately (trigger deletion) | ||
| 592 | - Apply blacklist removals immediately (optional auto-recovery) | ||
| 593 | - Requires careful concurrency design | ||
| 594 | |||
| 595 | ### Automatic Blacklist Recovery | ||
| 596 | Currently removing from blacklist requires manual restoration. Future enhancement: | ||
| 597 | - Detect unblacklisted repos in holding area on startup | ||
| 598 | - Automatically restore if within retention period | ||
| 599 | - Configurable policy: auto-restore vs manual-only | ||
| 600 | |||
| 601 | ### Enhanced Manual Ejection | ||
| 602 | Current design includes basic manual ejection. Future enhancements: | ||
| 603 | - Web UI for holding area management | ||
| 604 | - Batch ejection operations | ||
| 605 | - Selective ejection (events only, keep git archive) | ||
| 606 | - Export before ejection (backup) | ||
| 607 | - Enhanced audit logging with operator identity | ||
| 608 | |||
| 424 | ## Conclusion | 609 | ## Conclusion |
| 425 | 610 | ||
| 426 | The deletion request system balances three competing needs: | 611 | The deletion request system balances three competing needs: |