Or how to make a basic "social-networking" application using the Nostr protocol that is safe and promotes decentralization.
The basic app views
Suppose a basic "social-networking" app is like Twitter. In that, one has basically 3 views:
- A home feed that shows all notes from everybody you follow;
- A profile view from a specific user that shows all notes from that user;
- A replies view that shows all replies to one specific note.
Some Nostr clients may want to also provide another view, the global feed which shows posts from everybody.
A simple classification of relays
And suppose that all existing relays can be classified in 3 groups (according to one's subjective evaluation):
- spammy relays, in which people of any kind can post whatever they want with no filters at all;
- safe relays, in which there are some barriers to entry, like requiring a fee, or requiring some cumbersome user registration process, and spammers or people who post bad things are banned -- but this is still a relay fundamentally open to anyone (although this is also subjective depending on the kind of restrictions);
- closed relays, in which only certain kinds of people enter, for example, members of a group of friends or a closed online community.
How to follow and find posts from a given profile
To follow someone on Nostr, it is necessary to know one or more relays in which that person is publishing their notes, otherwise it is impossible to fetch anything from them.
When a user starts to follow someone, that may be done through 4 different ways:
- from seeing that person in the app
- using an
nprofile
URI - using a NIP-05 address
- using a bare pubkey ('npub`)
Situation 1 may happen when that person is seen in the replies of yours or someone else's post, on a global feed post, or from a note referenced or republished from them by someone else. When that happens, it is expected that the references (in e
and p
tags) contain relay URLs. We must them inject that information to tentatively associate that person with a relay URL at that first contact.
In situations 2 and 3 both the nprofile
and the NIP-05 addresses should contain a list of preferred relays for that person, so we can bootstrap the relay list for that person based on that.
In situation 4 there is no relay list, so we must either prompt the user through an annoying popup or something -- or it can try searching for that pubkey in one of their known relays. This remains an option for the other methods too.
Once we have relay URLs for a given profile we can use these relays to query notes from that pubkey. As time passes that user may migrate to other relays, or it may become known that the user is also posting to other relays. To make sure these things are discovered, we must pay attention to hints sent in tags of all events seen everywhere -- from anyone --, and also events of kind 2 and 3, and upgrade our local database that has the knowledge of relationship between profiles and relays accordingly.
Rendering the app views
From what we've gathered until now, we can easily render the home feed and the profile view. To do that it just uses local information about relationships between profiles and relays and fetch notes:
- for the home feed, from all people we're following;
- for the profile view, from just that specific profile.
Since we'll be asking for very specific data from these relays, we do not care about where they're safe or not. They will never send us spam (and if they do that will just be filtered out since it wouldn't match our strict filter).
Now whenever the user clicks on a note we will want to display the replies view. In this case we will just query only the safe and the closed relays, since otherwise spam might be injected into the application. The same principle applies to the global feed view.
Other heuristics and corner cases
There are probably many corner cases not covered in this document. This was meant to just describe one way that seems to me to be sufficiently robust for a decentralized Nostr.
For example, how to display a note that was referenced by someone? If it has a relay hint we query that relay. If it doesn't we can try the relays associated with the person who have just mentioned it, or the same relay we've just seen the note that mentioned it -- as, when mentioning it, one might have published it directly to their own relays -- and so on. But all this may fail and then it is probably not a big deal.
Final thoughts
More important than all, is that we must keep in mind that Nostr is just a very loose set of servers with basically no connection between them, there are no guarantees of anything, and the process of keeping connected to others and finding content must be addressed through many different hackish attempts. To write Nostr applications and to use Nostr one must embrace the inherent chaos.