nips
nips copied to clipboard
Protocol initial hanshake
The vast majority of peer to peer communication protocols have some form of initial handshake. It allows both sides of the conversation to identify each other and exchange information about the features and services offered.
The NOSTR protocol does not have this, and I personally consider it a huge mistake. In normal society, when I come to visit someone, I introduce myself to the person who opened the door for me. And he also introduces himself and lets me go. In NOSTR's case, I barge in and immediately start asking a bunch of questions... and while the owner tries to cut me off and "wait wait who are you, how about introducing yourself?"
Relay has no idea who it has the honor of communicating with and how it should treat the visitor in order to avoid misunderstandings. And the client would probably want to know the same. What if the relay can't do what it wants? What if the client can't do what the relay requires?
There is a NIP-11 document, but that is a completely useless feature. It's almost like in "The Hitchhiker's Guide to the Galaxy" where important information is on a bulletin board in the basement. No one reads it, at least not the clients I tried.
Therefore, I propose to introduce an initial handshake in the form of two new commands.
client: ["HELLO",{... client capabilities...}]
relay: ["WELCOME", {... relay capabilities...}]
The client capabilities should contain at least "user_agent":"name" and "supported_nips"
The relay capabilities can be in the same format NIP-11.
When this exchange is done, communication continues as usual.
If the relay requires mandatory authentication, then the handshake will look like this
client: ["HELLO",{... client capabilities...}]
relay: ["AUTH","<NIP-42>"]
client:["AUTH":<event>"]
relay: ["WELCOME", {... elay capabilities...}]
Relay does not need to provide services until the client completes the entire authentication process. The client must not send any other commands until it receives a WELCOME from the relay. If both parties do not agree, both parties must terminate the connection.
In terms of backwards compatibility: Relay will probably have to provide services to clients. who will not use this protocol. But they are likely to be seen as less trustworthy.
I'm interested in other people's opinions. I'm willing to put it together as a new NIP
Why? There is a handshake when the web socket is established. Auth challenges are covered by NIP-42 and capabilities by NIP-11, as you've alluded to. Already, a relay does not need to provide any services until it's requirements have been met. A client does not need to advertise it's capabilities, only respond to challenges and conform to the relays expectations. What do you want to do that cannot be achieved with what is already spec'd?
NIP-11 nobody reads. NIP-42 doesn't cover capabilities Web clients has limited ways to add extra informations into websocket handshake.
What about statistics. How I determine which clients are connected to my relay? Which nips they are supporting. Why a user complains about poor service with an outdated client?
Let say, you have this communication
[5] New connection
[5] LOG: <0> Connected client: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
[5] LOG: <0> Received: ["REQ","user:68c14d1993aa7053d17023fc577d334618b05900",{"kinds":[0,3,7,6,4],"authors":["68c14d1993aa7053d17023fc577d334618b05900f6c8a1d8419726b38bcbd8bf"],"limit":0}]
[5] LOG: <0> Send: ["AUTH",";uDg9gIp=wM-%.|"]
[5] LOG: <0> Received: ["REQ","notifications:68c14d1993aa7053d17023fc577d334618b05900",{"kinds":[1,7,6,4],"#p":["68c14d1993aa7053d17023fc577d334618b05900f6c8a1d8419726b38bcbd8bf"],"limit":400}]
[5] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[5] LOG: <0> Received: ["REQ","feed:following",{"kinds":[1],"authors":["0000000000000000000000000000000000000000000000000000000000000000"],"limit":50}]
[5] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[5] LOG: <0> Received: ["REQ","profile",{"kinds":[0],"authors":["68c14d1993aa7053d17023fc577d334618b05900f6c8a1d8419726b38bcbd8bf"]}]
[5] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[5] LOG: <0> Received: ["REQ","contact",{"kinds":[3],"authors":["68c14d1993aa7053d17023fc577d334618b05900f6c8a1d8419726b38bcbd8bf"]}]
[5] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
You are owner of relay. Tell me, where is the problem? This is NIP-11 of this relay
{
"contact": "[email protected]",
"description": "Novacisko nostr server",
"limitation": {
"auth_required": true
},
"name": "novacisko",
"pubkey": "npub1skfmmcet2244yx8xn5xjfcmwg3u4mcjdndpd2gcjkq4qfycm6xtql0xa7d",
"software": "git+https://github.com/ondra-novak/nostr_server.git",
"supported_nips": [ 1, 9,11,12,16,20,33, 42, 45, 50 ],
"version": "0.0.1-28-g54ee210"
}
The client has not responded to the AUTH challenge but proceeds to make requests.
Other example
[8] New connection
[8] LOG: <0> Connected client: Dart/3.0 (dart:io)
[8] LOG: <0> Received: ["REQ","e0785033c1",{"kinds":[1984],"#e":["6256567638ab374c76a666082d596b0b6dad0c4ddc13f30a26810a4e7435e344"],"limit":3}]
[8] LOG: <0> Send: ["AUTH","P@lcNc8~NhUnjr:"]
[8] LOG: <0> Received: ["REQ","ad472d2e36",{"kinds":[1984],"#e":["594522e7388b199dc1fe6ea7e799fb327c344bca37fc4d925a81abde9b282ab6"],"limit":3}]
[8] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[8] LOG: <0> Received: ["REQ","bb86a13edb",{"kinds":[1984],"#e":["2c25fc24acf4ebb007fc4e1bffa99117cce5039b1d9d47754bf49013c7586ea7"],"limit":3}]
[8] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[8] LOG: <0> Received: ["REQ","79bc131a2d",{"kinds":[6,7,9735],"#e":["6256567638ab374c76a666082d596b0b6dad0c4ddc13f30a26810a4e7435e344"]}]
[8] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[8] LOG: <0> Received: ["REQ","4996c86ac4",{"kinds":[6,7,9735],"#e":["b4aa3c1e05e94dd36b0315b101756c5a8e782ad5671e06c669ff58390dd1ece9"]}]
[8] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[8] LOG: <0> Received: ["REQ","5a64e55daa",{"kinds":[6,7,9735],"#e":["bed0bd3c44afdcf4c027cc63e148d49b6bba65f98138c5afbb95e6d8cb1f1ae5"]}]
[8] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[8] LOG: <0> Received: ["REQ","e4a82210e6",{"kinds":[6,7,9735],"#e":["594522e7388b199dc1fe6ea7e799fb327c344bca37fc4d925a81abde9b282ab6"]}]
[8] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[8] LOG: <0> Received: ["REQ","b6e1ade9a1",{"kinds":[1],"#p":["8593bde32b52ab5218e69d0d24e36e44795de24d9b42d52312b02a04931bd196"],"limit":100}]
[8] LOG: <0> Send: ["NOTICE","restricted: Authentication is mandatory on this relay"]
[8] closed. Read: 1 KiB / Write: 1 KiB
Can you determine which client is it? I can't. Apparently the client is unable to access my relay.
Actually I know this client, it is Plebstr for Android. But I as owner of the relay have no idea how to contact developers of this client. The client doesn't share any informations.
The client has not responded to the AUTH challenge but proceeds to make requests.
Can I contact the developer? How can I find out, who is developer?
Actually this is Hamstr.to, because I am using it for testing.
Why do you need to contact the developer? Maybe they only want to support free relays. They have all the information they need to support the relay if they choose to.
Maybe they only want to support free relays
please explain this to me in more detail
I assume the principle that clients should share as little information as possible in order to interact with relays. I do not want relays collecting analytics that are superfluous to functioning of the protocol.
Clients are free to interact with the network per the specification, however they choose to. NIP-42 is optional. Why as a client developer would I want to receive emails from some random relay operator telling me to change my client? The relay already communicates it's requirements for a client to act on. And is free to drop connections from clients that do not respect it's requirements.
I think a case could be made to make a subset of NIP-11 mandatory. But I also think NOTICE messages are all that is strictly required.
I assume the principle that clients should share as little information as possible in order to interact with relays. I do not want relays collecting analytics that are superfluous to functioning of the protocol.
The network status is curently "unusable", resulting in a very poor user experience. A lot of people talk about it but gradually stop when they try it. It's just a terrible shame. What's the point of all the nips if they're not even followed. And when protocols are designed by people who have zero experience with it
Why am I even trying?
The network status is curently "unusable", resulting in a very poor user experience. A lot of people talk about it but gradually stop when they try it. It's just a terrible shame. What's the point of all the nips if they're not even followed. And when protocols are designed by people who have zero experience with it
Why am I even trying?
I don't know. I'm only one person. Others who are have more experience may agree with you, let's see.
NIP-11 nobody reads.
Lol that's not an excuse. And if there's a nip for "protocol handshake" I guarantee you nobody will implement that either. Just admit you didn't know about NIP-11.
Lol that's not an excuse. And if there's a nip for "protocol handshake" I guarantee you nobody will implement that either. Just admit you didn't know about NIP-11.
What? I meant that none of the clients bother to visit that endpoint and retrieve the contents of that JSON document.
Implemented here: https://github.com/ondra-novak/nostr_server
I would have done something like this if I had come to nostr early enough to change this aspect of it. I would have also made it binary. In fact, in a fit of rage (or something like it) I developed "nostr-odyssey", a very nostr-like protocol, and then when my sanity returned I buried it. It specified a websocket-subprotocol header (so the client can be sure the server isn't just websockets, but specifically is talking in nostr-odyssey) and it started with handshake messages where both sides negotiated the set of NIPs they have in common as well as AUTH.
But nostr is widely deployed. I wouldn't dain to try to rewrite the protocol so significantly at this point.
When you send your "HELLO" to a relay that doesn't support this, will you wait forever for the "WELCOME" message that the relay never sends? Or will you check NIP-11 first, and if so, then what is the point? Maybe you plan to only talk to relays that also support this and never to the rest of us.
I agree with @ondra-novak that protocol handshakes are generally useful. I have built several protocols before that did not have handshakes and later on had to add them.
NIP-11 for detecting relay capabilities is not good. In fact there is a race condition here: What if the relay is upgraded in between when the NIP-11 request is performed and when the websocket connection is created? The capabilities could've changed in between. Or how about relays behind a load balancer? Suppose the NIP-11 request gets routed to a different server than the websocket connection. These servers could have different capabilities.
My point is: We have a stateful websocket connection. Why are we doing out-of-band capability negotiation to determine this connection's capabilities?
(Needless to say, a handshake would also help with the NIP-42 AUTH flow.)
I was just about to echo @mikedilger that this would be too painful to do in a backwards compatible way, but there is actually a (slightly hacky) path for this. I checked nostr-rs-relay, nostream, and strfry and they all reply to an unknown command with a NOTICE (I guess most other impls also). So the client flow would be to send HELLO and wait for either WELCOME or a NOTICE. If it got a NOTICE it would know to not assume anything about capabilities, or fallback to a NIP-11 query.
If there are any real issues with NIP-11, I think they can be / should be addressed without sharing client information.
If the relay requires mandatory authentication, then the handshake will look like this client: ["HELLO",{... client capabilities...}] relay: ["AUTH","<NIP-42>"] client:["AUTH":
"] relay: ["WELCOME", {... elay capabilities...}] Relay does not need to provide services until the client completes the entire authentication process.
It would be great but I think this ship has sailed as @mikedilger pointed out. I'm not the one who decides but I would guess there is 0% chance of this becoming mandatory (NIP-01). And if it isn't mandatory, it won't work.
Also, NIP-42 has a "flaw" because "At any moment the relay may send an AUTH message to the client". "At any moment" means client will do as your example sending [8] LOG: <0> Received: ["REQ","e0785033c1",{"kinds":[1984],"#e":["6256567638ab374c76a666082d596b0b6dad0c4ddc13f30a26810a4e7435e344"],"limit":3}] at connection start instead of waiting for AUTH, because even if NIP-11 says it requires auth, client won't know if it is just for DMs, for instance. Also NIP-11 isn't mandatory for relays implementing NIP-42.
NIP-43 - Fast Authentication atleast would fix this. Client is expected to send an auth event on connection start embedded in the url (query string). Clients can always send it even if relay doesn't need auth or check NIP-11 first to know if it is required.
My implementation of relay always sends AUTH as a very first message, but it is always optional. Without response the relay can continue to server but some features can be blocked.
As a shortcut, we can define new tags into AUTH response which might be sufficient for this purpose
["AUTH",{
"id":...,
"pubkey":...,
"tags":[
["user_agent","name of client"],
["supported_nips","0,1,2,3..."],
// other fields
],
//other fields
}]
This is a less controversial proposal than the original idea
@ondra-novak From the discussion above it's clear that a valuable problem exists. +1 on a NIP.
I came here because there's some messy json out there and though this is a little different than what you're talking about I was surprised to find no indication as to who's creating it.
I also think this would be very useful, it's a shame that it got no traction.
On a related note, I think that disregarding improvements to the protocol because they are not backwards compatible is a mistake and could back nostr into a corner where it can't evolve or fix past decisions.