An editable event is unique by a combination of its .kind
, .pubkey
,
and one or more specific tags.
Therefore, given two events with the same combination of such fields,
the one with the most recent .created_at
is kept while the other is discarded.
If the timestamps are the same, the event with the lowest id (first in lexical order)
should be retained, and the other discarded; unless there is a dd
tag set to a value that contains the @
char, in which case, before comparing ids,
the one with the highest updated_at
tag value (unix timestamp in seconds)
converted to integer should be kept.
When a d
(deduplication) tag is the first event tag, the event is editable by its .kind
, .pubkey
and the first d
tag value.
The d
tag is set as follows: ["d", "<string>"]
.
If set to an empty string, the event in practice is treated as NIP-01 replaceable events, else as addressable (aka parameterized replaceable event).
This NIP section simply expands NIP-01 replaceable/addressable event usage to any event kind, no matter if outside the 10000 <= n < 20000
and 30000 <= n < 40000
kind ranges.
These events are referenced by NIP-01 a
tags instead of e
tags.
When a dd
(multiple deduplication) tag is the first event tag, the event is editable by its .kind
, .pubkey
and the first
occurrence of each tag listed on the dd
tag value.
The dd
tag is set as follows: ["dd", "<one-letter tag name list, order doesn't matter>"]
.
The tag list can't repeat letters. Each listed tag should appear only once as an event tag.
The list may include an @
char that references the .created_at
value instead of an one-letter tag.
- Examples with a single
e
tag:["dd", "e"]
- Example with three tags:
["dd", "eap"]
. - Example for an event unique by its
.created_at
field ande
tag:["dd", "@e"]
.
Note: d
tags take precedence over dd
tags, but d
and dd
tags shouldn't be used together.
{
"kind": 99,
"pubkey", "827ade3254c72ae811201b418766d9a8802e91b029d95cc9de2f0169274aedd7",
"tags": [
// relays and clients will check for other events with j tag = "12", z tag = "zz",
// kind = 99 and same .pubkey, before choosing this event as the latest version
["dd", "jz"],
["j", "12"],
["c", "abc"],
["z", "zz"],
["status", "ok"]
],
// ...other fields
}
A u
(unique id) tag is used (instead of e
) for referencing the latest version of an event that has a dd
tag.
It has the following value format:
"<event kind>:<event pubkey>:<tag name><tag value>
" or
"<event kind>:<event pubkey>:<tag name><tag value><delimiter><tag name><tag value><delimiter>...
"
The delimiter between tag name/value pairs is the tilde ("~"). If a tag value used on
a u
tag contains a tilde, the "~" char should be escaped with a preceding dash "-".
Referencing three editable (by dd
tag) events:
{
"kind": 100,
"pubkey", "38cdb0e540bc08beb647c7d786c2a3efa9348f9dbbfa66b55959f150310ced1a",
"tags": [
["u", "99:827ade3254c72ae811201b418766d9a8802e91b029d95cc9de2f0169274aedd7:j12~zzz"], // j tag = "12" and z tag = "zz"
["u", "102:827ade3254c72ae811201b418766d9a8802e91b029d95cc9de2f0169274aedd7:@1729383776"], // created_at field = 1729383776
// example of escaping the ~ char
["u", "1009:0b8e7a1a4bdba24c911d10e9fc12417d420807488510dbd4fcd8b43e9a66ad74:c1-~3~d4-~6"] // c tag = "1~3" and d tag = "4~6"
],
// ...other fields
}
To avoid relying on relay support, clients should:
A) When possible, filter specific events with limit: 1
, or else, filter out stale events client-side.
Example using the above event's first u
tag value: ["REQ", 'sub', { "limit": 1, "kinds": [99], "authors": ["827ade32..."], "#j": ["12"], "#z": ["zz"] }]
B) Delete old event versions after saving a new one.
That is, fetch old event versions' ids, save the new event, then send a NIP-09 deletion request with those ids.