nip-17: support seen events.#1761
Conversation
|
You have to specify exacly how the bloom filter MUST be coded in the client and how its byte array should be encoded. Take a look at #1497 as an example. |
|
@vitorpamplona thanks a lot. ill take a look. you can check my example code in link above as well. but ill try to add details same as what you suggest now. |
|
Yeah, the code is way too big. The encoding + choices on the filter like what is the hashing function being used should be crystal clear. Keep in mind that each language has a bloom filter, and they all work with different hashing functions and encoders. We can't rely on them alone to spec this. |
|
yes, i realized that after checking your example. can i use some stuff from your example? |
|
Sure! feel free to use anything you want. |
|
@vitorpamplona i think you can check it again. I've updated the bloom specs. 🫡 |
|
|
||
| ## Seen status | ||
|
|
||
| A client MAY publish a kind `30010` which means saw messages with a `"d"` tag set to receivers pubkey. |
There was a problem hiding this comment.
Major privacy issue here is that it exposes who you are talking to in the d-tag. Take a loot at the Private relationship status d-tag from here: #761
There was a problem hiding this comment.
Also, keep in mind that NIP-17 has support for groups. The D tab must support multiple pubkeys at the same time as well.
There was a problem hiding this comment.
what is your suggestion for privacy issue? can we gift wrap the seen list?
is there any id for groups? so we can use group ids in "d" tag instead of multiple pubkeys...
There was a problem hiding this comment.
#761 proposes: sha256(hkdf(private_key, salt: 'nip17') || "<pubkey>") as d-tag with hkdf defined in NIP-44.
For groups, we can just concatenate the sorted pubkeys in hex of all members, like sha256(hkdf(private_key, salt: 'nip17') || "<pubkey1> || <pubkey2> || <pubkey3>"). Logged in user can be excluded from the list.
There was a problem hiding this comment.
that's a great idea. for nip-29 we can do the same but with id. or a specific kind for it. but i can see people still talking about it...
There was a problem hiding this comment.
Or maybe do a new PR with the timestamp idea, so that this one stays live as well.
There was a problem hiding this comment.
no this pr don't care about taking missed events for sender. the goal is to be aware of which messages have been seen by other participant.
im ok with both id and timestamp idea.
There was a problem hiding this comment.
in this situation i know my friend seen my message.
in nostr my friend can put the timestamp of 23:52 on an event which is encrypted for me so i can know he saw any message until then and my client can show this check mark.
or just put the event id of this your welcome message there so i can know any message before this one including itself is saw by him.
There was a problem hiding this comment.
for groups i shows a list of people in a drop down. i don't have examples yet.
|
@kehiy definitely like the idea of read receipts or seen events. I agree it would improve UX. A few questions/thoughts on this: Why use the bloom filters and a replaceable event for this instead of just sending very simple read receipt events? It seems very overly complicated for something that could be very simple. Before coming across your PR here, I was thinking about this and my initial naïve thought was that we should use a simple event (1-9999) that can be encrypted to show that a specific user has read a specific a or e tagged event. However, my use case is within the White Noise client which uses MLS to encrypt data and events within an outer wrapper so I don't have to worry about leaking metadata on the read receipt itself because it will be embedded in another event. It's encrypted. This doesn't necessarily allow for clients to easily aggregate these read receipts, but I feel that since read receipts are mostly important over a short time window, it's not really necessary for clients to go way back into the past to find the read receipts and the exact time that things were read. |
|
@staab made an earlier attempt to log each seen event ID in a list. The main issue is that those lists become MASSIVE very quickly. Having individual events to mark as read makes the db even bigger. So, the bloom filter here, as a replaceable event, (clients discard the past) could be a big improvement. |
|
As an FYI, I will start using bloom filters for many things on Amethyst, including logging which events have been seen by the user across all screens and saving it on the user's Private Outbox relay. I might just have one big bloom filter for everything, including likes and zap events of each post. |
|
No I don't want a list of seen events, I don't even think we should care about whether these are stored on relays for the long term. TBH, I think putting a short expiration on these types of events would be fine. Reconstituting the read receipts for a chat conversation, especially one that is private, isn't important. |
Why wouldn't you just use a local database? |
Way too slow. |
|
And things like Tiktok feeds need to know which posts users have seen so that they can show only new content |
|
You might want to look into Cockoo filters for being more compact and allowing deletion of inserted elements. |
|
For anyone interested in the previous attempts, see #1405 The summary is that timestamp-based misses events that get discovered out of order that you might still want to notify your user of (which would imply tracking when an event was first seen in the client so you can promote it). The solution was enumerating ids. But that resulted in huge lists. So I tried combining it with timestamps, and keeping track of the last few ids. But that actually resulted in events that were in fact seen in other instances of the client to be marked as unread. So I went back to timestamps. I don't think bloom filters will really solve anything, since you can get false negatives, resulting in sticky events that can't be marked as read without enumeration or a timestamp. It's a weirdly hard problem. |
Wait, a Bloom filter can only produce false positives; it cannot have false negatives. It will never incorrectly say an element is not in the set when it actually is. This means that we can have items that are marked as seen but were never seen but not the opposite. Or am I misunderstanding something? |
|
i think the best way for read status (classifying: not in clients internal system to know what events is seen by user. for chat apps to show the seen status to other party. same as telegram example i sent) is sharing the timestamp of last event we seen from them. they consider all messages with created at <= to that timestamp seen and the rest as ubseen. what do you think? |
|
Use a low FPS CUCKOO filter. FPR of 10E-6 with up to 3k elements would be 11kB regardless of how full it is. |
|
@kehiy purely time-based is not great for two reasons:
|
My mistake, I always get this backwards. Bloom filters might be a good solution in that case. |
what if you use the id of last event saw by us? |
|
I think in similar ways we have to support: (Some apps support 3 message status: |
|
Consider that these statuses are not related to users general status like playing game, listening to music and ... These are chat specific statuses. One per each chat. |
|
I'll open final standard after reaching a point at #2002 |

a simple example that i made for testing: https://github.com/kehiy/-/blob/main/research/nostr-seen/main.go
this feature is used on most of chat apps like telegram and simplex chat. it helps the client to show the seen messages with a double check so the sender can be aware of this. its not trust less since we can receive the message and don't add it to the bloom filter. same thing can be done in telegram for example using unofficial versions.
it would deliver a better user experience.
i believe the most important difference between email or similar protocols to modern real-time chat protocol which makes then feel better and more live is seen/(online/offline)/typing statuses.