LIMITS command
Adds a LIMITS command to help relays define "NIP-11-like" limits for each connection. It is designed to be reactive and actionable by clients. These LIMITS can change at any time and MUST reflect the rights the connected user has into the relay.
The properties were designed to be easy for Clients to implement and filter which and when REQs and events can be sent to the relay.
Use cases:
- Unauthed connection has different limits than authed.
- Limits can be clarified based on location/IP, pubkey reputation, etc.
- Rate limits can get more limiting over time if abused
- Clear requirements for which kinds are acceptable and rejected based on the connection information.
- Relays that only accept posts in a given language or location can make that clear to clients.
Relays are encouraged to punish Clients that don't limit themselves by these properties by making even stricter limits as the connection ages.
Read here
Relays do have limits and put limits on clients, and this data expresses some of that. And it is in-protocol instead of the NIP-11 thing (which I hate).
But how are you actually going to use this data? Is Amethyst going to change how it packages up requests to fit within the request limit? That sounds complicated. Is it going to verify that events it wants to post pass the limits check before trying to post them? The benefit of that is just that it avoids a post-then-error, which I guess is better but not by a lot. I guess I'm saying that to take advantage of this data, clients will become a lot more complicated.
I don't see the use case for "required_tags".
But how are you actually going to use this data? Is Amethyst going to change how it packages up requests to fit within the request limit?
I have to. It's not very hard, though. The hard part is figuring out the limits for each relay. That's what this is trying to solve.
Rate limits for instance are a mess. If they tell me when to send, I can debounce on the client and skip a few more reqs when the user is scrolling.
required_tags work for relays that require geolocated, language tags, or personal relays that only take one author, one community or one chat header event, etc.
This PR adds two filters on the client, one when sending REQs and another when sending EVENT. If this data comes down from the connection, I can cache it locally in the Relay object and just have a match function that filters the REQ and the Event list to the desired approach by the relay.
I like this PR, I think it is good.
So this is off topic, but gossip doesn't seem to be having request limit issues. I'm wondering if I'm just not noticing or if you are making many more requests than gossip does. To be explicit, in the worst case we have 12 subscriptions open but that never actually happens (would need to be doing nip46 and everything else too). That includes seeking augmentations (likes, zap receipts, etc), loading more in a feed, loading future events in a feed, seeking a referred-to event, config updates, discovery (relay lists), dm channel messages and giftwraps, metadata loads.
So this is off topic, but gossip doesn't seem to be having request limit issues.
It comes and goes. Every week I find a new user that is having trouble accessing certain relays. I feel like if these restrictions were more core to the protocol (like this PR is trying to do), Clients and relays would pay more attention to setting them up. New codebases can always start without them and slowly add them as they start seeing the issues.
Also, I want to have a "Smart" Relay class that automatically merges subs and breaks filters down into many subs as well as chunks them if each filter becomes too big while accounting for EOSE re-calculations and since/until queries when the relay's limit is lower than what the filter wants. All that must react based on each relay's settings. Devs just code the base filter they need and the Relay class will figure out how to make it work based on the restrictions of each relay.
Instead of all that, we should standardize some NIP-01 messages like ~rate-limited: slow down there chief~ -> rate-limited: <seconds-to-wait-before-retrying> and ~pow: difficulty 26 is less than 30~ -> pow: <required-difficulty>
I don't think it is viable to send all relay restrictions in individual notices. There is just too many. It's better to send it all at once.
What I meant is that, considering that relay-client communication lacks a handshake step requirement, it is possibly better to receive more informative NOTICE/CLOSED msgs after client tries and fails an operation. Not all relays limits, but just info on what is needed to fulfill the previous failed operation.
Some of the current messages can't be acted upon, but could be made actionable as showed on my above examples.
This PR is trying to create the said handshake step, but clients can't wait for that before making a request cause it isn't a requirement. This PR is a few years late and now it won't work.
Just my opinion.
Not all relays limits
Maybe small clients don't see them, but Amethyst has hit the limits of every single relay I can think of. In fact, from my perspective, I haven't found a single relay in production that doesn't limit clients. Which is Ok, as long as the Client can adapt to the limits of the relay, things work.
clients can't wait for that before making a request cause it isn't a requirement.
It doesn't need to be a requirement. The PR is to improve comms for relays and clients that implement it. This should never be a requirement of the core protocol. It's something devs should learn after they make things available for the first time.
Hopefully, it makes sense for relays to offer this because it reduces the workload of the server from invalid calls from compliant clients. And it makes sense for Clients to implement it just to avoid pissing off relay operators everywhere and increasing limits even further.
Also, this PR is not a replacement for notices.
Connection limits also help relays create multiple plans that users can subscribe to. For instance, relays can offer a basic plan for kind 1 notes and a pro plan to store all drafts privately.
Or a basic plan where clients can only make 1 request per second and a pro plan with 50 reqs per second.
They just need to let the Client know when different plans are active by sending the limits payload.
This looks reasonable, but I'm curious about client side implementation. Since relays can send this at any point, would clients have to "guess" some default values before receiving anything? When you say relays are encouraged to punish clients which don't adhere to those limits, does that motivate clients to first wait for the first LIMITS message before publishing / subscribing first time?
I would consider making it mandatory for relays to send it right when a client connects.
Or are clients first expected to use data from NIP11 and then listen to changes from LIMITS messages? If so I think nip11 should be mentioned here.
Also, I don't think it's good practice to reuse NIPs. E.g. strfry says it supports NIP20, reusing this number will be confusing.
Since relays can send this at any point, would clients have to "guess" some default values before receiving anything?
Since all of these restrictions simply filter which and how messages go out, the default implementation simply doesn't do any filtering at all.
first wait for the first LIMITS message before publishing / subscribing first time?
They could, but I don't think they would. Clients will always try to push the boundaries until the limit comes up.
I would consider making it mandatory for relays to send it right when a client connects.
We can add that for those who opt to implement this feature.
Or are clients first expected to use data from NIP11 and then listen to changes from LIMITS messages?
NIP-11 is a mess. No client is using it beyond just displaying information to users.
I don't think it's good practice to reuse NIPs. E.g. strfry says it supports NIP20, reusing this number will be confusing.
makes sense, I will change it.
I would consider making it mandatory for relays to send it right when a client connects.
We can add that for those who opt to implement this feature.
I'm all for it
Or are clients first expected to use data from NIP11 and then listen to changes from LIMITS messages?
NIP-11 is a mess. No client is using it beyond just displaying information to users.
And getting the list of supported nips I assume? How else would relays advertise what they support?
I don't think it's good practice to reuse NIPs. E.g. strfry says it supports NIP20, reusing this number will be confusing.
makes sense, I will change it.
Strfry's readme says they support 22 too, lol (nostream too). Was it a nip which got deleted?
How else would relays advertise what they support?
I don't think that information is relevant for clients. It's too broad. I would prefer that relays break down by more basic limitations like not accepting Deletion events if they don't support deletion, for instance.
PS, I am also considering bloomfilers to help clients filter by any user or event:
accepted_authors: <bloomfilter in Base64>, Clients MUST filter publishing events by these authors.accepted_p_tags: <bloomfilter in Base64>, Clients MUST filter publishing events by these p_tags.accepted_e_tags: <bloomfilter in Base64>, Clients MUST filter publishing events by these e_tags.accepted_a_tags: <bloomfilter in Base64>, Clients MUST filter publishing events by these a_tags.blocked_authors: <bloomfilter in Base64>, Clients MUST remove events by these authors.blocked_p_tags: <bloomfilter in Base64>, Clients MUST remove events that have these p_tags.blocked_e_tags: <bloomfilter in Base64>, Clients MUST remove events that have these e_tags.blocked_a_tags: <bloomfilter in Base64>, Clients MUST remove events that have these a_tags.blocked_words: <bloomfilter in Base64>, Clients MUST remove events that include these words in.content.
But those are going to be for a later date after we define how bloomfilters should be represented.
I like this NIP proposal as it seems like it could offer a better interface between relays and clients which in turn could improve client UX if implemented properly. I haven't implemented my own relay or social client yet so I don't know how hard/complex it would be to implement and handle this in relay/client software? That said, as long as it's optional it's not really an issue.
Is this NIP implemented anywhere yet?
fyi, NIP-22 was previously a NIP to define a relays created_at lower/upper limits but was later "moved" to NIP-11 in #897.
@vitorpamplona To clarify for accepted/blocked kinds, is this intended to be used as an inclusion/exclusion pattern whereby if accepted_kinds is set OR has a length, then implicitly blocked_kinds can be ignored?
And inversely, if accepted_kinds is NOT set or has no length, then implicitly the relay is signaling it accepts all kinds other than those that may or may not be set in blocked_kinds?
Yes, I would code it like this:
if (accepted_kinds.length > 0 && !accepted_kinds.contains(newEvent.kind)) {
return // don't send it.
}
if (blocked_kinds.length > 0 && blocked_kinds.contains(newEvent.kind)) {
return // don't send it.
}
sendEvent(newEvent)
Excellent. It might be beneficial to note this in the NIP, namely that accepted_kinds takes precedence over blocked_kinds. That way, in the event both are set (this is nostr after all), the behaviors are more likely to be consistent across clients.
PS, I am also considering bloomfilers to help clients filter by any user or event:
* `accepted_authors: <bloomfilter in Base64>`, Clients MUST filter publishing events by these authors. * `accepted_p_tags: <bloomfilter in Base64>`, Clients MUST filter publishing events by these p_tags. * `accepted_e_tags: <bloomfilter in Base64>`, Clients MUST filter publishing events by these e_tags. * `accepted_a_tags: <bloomfilter in Base64>`, Clients MUST filter publishing events by these a_tags. * `blocked_authors: <bloomfilter in Base64>`, Clients MUST remove events by these authors. * `blocked_p_tags: <bloomfilter in Base64>`, Clients MUST remove events that have these p_tags. * `blocked_e_tags: <bloomfilter in Base64>`, Clients MUST remove events that have these e_tags. * `blocked_a_tags: <bloomfilter in Base64>`, Clients MUST remove events that have these a_tags. * `blocked_words: <bloomfilter in Base64>`, Clients MUST remove events that include these words in `.content`.But those are going to be for a later date after we define how bloomfilters should be represented.
Bloom filters have false positives
Yep, it's the relay's job to figure out if the precision of bloom filter is good enough for them or not, in each use case. But if it comes down to the Client, it will be taken into account.
I'm on board with this, but I have one request, can we settle on a single format that works for both NIP 11 and LIMITS? LIMITS should be treated as a refinement of the general purpose limits detailed in NIP 11 based on the connection/IP/auth'd user, not as a totally different thing. The stuff in limits is useful in nip 11, the stuff in nip 11 is useful here. I'd be ok with deprecating supported_nips on nip 11 and adding a limits key there that contains this data format.
I wonder how created_at_(upper|lower)_limit should be treated in case of Private Direct Messages (NIP-17), these create created_at timestamps likely outside the limit. For now I would propose to ignore this limit for regular event kinds, but I am unsure of all the implications of that decision.
If the relay is blocking events outside the date range, ignoring it will just necessarily use the user's data plan. I would simply report back to the user that a given DM he/she just sent cannot be saved on the target relay.
ignoring it will just necessarily use the user's data plan
What do you mean by this, @vitorpamplona ?
What do you mean by this, @vitorpamplona ?
If the relay ignores events older than today, if you send a DM where the GiftWrap has yesterday's date, the relay will reject it. You just wasted data plan.
I was thinking of a new relay-to-client message, then bumped into this spec again. I think this spec has two major flaws:
- It isn't part of NIP-01, i.e. with your spec clients wouldn't be able to wait for the relay msg before taking action (sending their own msgs or deciding to disconnect).
- Too many fields that aren't needed. Things like accepted tags or number of them, accepted authors. If you use reasonable number of tags, specially the one-letter ones relays won't block events. Accepted authors is redundant because following NIP-65 or other discovery ones will let you know who has access to which relays.
My alternative PR: #1969
I think you meant to reply to https://github.com/nostr-protocol/nips/pull/1924 and not this one.
Limit definitions that can change during the connection time and feature definitions that just expose which things are available are very different to each other in my mind.