docs: clarify deletion retention tradeoffs

This commit is contained in:
DanConwayDev
2026-07-17 12:57:09 +01:00
parent 0784c89c70
commit af335228e5
6 changed files with 54 additions and 4 deletions
+2
View File
@@ -286,6 +286,8 @@
# start at relay-observed first_seen_at; used clocks start at last_used_at.
# "Additional" periods begin after their corresponding served period ends.
# Used requests remain served indefinitely when deletion-request-disrespector is true.
# Seconds permit short tests; production periods should be at least one day and
# comfortably exceed worst-case deletion/archive processing time.
# How long an unused request remains served from first_seen_at
# CLI: --deletion-request-retention-unused-served-secs <seconds>
+30
View File
@@ -83,6 +83,18 @@ rejected under valid ownership, coordinate-cutoff, relay-targeting, and other
NIP-09/NIP-62 rules. Merely matching a tag, inspecting the request, or discovering
that a different author owns the target does not count as use.
The unserved gate is the deliberate observation period that establishes whether
an apparently unused request still has practical value. While the request was
served, clients that saw it could avoid sending the deleted target to this relay,
even if copies of that target continued circulating elsewhere. Removing the
request from relay queries gives those late copies an opportunity to reach the
relay again. The retained Tombstone gate still rejects a covered target; that
rejection is evidence of current utility, so the winning request becomes used,
is promoted back to Main, and begins the longer used lifecycle. If no covered
target arrives during the unserved period, the request has supplied no evidence
that its gate is still needed and can expire. Replaying the deletion or vanish
request itself is not such evidence and does not update `last_used_at`.
#### Used
When a request is used:
@@ -115,6 +127,14 @@ physical removal from Main and Tombstones is asynchronous cleanup and may occur
on the next scheduled pass. This bounded cleanup delay is intentional and does
not extend admission-gate eligibility past the deadline.
Durations use seconds for configuration consistency and to permit short automated
tests. Production values are expected to be at least one day and comfortably
longer than the worst-case processing time for a deletion or vanish request,
including repository archival and cascade work. Sub-day values are a testing
facility, not a supported production operating point; cleanup is therefore not
coordinated with an initial request handler across an artificially short total
lifetime.
### Multiple matching requests
If several pending requests would independently reject the same arriving event,
@@ -217,6 +237,16 @@ correctness, but their retention clocks do not change. This rule is independent
of database iteration order and prevents one event arrival from extending an
arbitrary number of duplicate requests.
This deterministic single-winner rule applies when one later target admission is
matched against already-pending requests. Initial processing of distinct deletion
requests is intentionally less strict: two requests processed concurrently may
both observe the same stored target before either removal completes and may both
receive use credit. The database deletion API does not report an authoritative
per-event removed count, so eliminating that narrow race would require broader
target-level serialization. The occasional extra used request is accepted: it is
bounded by actual concurrency, does not change deletion correctness, and still
expires through the normal used lifecycle.
### Simplification of target-set deduplication
The lifecycle replaces semantic target-set deduplication and supersession as the
+2
View File
@@ -1190,6 +1190,8 @@ These options govern the bounded lifecycle of accepted NIP-09 deletion requests
The relay derives unused deadlines from relay-observed `first_seen_at`, never from the client-controlled event `created_at`. It derives used deadlines from `last_used_at`. Each `...ADDITIONAL...` option begins only after its corresponding served period ends; it is not a total retention duration.
Seconds are used for configuration consistency and short automated tests. In production, configure every period to at least one day and keep each total lifecycle comfortably longer than the worst-case deletion processing time, including repository archival and cascade work. Sub-day values are intended only for tests.
#### `NGIT_DELETION_REQUEST_RETENTION_UNUSED_SERVED_SECS`
- **Description:** How long an unused deletion or vanish request remains served from relay-observed `first_seen_at`
+9 -4
View File
@@ -198,7 +198,8 @@ let
default = 2592000;
description = ''
Time in seconds an unused deletion or vanish request remains served
from relay-observed first_seen_at (default: 30 days).
from relay-observed first_seen_at (default: 30 days). Production
periods should be at least one day; sub-day values are for tests.
'';
};
@@ -208,7 +209,8 @@ let
description = ''
Additional time in seconds after unused serving ends that a deletion
or vanish request remains unserved but eligible to gate admission
(default: 180 days).
(default: 180 days). Production periods should be at least one day;
sub-day values are for tests.
'';
};
@@ -219,7 +221,8 @@ let
Normal-mode time in seconds a used deletion or vanish request
remains served after last_used_at (default: 270 days, 9 fixed
30-day months). Used requests remain served indefinitely when
deletionRequestDisrespector is enabled.
deletionRequestDisrespector is enabled. Production periods should
be at least one day; sub-day values are for tests.
'';
};
@@ -230,7 +233,9 @@ let
Additional normal-mode time in seconds after used serving ends that
a deletion or vanish request remains unserved but continues gating
admission (default: 90 days, 3 fixed 30-day months). This duration
does not expire used requests when deletionRequestDisrespector is enabled.
does not expire used requests when deletionRequestDisrespector is
enabled. Production periods should be at least one day; sub-day
values are for tests.
'';
};
};
+3
View File
@@ -453,6 +453,9 @@ pub struct Config {
)]
pub holding_cleanup_interval_secs: u64,
// Retention is expressed in seconds for configuration consistency and short
// tests. Production deployments are expected to use periods of at least one
// day, comfortably exceeding worst-case deletion/archive processing time.
/// How long an unused deletion or vanish request remains served after relay-observed first_seen_at.
#[arg(
long = "deletion-request-retention-unused-served-secs",
+8
View File
@@ -447,6 +447,14 @@ impl DeletionPolicy {
return outcome;
}
// NostrDatabase::delete does not return a per-event removed count. Two
// concurrently processed requests can therefore both have observed one
// target and both receive use credit after successful delete calls. This
// narrow over-credit race is accepted: avoiding it would require broad
// target-level serialization, it cannot affect deletion correctness,
// and any extra credited request still follows bounded used retention.
// Deterministic single-winner attribution remains strict for the more
// important case of one later admission matching many pending requests.
let deleted_count = deletable.len();
let delete_filter = Filter::new().ids(deletable);
if let Err(e) = self.ctx.database.delete(delete_filter).await {