mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 23:18:24 +00:00
fix: deduplicate archival deletion requests
This commit is contained in:
@@ -328,11 +328,16 @@ When `deletion_request_disrespector = true`:
|
||||
```
|
||||
1. Kind 5 deletion request arrives
|
||||
↓
|
||||
2. Store deletion request event in main database
|
||||
2. Check idempotency:
|
||||
- if every actionable `e`/`a` target is already covered by an existing
|
||||
same-author kind-5 request, return a duplicate success response without
|
||||
storing the new deletion event
|
||||
↓
|
||||
3. Do NOT process deletion
|
||||
3. Store deletion request event in main database
|
||||
↓
|
||||
4. Repository and events remain fully accessible
|
||||
4. Do NOT process deletion
|
||||
↓
|
||||
5. Repository and events remain fully accessible
|
||||
↓
|
||||
Result: Archival relay preserves all content
|
||||
```
|
||||
@@ -349,9 +354,11 @@ NIP-62 vanish requests. It does NOT prevent blacklist-triggered deletions.
|
||||
**Implementation Note:** Implemented. The LMDB backend's automatic NIP-09 and
|
||||
NIP-62 processing is disabled (`process_nip09(false)`, `process_nip62(false)`);
|
||||
ngit-grasp owns deletion handling in relay policy code, which short-circuits
|
||||
when `deletion_request_disrespector` is set — storing but not acting on kind-5
|
||||
or kind-62 requests. NIP-09 and NIP-62 are omitted from the NIP-11
|
||||
`supported_nips` list in this mode.
|
||||
already-covered NIP-09 deletions before the `deletion_request_disrespector`
|
||||
archival-mode branch. When `deletion_request_disrespector` is set, new kind-5
|
||||
requests are stored but not acted on, and kind-62 requests are stored but not
|
||||
acted on. NIP-09 and NIP-62 are omitted from the NIP-11 `supported_nips` list
|
||||
in this mode.
|
||||
|
||||
## Recovery Mechanism
|
||||
|
||||
|
||||
@@ -73,9 +73,32 @@ impl DeletionPolicy {
|
||||
/// main database) so clients see an OK and the request is preserved, but it
|
||||
/// is NOT acted upon — no purgatory eviction, no main-DB deletion, and no
|
||||
/// tombstone recording. The targeted events therefore remain fully
|
||||
/// accessible. The same archival-mode contract is applied to NIP-62 vanish
|
||||
/// requests in [`DeletionService::handle_vanish`](super::service::DeletionService::handle_vanish).
|
||||
/// accessible. Already-covered kind-5 requests are still treated as
|
||||
/// duplicates before archival-mode storage to avoid polluting the served
|
||||
/// deletion-request stream. The same archival-mode contract is applied to
|
||||
/// NIP-62 vanish requests in [`DeletionService::handle_vanish`](super::service::DeletionService::handle_vanish).
|
||||
pub async fn handle(&self, event: &Event) -> WritePolicyResult {
|
||||
match self
|
||||
.ctx
|
||||
.tombstones
|
||||
.deletion_targets_already_covered(event)
|
||||
.await
|
||||
{
|
||||
Ok(true) => {
|
||||
tracing::info!(
|
||||
event_id = %event.id.to_hex(),
|
||||
author = %event.pubkey.to_hex(),
|
||||
"Skipping duplicate NIP-09 deletion request; all actionable targets are already covered"
|
||||
);
|
||||
return duplicate("deletion target(s) already covered");
|
||||
}
|
||||
Ok(false) => {}
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "Tombstone lookup failed during duplicate deletion check");
|
||||
return reject_error(format!("internal error checking deletion coverage: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
// Archival mode: store the deletion request but do not process it.
|
||||
if self.ctx.config.deletion_request_disrespector {
|
||||
tracing::info!(
|
||||
@@ -104,27 +127,6 @@ impl DeletionPolicy {
|
||||
return reject_invalid("too many deletion targets");
|
||||
}
|
||||
|
||||
match self
|
||||
.ctx
|
||||
.tombstones
|
||||
.deletion_targets_already_covered(event)
|
||||
.await
|
||||
{
|
||||
Ok(true) => {
|
||||
tracing::info!(
|
||||
event_id = %event.id.to_hex(),
|
||||
author = %event.pubkey.to_hex(),
|
||||
"Skipping duplicate NIP-09 deletion request; all actionable targets are already covered"
|
||||
);
|
||||
return duplicate("deletion target(s) already covered");
|
||||
}
|
||||
Ok(false) => {}
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "Tombstone lookup failed during duplicate deletion check");
|
||||
return reject_error(format!("internal error checking deletion coverage: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate authorship of all targets first. If any `e`-tag target exists
|
||||
// in the main DB and is owned by someone else, the whole deletion is
|
||||
// invalid (mirrors the backend's `handle_deletion_event` returning
|
||||
|
||||
Reference in New Issue
Block a user