Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIP-88: Notify request spec for relays #901

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

vitorpamplona
Copy link
Collaborator

@vitorpamplona vitorpamplona commented Nov 27, 2023

Paid relays often run into friction when renewing subscriptions or when upselling users to the next plan they have available. This PR allows relays to request payments on an as-needed basis.

This has been implemented on inbox.nostr.wine and Amethyst.

Read: https://github.com/vitorpamplona/nips/blob/pay-spec/88.md

@fiatjaf
Copy link
Member

fiatjaf commented Nov 27, 2023

I don't like this idea, I think it is too short-sighted. Ties Nostr too much into Bitcoin and Lightning. It's cool for its limited use case, but I don't see it gaining much adoption, even if I liked it.

Another concern I have is: will you throw this popup up for every relay that sends a PAY message? Or is it just for relays that you have added to your "inbox" list? Sounds too limited at first glance.

I feel like a NOTICE with a clickable link that leads the user into a webpage for paying (or even a clickable lightning:... invoice inside the NOTICE text) would work better and be simpler and more flexible and easier for everybody.

Or, if you want a very custom experience, do a normal REQ with the relay's own pubkey only for the specified relays you know you're paying and whenever you get an event from that relay pubkey you display it in a custom way?

@vitorpamplona
Copy link
Collaborator Author

Another concern I have is: will you throw this popup up for every relay that sends a PAY message?

Every different PAY message, yes. This works for all types of relays, even public ones that just want donations. Any payment request any relay wants to send will be displayed. If they annoy the user, it's their customer to loose.

Or, if you want a very custom experience, do a normal REQ with the relay's own pubkey only for the specified relays you know you're paying and whenever you get an event from that relay pubkey you display it in a custom way?

We don't know what types of access relays will be requesting payments for. So, it's better for them to send us a note when we ask for something that is behind a paywall.

I feel like a NOTICE with a clickable link that leads the user into a webpage for paying (or even a clickable lightning:... invoice inside the NOTICE text) would work better and be simpler and more flexible and easier for everybody.

We could use NOTICE as well, but we will need to know which ones are DEBUG messages and which NOTICES are to be displayed to the user immediately. Today, the only use of NOTICE is for debugging. A popup for each NOTICE is too much.

88.md Outdated Show resolved Hide resolved
Copy link
Member

@staab staab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been wanting to see this for a long time, but now that it comes to it I think I agree with fiatjaf that we should start with a URL rather than an invoice (although NOTICE seems like a bad idea). That way we're not tying ourselves to lightning, but we can also handle other use cases like subscriptions, terms of service, and anything else a relay cares to pack into the page its url points to. The key pain point this solves is flagging within clients that a rely requires admission, which doesn't work at all currently, not facilitating payment, since invoices have to be relayed to wallet software anyway.

88.md Outdated
]
```

Supporting clients SHOULD display a popup or notification to describe the action needed and collect immediate response from the user: `pay` and keep using the relay, `dismiss` and stop using the relay, or `more information` by navigating the user to the URL. Clients SHOULD expect that the same request might be sent multiple times, if the user dismisses or pays the amount, the client SHOULD ignore following requests with the same invoice.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave out the first sentence here. Clients SHOULD handle this in a way that makes sense. A pop-up might not be it.

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Nov 27, 2023

I think I agree with fiatjaf that we should start with a URL rather than an invoice (although NOTICE seems like a bad idea). That way we're not tying ourselves to lightning, but we can also handle other use cases like subscriptions, terms of service, and anything else a relay cares to pack into the page its url points to.

URL alone does add friction though. My goal was to get going as soon as possible. On-click, NWC payment if possible. The user never leaves the app. Every extra click matters.

Also, the URL is there, but it is the exception: "pay this to continue now or look at this URL for terms + other options".

@staab
Copy link
Member

staab commented Nov 27, 2023

What if clients just "open" the url and custom protocol handlers can take over from there? So https: would go to a browser, nostr: would go to the user's nostr app, invoice: (or whatever that prefix would be) would open a wallet. Clients can then special case those custom protocols if they want to handle them explicitly.

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Nov 27, 2023

What if clients just "open" the url and custom protocol handlers can take over from there?

We can do lightning:lnbc... I am not sure if the generalization is useful.

Maybe we can make the invoice optional and the URL the message's identifier?

@staab
Copy link
Member

staab commented Nov 27, 2023

Maybe we can make the invoice optional and the URL the message's identifier?

I would be fine with this. To justify the generalization though, it would allow relays to control the UX. If all they want is a payment and want to support a minimal-clicks UX, they can send an invoice and clients can use NWC to pay it (or redirect to a wallet if NWC isn't supported). If they want to offer other payment methods, ask users to create an account or accept a ToS, or provide additional tools for relay configuration or whatever, they can send a vanilla url to a page they own. They could even redirect users to a note using a nostr: prefix that explains how to gain access to the relay. This makes this proposal simpler, and allows for relays to have pretty unlimited flexibility and control over the UX.

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Nov 27, 2023

Well, if that is the case, then we can just have a URL and a Kind1-renderable description. So, If there is a lightning invoice in it, it will be shown. If there is a cashu token as a refund, it will be shown as well. Any other links would be clickable. Images can also be present.

Then the description should contain the entire text, including what to do if you decide to not continue to pay for the subscription (e.g. removing the relay from the relay list).

Then relays can also use it to show a... post of the day, for instance. It is not a PAY-only instruction anymore.

We can do a INTERRUPT_USER spec instead of PAY only

@staab
Copy link
Member

staab commented Nov 27, 2023

I can't tell if you're being sarcastic, but yes haha that sounds fine. Although I would again not call it interrupt_user, since it's not necessary to make it an interruption. Clients could aggregate these in the background and create a digest, automatically handle them, whatever.

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Nov 27, 2023

Sorry. No, I wasn't sarcastic :)

The issue is that the message is related to whatever the user is trying to use at that moment. For instance, if the user is trying to access Chats and this relay is paid for Chats, the relay won't return anything until the user Pays and the screen will be empty. So, it makes sense to have an immediate interruption of the flow in order to continue the UX the user wants.

Putting my sales hat on, an immediate response is a big deal. If these messages just go to notifications then we don't need a spec for it. The relay can just send a Nostr event that will show up in Notifications anyway. The popup at the right time gives the reason to buy. If the user waits, he might not understand why that payment message is even there.

@staab
Copy link
Member

staab commented Nov 27, 2023

Interesting point. Why do we need to add this to the spec then? A relay could send a NIP 24 DM from the relay's pubkey specifying payment details (or we could just create a new kind that represents a general-purpose payment request). This is how it's already done in fact. All the UX stuff you're talking about could be supported by clients pretty easily by identifying pubkeys that belong to relays. Latency isn't a problem, because the relay can send the DM using the connection that's already open.

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Nov 27, 2023

Sure, as long as the new event kind has the current filter/subscription in it so the app knows which request (or post) triggered it, we can do it as an event kind as well.

I don't think we have an official pubkey per relay, do we? We have the owner's key, but not a key that the relay could use to send new notifications anywhere.

Clients will have to add a new filter to listen to those events from special keys for each relay being used, but sure.

I think this is just simpler.

@vitorpamplona vitorpamplona changed the title NIP-88: Pay request spec for relays NIP-88: Notify request spec for relays Nov 28, 2023
@vitorpamplona
Copy link
Collaborator Author

Ok, I just changed this to a more generic messaging spec and removed the direct dependency on bitcoin invoices.

Let me know what you all think.

@nostr-wine
Copy link
Contributor

We've been working with Vitor on this and we are happy with the latest version of using NOTIFY instead of PAY and letting the relay decide what messaging to use.

I think it is important that clients always show the ws url that sent the NOTIFY when it is displayed, particularly if it includes an invoice.

@jb55
Copy link
Contributor

jb55 commented Nov 28, 2023 via email

@nostr-wine
Copy link
Contributor

inbox.nostr.wine has been updated with the new spec for testing. Here is an example:

Connected to wss://inbox.nostr.wine.
> ["REQ","cn",{"limit":2,"kinds":[4], "#p": ["7088717b932504f20b337bf3f9aea0b76516e4f3e1e504eadb8bd2dfa962b103"]}]
< ["AUTH", "e677f109-a033-4880-8bf9-291d86c456f7"]
> ["AUTH",{"id": "ca0b6b7c68b5d86409dbf897fb874e3fe1c63e0690d5ff355ea05677d88d5150","pubkey": "7088717b932504f20b337bf3f9aea0b76516e4f3e1e504eadb8bd2dfa962b103","created_at": 1701195326,"kind": 22242,"tags": [["relay","wss://inbox.nostr.wine"],["challenge","e677f109-a033-4880-8bf9-291d86c456f7"]],"content": "","sig": "e3a5bc45c322a773621bf7f3c43eca85e21b6f76e8ba4af0017c01e37b38a0ef334033982c564a83449904db2397c2740df917737e40bcb78f1dcb0971ba8156"}]
< ["OK", "ca0b6b7c68b5d86409dbf897fb874e3fe1c63e0690d5ff355ea05677d88d5150", false, "error: user unauthorized"]
< ["NOTIFY", "This relay requires an active subscription. You can purchase access for 30 days by paying the invoice below.\nlnbc50u1pjkvtj8sp570z6jc6zpdn522s4lmjw7s3clapad70ds6cswewf6v8ha2xsws5qpp5gz4pjr8h2fh2uyyzyfg7xslfzh6aa56zg85ws9h6z84k27qy7j2qdpdd9hxymmc9ehx7um5wgh8w6twv5sy6mmww35xc7fqgejk2xqy6thlcqpjrzjqf9l4ux2helcwn7n86lhcm6w2wzew879qnhn7jfyxt578mrhux6u7rzgqyqqtwqqqqqqqqqqqqqqtkcqjq9qxpqysgqqzdjhk3ft2pa0pxg87lu7dqk3pxfrrgtnqlpu7tqamyayvhz3dunn6reersjg9u08w2hqj8vqlxaapat0tdecyzjm24mdzlfvr86ymcpma3762\nBy paying this invoice, you are accepting our terms and conditions.\nLearn more and see additional subscription options on https://inbox.nostr.wine/\nIf you do not intend to use this relay anymore, please remove it from your relay list."]

vitorpamplona added a commit to vitorpamplona/amethyst that referenced this pull request Nov 29, 2023
@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Nov 29, 2023

New NOTIFY request has been released on Amethyst (0.81.2) and is live on both nostr.wine and inbox.nostr.wine

@Yonle
Copy link

Yonle commented Nov 30, 2023

SATA ANDAGI
Screenshot_20231130-230510

@vitorpamplona
Copy link
Collaborator Author

So many ideas with this design :)

@Yonle
Copy link

Yonle commented Nov 30, 2023

So many ideas with this design :)

Including being the "Message of the day".
Screenshot_20231201-005028

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Nov 30, 2023

I would reserve these notifications for more immediate cases, like pay now to get access to the data you are trying to search for in this screen.

Everything else could be a simple new Nostr event that will appear in the notification bar like everything else.

Copy link
Collaborator

@Semisol Semisol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too inflexible. A notice to users about relay maintenance (which should be shown as a notification), a user needing to pay for access (which should be presented when they add the relay), a user needing to renew their subscription (which should be presented immediately), and other information all get bunched into one message.

Also, invoices should not be created unless the client needs it. I don't get why you are trying to do "server pushes invoices even if the client doesn't need it" for anything related to relay payments, but that one extra round trip is worth the load you are saving on relays.

@vitorpamplona
Copy link
Collaborator Author

Too inflexible. A notice to users about relay maintenance (which should be shown as a notification), a user needing to pay for access (which should be presented when they add the relay), a user needing to renew their subscription (which should be presented immediately), and other information all get bunched into one message.

If you want to send notifications, send notifications via regular Nostr events or DMs. Those things already exist. This spec is not for that. This is explicitly for actions that need to interrupt or capture the user in the moment.

Also, invoices should not be created unless the client needs it

Feel free to create a new encoding to be used inside a Kind 1 that would tell Clients to ping the server, create and pay an invoice for a given amount. Then you can also give people multiple options right in the message.

@Semisol
Copy link
Collaborator

Semisol commented Dec 3, 2023

If you want to send notifications, send notifications via regular Nostr events or DMs.

Not every client implements DMs.

We are going to repeat the same mistake as NIP-50. Someone comes along (nostr.band or nostr.wine), wants to standardize something (search or relay notifications), but wants to avoid changing their implementation (single query for search or sending a notification with the invoice) at the cost of restricting the use cases or resulting in a worse experience for anyone that doesn't want to follow that strict model (search being limited in terms of autocomplete and sorting and specifying params or notifications having varying degrees of importance or multiple choices or the client requesting invoices from the server)

@nostr-wine
Copy link
Contributor

As usual, you're misinformed and not helpful. We created an implementation for this NIP from scratch so I'm not sure what you are talking about. We certainly aren't forcing this spec to fit something that already exists on our end. Our #1 priority is client interoperability across anything we deploy.

We can and will update our integration to fit whatever standard is ultimately agreed upon. We actually spend our time developing our relay not just yelling at the sky. If you don't like the NIP as proposed why don't you suggest some actual changes instead of drawing false conclusions about NIP-50 which has nothing to do with this proposal.

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Dec 3, 2023

@Semisol feel free to propose an alternative that is actually implemented in your relays. I am happy to code and test if it's better than this.

As you know, I have been asking for something like this for 5 months already. You ended up never proposing anything. @nostr-wine not only proposed, but implemented, shipped and it's working really well.

Same for NIP-50, you have not proposed anything workable yet.

@monlovesmango
Copy link
Member

monlovesmango commented Dec 7, 2023

agree this workflow is pretty slick but it makes me concerned about the user experience for non paying users. most paid relays will likely still allow some level of querying kind 1 events, and clients who query a paid relay on an adhoc basis (based on their follows, or a follows set list, or relay hint) will get bombarded with these notifications from relays they don't intend to pay for.

long term I don't see the typical user paying for more than 5 relays, but I do anticipate clients querying much more than 5 relays. so imo this has the potential to do more harm than good and will lead to pop up hell. I realize that clients can just choose to opt out, or opt out for all but a user's listed relays (which assumes all the relays a user pays for are used in their relay list), but generally we shouldn't create new message types that could largely be ignored.

I think ephemeral events would be much better suited for this use case (this way relays don't have to store notification events for every user and can create them on the fly with any key). create a new ephemeral event kind for relay notifications, and clients can query for that kind if they want to display the relay's notifications to the user. clients can put the user's pubkey in filter #p to indicate which user's notifications should be sent. you could also define different types of notifications with l tag such as "payment", "promotion", "donation" so clients could pull only certain types of notifications. what do you think?

@vitorpamplona
Copy link
Collaborator Author

Clients can block Notifications from relays that are not on the user's relay list. It's up to them. But if people have a paid relay in their list and are not paying, I think it's fair to blast them with payment requests. They can choose to pay or to remove the relay from their main relay list.

If clients are pinging different relays than those the user expects them to ping, they don't need to show such notifications if they don't want to. The NOTIFY works best for relays the user knows and is choosing to use.

but generally we shouldn't create new message types that could largely be ignored.

Relays do that all the time, though. Most of the "NOTICE" messages are completely ignored.

create a new ephemeral event kind for relay notifications, and clients...

It's an alternative. But you have to create the infrastructure to make it work. Clients will need to know what is the pubkey used for those messages for each relay and discard everything else. Right now, relays don't have a pubkey for them to message clients. Clients will also need to make sure they don't notify from an old event that was stored in a separate relay and it just happens that that relay decided to send that event.

In other words, competing relays can keep old versions of these notifications, and as long as they are sent before a newer event, the user will think it is real. Basically, relays can send a popup hell chain by collecting and rebroadcasting events from other relays.

Anyway, we went there and it looked very messy. I am not opposed to it, but I don't know if I mapped out all the threats in that model.

@monlovesmango
Copy link
Member

Most of the "NOTICE" messages are completely ignored.

agree, but its the only message type that is ignored, and it is needed bc we need at least one way to communicate why connection failed, which shouldn't generally be user facing. this wasn't designed to be a user facing message, NOTIFY is.

But you have to create the infrastructure to make it work. Clients will need to know what is the pubkey used for those messages for each relay and discard everything else. Right now, relays don't have a pubkey for them to message clients.

no that is not what I am proposing. the pubkey of the ephemeral event could be anything and the client doesn't need to know what it is. the client would just send a req with filter { kinds: [relayNotificationKind], #p: [userPubkey] } and the relay would know to create an ephemeral event (with any key) to send to the client. this should require zero extra infrastructure above what is proposed in the NOTIFY workflow.

basically a relay notification event should be associated with the relay it comes from, not the pubkey on the event. so the "competing relays can keep old versions of these notifications" doesn't really apply.

@vitorpamplona
Copy link
Collaborator Author

vitorpamplona commented Dec 7, 2023

Why do you need to filter, then? The relay can just send it at any point in the regular subscription.

It's weird that it is a random key. The relay will need to make sure to not accept these events from anyone else because there is no way for the client to check if this was created at the relay or if it was broadcasted by somebody else.

Sounds dangerous and not backwards compatible. Relays that don't implement this variation wouldn't know they have to block a given event kind and will pass the fake notifications down to the client.

@monlovesmango
Copy link
Member

monlovesmango commented Dec 7, 2023

Why do you need to filter, then? The relay can just send it at any point in the regular subscription.

because otherwise there is no specific sub id to associate the event message with. plus relay should only send events in response to reqs, so that it is opt in on client side.

The relay will need to make sure to not accept these events from anyone else because there is no way for the client to check if this was created at the relay or if it was broadcasted by somebody else.

correct, but seems like pretty trivial logic.

Sounds dangerous and not backwards compatible. Relays that don't implement this variation wouldn't know they have to block a given event kind and will pass the fake notifications down to the client.

agree that's technically possible, but seems like an unrealistic attack, especially if using #p in your filter. takes a lot of effort for very low gain. besides, the nip could recommend that clients only request notifications from relay that support the relay notifications nip (which they could query via nip11)

@vitorpamplona
Copy link
Collaborator Author

seems like an unrealistic attack

After I saw people trying to dupe users with fake replies on the Nostr Wallet Connect, I think everything is possible. But in that case, the client knows which key is supposed to be used and can discard everything else.

takes a lot of effort for very low gain

I am not really sure if it is very low. Somebody can claim that their subscription to nostr.wine is up and include an invoice to the attacker's wallet. Just create payloads for each user in Nostr and send them all to unsupporting relays. This could be a profitable attack.

But if somebody wants to create this variation, I can do a test code as well.

@monlovesmango
Copy link
Member

Just create payloads for each user in Nostr and send them all to unsupporting relays. This could be a profitable attack.

they would have continuously be sending these for thousands of users to multiple relays (while avoiding being filtered by relay firewall) in the hope that for one specific user it overlaps with the 1-2 sec it takes the client to query for relay notifications (as the sub really shouldn't need to be kept open). sounds like lot of needed resources with low probability of success and low payout.

and again, could recommend only querying relays who support this nip.

@nostr-wine
Copy link
Contributor

Wanted to bump this and say nostr.wine have been using NOTIFY successfully with Amethyst across several of our relays for 4 months now. It's very useful for relays to be able to communicate directly with users for things that require immediate action (most notably payments). We'd love to see other clients implement this NIP.

@mikedilger
Copy link
Contributor

This doesn't seem like a well thought out solution:

Clients SHOULD expect the same request to be received multiple times. If the user dismisses it once, the client SHOULD ignore new requests with the same message.

Are you saying relays must watermark their messages to make sure they never repeat?

@vitorpamplona
Copy link
Collaborator Author

Clients SHOULD expect the same request to be received multiple times. If the user dismisses it once, the client SHOULD ignore new requests with the same message.

This was more of a protection against poorly implemented relays than anything else. We see relays sending 20 AUTH messages a second, so it's not hard to imagine that some implementations will send similar amounts of NOTIFY.

For Amethyst, we only allow one new message per session (go to the background and come back). Repeated messages can play when the user resumes the app unless the user marks to always dismiss this message, then they won't see it anymore.

@Yonle
Copy link

Yonle commented Feb 29, 2024

This function worked almost as similiar as javascript alert(). So we are neither using the current NOTIFY or the ALERT

@vitorpamplona vitorpamplona marked this pull request as ready for review March 28, 2024 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants