Skip to content

Conversation

@pablof7z
Copy link
Member

This PR introduces two independent but highly related NIPs.

Either NIP can be implemented separately and has independent benefits.

NIP-60: Cashu wallets

Store Cashu proofs in relays so that a wallet balance can follow you around nostr and can be easily spent from any client. Just like you load a client and your follow list is there, so can your pocket-money.

NIP-61: Nutzaps

Scheme to send nutzaps -- these are P2PK-locked cashu transfers that are NOT encrypted, but require a signature from a private key to be redeemed. Anyone can see the payload but only the holder of the private key can spend them.

Later on, I'll add DLEQ proofs to the nutzaps to make it possible to validate the validity of the proofs without talking to the mints.

Nutzaps have no zapper pubkey, where a third-party has to sign-off that a zap occurred.

NIP-60 money can be used to send NIP-57 zaps (by instructing the mint to pay the recipient's bolt11) or NIP-61 nutzaps.

Important considerations

Because ecash is such a contentious topic, here's a TL;DR:

  • you must OPT-IN to receive NIP-61 nutzaps -- if you don't signal with a bunch of mints you want to receive nutzaps on then people can't nutzap you.
  • nutzaps can provide a way around the issues that hodl invoices (e.g. zaps to zeus users) cause with force-closes -- a zeus user could use a nutzap as a temporary hold while they are offline and the moment they come online sweep everything into their zeus wallet.

```jsonc
{
"kind": 7375,
"content": nip44_encrypt({
Copy link

Choose a reason for hiding this comment

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

Curious, why not use the cashu token encoding here, rather than the raw json?
https://github.com/cashubtc/nuts/blob/main/00.md#v4-tokens

Choose a reason for hiding this comment

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

It would add an unnecessary extra encryption/decryption step.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup, what calvadev said

Copy link
Contributor

@dluvian dluvian left a comment

Choose a reason for hiding this comment

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

I feel like there is too much friction on the sender side of a nut zap. A sender has to fetch the Nutzap informational event, check if listed relays and mints are online and then mint new tokens in one of these mints. This complexity discourages client devs to adopt this standard and nut-zap-senders are discouraged from zapping if almost all of their attempts fail because the intended recipient hasn't published a 10019 event yet or the listed relays and mints are offline.

I haven't dabbled in the Cashu protocol yet but isn't it possible to timelock Cashu tokens with conditions? A sender could then timelock tokens in a way that allows the recipient to claim them anytime and the sender to reclaim them if they haven't been claimed after n days. Nuts will never get lost, relays don't have to be trusted and the sender can always immediately send those nutzaps.

@pablof7z
Copy link
Member Author

I feel like there is too much friction on the sender side of a nut zap. A sender has to fetch the Nutzap informational event, check if listed relays and mints are online and then mint new tokens in one of these mints. This complexity discourages client devs to adopt this standard and nut-zap-senders are discouraged from zapping if almost all of their attempts fail because the intended recipient hasn't published a 10019 event yet or the listed relays and mints are offline.

I haven't dabbled in the Cashu protocol yet but isn't it possible to timelock Cashu tokens with conditions? A sender could then timelock tokens in a way that allows the recipient to claim them anytime and the sender to reclaim them if they haven't been claimed after n days. Nuts will never get lost, relays don't have to be trusted and the sender can always immediately send those nutzaps.

No, this is as simple as it gets; anyone can nutzap without checking the 10019 of the recipient, but the recipient might never see those nutzaps if they don't check for them; this spec is actually incredibly simple and adding a timelock component would only make it more complex.

Checking if the infrastructure is online is a downside of a decentralized protocol; sure, it would be more straightforward to assume that you can publish to api.twitter.com and not even bother with error handling, but any nostr client needs must properly handle trying to speak with a few relays and having one of them be offline.

Fwiw, I wrote and implemented the entire spec in a few hours, this is not hard stuff whatsoever.

Also, you might have misunderstood the spec, relays don't need to be trusted in the same way NIP 65 relays don't need to be trusted, the listing in the 10019 is just a shorthand to make it easier to know where to publish in a way the recipient will see it. It's literally;y the exact same thing as nip 65 or as DM relays

@vitorpamplona
Copy link
Collaborator

Can the user's relay push its user by deleting all 7375 and 7377 events? I suppose you cannot recover the tokens if you don't have these events anymore.

@dluvian
Copy link
Contributor

dluvian commented Jul 23, 2024

relays don't need to be trusted

You need to trust the relay to not delete the token event and to deliver the event in the future. If you nutzap a 1-of-2 multisig nut then you don't have to trust the relay anymore as you can reclaim the funds if something goes wrong on the relay side. And if you can reclaim then you're free to zap anyone, making it less restrictive and simpler.

@pablof7z
Copy link
Member Author

@vitorpamplona

Can the user's relay push its user by deleting all 7375 and 7377 events? I suppose you cannot recover the tokens if you don't have these events anymore.

I don't understand the comment -- "relay push its user"? what does that mean? like kick out its user from using the relay?

At one point I had this appendix, that might shed some light to this concern, if that's what you meant. I removed it from the NIP because it sounded too paternalistic and clients can do whatever they choose to ensure their users are ok (i.e. like choosing good and enough relays, etc)

Appendix 1: Wallet health

Clients can check the resilience of the relays the are using to make sure tokens are not getting lost. Clients should regularly check if they are receiving the tokens they expect to receive across the set of relays where their proofs are being stored, and if a relay is not responding with the right tokens, the client can warn the user and/or roll the tokens to a new relay and discard the offending relay.

@pablof7z
Copy link
Member Author

relays don't need to be trusted

You need to trust the relay to not delete the token event and to deliver the event in the future. If you nutzap a 1-of-2 multisig nut then you don't have to trust the relay anymore as you can reclaim the funds if something goes wrong on the relay side. And if you can reclaim then you're free to zap anyone, making it less restrictive and simpler.

you're mixing two separate concerns: 1) token availability -- 2) clawback possibility

when you talk about "you need to trust the relay" you are exclusively talking about 1) because if you don't have the token you can't claw it back, regardless of what script you had in it, so the multisig comment is irrelevant here.

keep in mind that the recipient WANTS to receive the money, so they wouldn't choose relays that are going to delete their incoming money, that makes no sense. And ultimately the sender can publish to the relays the recipient specified and to their own relays or somewhere else.

@vitorpamplona
Copy link
Collaborator

@pablof7z sorry, I meant to punish the user. We have seen relays just going bust or deleting events at their will, either reacting to something the user uploaded or just as a suspicion of malfeasance. The warning you mention is good but it can be clearer:

In the current version of this NIP, the user can be rug pulled by the mint AND by the relay he/she decides to use.

I know the same risk is true for all nostr events, but losing money is very different than losing Twitter posts.

@pablof7z
Copy link
Member Author

@pablof7z sorry, I meant to punish the user. We have seen relays just going bust or deleting events at their will, either reacting to something the user uploaded or just as a suspicion of malfeasance. The warning you mention is good but it can be clearer:

In the current version of this NIP, the user can be rug pulled by the mint AND by the relay he/she decides to use.

I know the same risk is true for all nostr events, but losing money is very different than losing Twitter posts.

Ok, perhaps it should be explicit, but yeah, this is dollars to be used with multiple relays, particularly ones you run or trust; much like the drafts nip, I would hurt more if I lost my drafts than if I lose a few thousands sats. (Not kidding, I have tweet drafts I've only saved in typefully and have gone back to reading them for years, if typefully goes bust it'll suck; I need to move thise to nostr drafts asap)

I'll update the nip to make this explicit and perhaps add the Appendix as is if you think it's helpful.

* `a` an optional tag linking the token to a specific wallet.

### Spending proofs
When one or more proofs of a token are spent, the token event should be [[NIP-09]]-deleted and, if some proofs are unspent from the same token event, a new token event should be created rolling over the unspent proofs and adding any change outputs to the new token event.

Choose a reason for hiding this comment

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

This is challenging to maintain on the client since each spend requires the client to determine which events need to be deleted and what proofs from the deleted event should roll over to the new event while possibly adding more proofs.

What if the Spending History Event had inputs of the spent proofs and outputs of the new proofs instead? This event would now be mandatory, but it would make wallet tracking easier on the client since all the client has to do is replay all the events to get the current state. When the wallet makes a transaction, it is an easy-to-construct single event.

Maybe optionally, there can be a wallet "snapshot" or backup event that has all the unspent proofs at a specified point in time. The wallet can then replay the spending events since then to get the current state.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is challenging to maintain on the client since each spend requires the client to determine which events need to be deleted and what proofs from the deleted event should roll over to the new event while possibly adding more proofs.

This is not problematic at all, or maybe I'm not understanding your point; but if the client fetches, say, 4 events, which each has, say, 3 proofs inside, total of 12 proofs.

If the client spends, say, 10 of those proofs, it's trivial to know it has to rollover the 2 unspent proofs into a new token event and delete all 4 token events.

Which equates to the client publishing 2 events:
Kind 9 (event deletion) e-tagging 4 events
Kind 7375 storing two proofs.

Just to be very explicit, the relays won't send the 4 deleted events any more when a client REQs for 7375s, it would now just reply with a single event, the one that contains the two proofs.

Again, perhaps I misunderstood your comment 😂

Choose a reason for hiding this comment

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

I don't know if I would describe that process as "trivial." 😅

This way of handling proofs forces clients to associate event IDs with proofs versus the suggested alternatives, which don't require that association. So, in that sense, this idea is more complicated for the client than it needs to be.

Copy link

@ticruz38 ticruz38 Aug 25, 2024

Choose a reason for hiding this comment

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

I tend to agree with @davidcaseria
The annoying part with deleting events is if you have two nip60 wallet implementations on your device or different device.
You would store those events/proofs in an indexdb or local storage.
To get these 2 wallets in sync means replaying all the unspent proof events from the wallet creation (to account for the deleted one that once existed) Which you probably don’t want to do each time you open your app (or worse have both wallet polling all events every x seconds or min)

the append only method sounds simpler and more robust to sync the wallet accross platform/app, you just take all the unspent proofs from the events feed minus the spent proofs and you should be fine.

it doesn’t change anything for that NIP except that the spent proofs feed/event would now be mandatory. One can still delete the unspent proofs event but this would now be optional.

Choose a reason for hiding this comment

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

Also if the wallet wants to show an history for the user, which most neobanks do (wise, Revolut etc) keeping the full event history is a must

Copy link

Choose a reason for hiding this comment

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

I have the feeling the NIP is getting too deep into implementation details at this point, which isn't the goal of a NIP. It should be an implementation decision how to track the balance of the wallet. I'd find great to make the Spending History Event mandatory and thus wallets could choose an event sourcing approach as proposed by @davidcaseria too.

@wcat7
Copy link
Contributor

wcat7 commented Jul 27, 2024

NIP-61 still doesn't seem to address my previous concern:

If the content(nuts) of kind7337 is obtained by someone else and they also send a kind7337 event with this content(nuts), then who is the real person sent this nuts zap? How does the client determine this?

@callebtc
Copy link

callebtc commented Jul 27, 2024

If the content(nuts) of kind7337 is obtained by someone else and they also send a kind7337 event with this content(nuts), then who is the real person sent this nuts zap? How does the client determine this?

Very good point! I think this can be addressed: you can commit to arbitrary data by adding tags to the Secret field of a Cashu proof, just like you would with a nostr note. If this included the note ID that is being zapped, replays should be impossible.

@callebtc
Copy link

callebtc commented Jul 27, 2024

A sender could then timelock tokens in a way that allows the recipient to claim them anytime and the sender to reclaim them if they haven't been claimed after n days. Nuts will never get lost, relays don't have to be trusted and the sender can always immediately send those nutzaps.

This is certainly possible from a Cashu perspective and maybe even compliant with this spec? A P2PK-locked token can have a locktime tag which would allow the sender to redeem it back. Clients could become aware of this (and treat the zap as valid only if local time < locktime) or regard timelocked zaps as invalid altogether.

@pablof7z
Copy link
Member Author

If the content(nuts) of kind7337 is obtained by someone else and they also send a kind7337 event with this content(nuts), then who is the real person sent this nuts zap? How does the client determine this?

Very good point! I think this can be addressed: you can commit to arbitrary data by adding tags to the Secret field of a Cashu proof, just like you would with a nostr note. If this included the note ID that is being zapped, replays should be impossible.

Awesome, yeah, this is a good point -- I'll update the NIP to include tags for the sender and an optional "e" of the zapped note.

So the proofs would have an additional "tags": [ [ "p", "<sender-pubkey>" ], [ "e", "<optional-nutzapped-event>" ] ]

@davidcaseria
Copy link

In order for a client to implement a complete wallet, there needs to be a way to store NUT-04 and NUT-05 quotes. I believe a new expirable event for each quote is probably best suited to handle the wallet's storage needs. Note that the quote ID must remain private, so it needs to be in an encrypted content payload.

@davidcaseria
Copy link

Another case that should be handled is the different statuses a proof can be in. For example, when sending a token, CDK puts a proof in a Reserved status. This status is essential so that a wallet does not double-spend a proof. Only later does the CDK wallet check if the sent proofs have been claimed and remove them from the database.

@pablof7z
Copy link
Member Author

pablof7z commented Aug 7, 2024

Another case that should be handled is the different statuses a proof can be in. For example, when sending a token, CDK puts a proof in a Reserved status. This status is essential so that a wallet does not double-spend a proof. Only later does the CDK wallet check if the sent proofs have been claimed and remove them from the database.

yeah, the idea I had for this was to moved the proofs that are in reserved status to a new kind, so the same operation of rolling over proofs from a token event after they're spent, but move them to a different event kind, exact same format; upon initialization a wallet can check for this event kind and check if the proofs have been spent or not and either delete them or roll them back in into the available tokens kind.

@davidcaseria
Copy link

What if instead of having to manage deleting events, the token event was structured in such a way that the action for the proof was embedded:

{
  "a": [<proof>], // added proofs
  "d": [<proof>], // deleted proofs
  "r": [<proof>]  // reserved proofs
}

I want to refrain from relying on deleted events because that can cause issues if mishandled.

@pablof7z
Copy link
Member Author

pablof7z commented Aug 8, 2024

What if instead of having to manage deleting events, the token event was structured in such a way that the action for the proof was embedded:

{
  "a": [<proof>], // added proofs
  "d": [<proof>], // deleted proofs
  "r": [<proof>]  // reserved proofs
}

I want to refrain from relying on deleted events because that can cause issues if mishandled.

Oh yeah, that's an interesting idea; I'm not sure we need the deleted proofs one, the state of these events shouldn't grow too much as that would make syncing a bit more expensive -- is there a benefit to keeping that state?

can cause issues if mishandled.
What do you mean by "mishandled"?

Mishandled by the relays, as in, you instruct the relay to delete and it doesn't? Or what do you mean exactly?

@davidcaseria
Copy link

Mishandled by the relays, as in, you instruct the relay to delete and it doesn't? Or what do you mean exactly?

Yes, or clients. One relay implementation I was using returned both the deleted event and the kind 5 event, while another didn't return either. It's easy for an implementor not to handle that properly, so it should be avoided if possible.

Oh yeah, that's an interesting idea; I'm not sure we need the deleted proofs one, the state of these events shouldn't grow too much as that would make syncing a bit more expensive -- is there a benefit to keeping that state?

Thanks. The deleted proofs are needed because if a proof is reserved and doesn't get claimed, the user can reclaim it. Proofs will always go from added (unspent) to reserved to deleted (spent). It's best not to confuse events with states. In event-sourced systems, a state is always derived from events. If you replay all the events through a proof state machine, it's easy to arrive at the same deterministic result. Also, I recommend that most clients implementing this NIP save a local copy of these events since it's critical not to lose the proofs, so relying exclusively on relays would be imprudent. From there, syncing would be querying for events since the last event. If the number of proof events grows too large for the client to store, we can introduce an optional proof snapshot (replaceable) event type. This should have either unspent or reserved proofs in its current state. Seeding that event as the initial event into the client's state machine, you can replay events after the point in time of the snapshot and similarly arrive at the same deterministic result.

Clients MUST add `e` tags to create references of destroyed and created token events along with the marker of the meaning of the tag:
* `created` - A new token event was created.
* `destroyed` - A token event was destroyed.
* `redeemed` - A [[NIP-61]] nutzap was redeemed.
Copy link
Contributor

@believethehype believethehype Aug 9, 2024

Choose a reason for hiding this comment

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

Would it make sense to also log outgoing zaps? When balance changes because of "out" it could be due to the user e.g. melting tokens to lightning, but it could also be due to sending a zap. (as in "redeemed" but in the other direction)

@wcat7
Copy link
Contributor

wcat7 commented Aug 21, 2024

NIP-61 uses p2pk-pubkey to receive eCash, which is great for enhancing security. However, NIP-60 with kind 37375 places the p2pk privkey within the content. If a user's privkey is compromised, the p2pk privkey would also be exposed. My point is, instead of storing the p2pk privkey, could we allow users to securely manage keys on their own? Alternatively, could we store ncryptsec rather than the p2pk privkey to mitigate this risk?

@arthurfranca
Copy link
Contributor

I have a legit doubt, really not trying to poo on this PR.

NIP-60: Cashu wallets: [...] a wallet balance can follow you around nostr and can be easily spent from any client. [...]
NIP-61: Nutzaps: [...] NIP-60 money can be used to send NIP-57 zaps (by instructing the mint to pay the recipient's bolt11) or NIP-61 nutzaps

I saw that minibits, a cashu wallet that supports its own mint but also from others and offers lightning address to users, recently added NWC support.

NWC can pay zaps and also has a get_balance method.

Are nutzaps so much better than NIP-57 to justify creating a competing standard for transfering sats within nostr? If it is better than ok, let NIP-57 die with time, but else, this may confuse users a lot for little gain.

@ticruz38
Copy link

Might be asking in the wrong place entirely

Don't you think it would be possible to somehow use a Nostr extension, NIP-07, to let the mint know you are indeed in possession of the private key to the P2PK proofs that this mint has emitted?
The step that want you to encrypt the wallet nsec in the Nostr don't seems super safe and smooth, the beauty of those nips is to use your Nostr profile as your wallet.

You would just need to sign some sort of secret, that's probably possible already...

@callebtc ?

Copy link

@ekzyis ekzyis left a comment

Choose a reason for hiding this comment

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

This looks cool! I haven't read through the details yet but I am already intrigued by this. I was wondering about this though:

NIP-60 money can be used to send NIP-57 zaps (by instructing the mint to pay the recipient's bolt11) or NIP-61 nutzaps.

Can't NIP-60 money be used to pay any bolt11 (like any other cashu wallet), no matter how we fetched it? As this is written with specific mentions of NIP-57 and NIP-61, this suggests that might actually not be the case.

@pablof7z
Copy link
Member Author

This looks cool! I haven't read through the details yet but I am already intrigued by this. I was wondering about this though:

NIP-60 money can be used to send NIP-57 zaps (by instructing the mint to pay the recipient's bolt11) or NIP-61 nutzaps.

Can't NIP-60 money be used to pay any bolt11 (like any other cashu wallet), no matter how we fetched it? As this is written with specific mentions of NIP-57 and NIP-61, this suggests that might actually not be the case.

yes, of course, you can pay any bolt11 with a nip-60 wallet

@ekzyis
Copy link

ekzyis commented Sep 21, 2024

@arthurfranca:

Are nutzaps so much better than NIP-57 to justify creating a competing standard for transfering sats within nostr? If it is better than ok, let NIP-57 die with time, but else, this may confuse users a lot for little gain.

-- #1369 (comment)

NIP-60 is always custodial since it's based on cashu whereas NWC NIP-57 does not have to use cashu. I don't think we should limit sending sats on nostr to cashu-only.

@Egge21M
Copy link
Contributor

Egge21M commented Sep 29, 2024

Don't you think it would be possible to somehow use a Nostr extension, NIP-07, to let the mint know you are indeed in possession of the private key to the P2PK proofs that this mint has emitted? The step that want you to encrypt the wallet nsec in the Nostr don't seems super safe and smooth, the beauty of those nips is to use your Nostr profile as your wallet.

You would just need to sign some sort of secret, that's probably possible already...

NIP-07 does not allow you to sign arbitrary data, only nostr events. Every attempt to change this got shut down quite quickly

@pablof7z
Copy link
Member Author

Don't you think it would be possible to somehow use a Nostr extension, NIP-07, to let the mint know you are indeed in possession of the private key to the P2PK proofs that this mint has emitted? The step that want you to encrypt the wallet nsec in the Nostr don't seems super safe and smooth, the beauty of those nips is to use your Nostr profile as your wallet.
You would just need to sign some sort of secret, that's probably possible already...

NIP-07 does not allow you to sign arbitrary data, only nostr events. Every attempt to change this got shut down quite quickly

yes, the privkey and the different p2pk is to get around this limitation

@ticruz38
Copy link

Correct me if I'm wrong, but cashu and nostr do not share the same encryption. That means cashu and Nostr pubkey cannot be the same.
nutszap are only possible to profile that have previously disclosed their cashu pubkey?

If this above is correct, I would rather have NIP-61 NIP-44 encrypt the C part of the proofs a sender wants to zap, without p2pk lock the proofs at all, at least you can now zap anyone on Nostr without any previous configuration from the recipient

@pablof7z
Copy link
Member Author

Correct me if I'm wrong, but cashu and nostr do not share the same encryption. That means cashu and Nostr pubkey cannot be the same. nutszap are only possible to profile that have previously disclosed their cashu pubkey?

incorrect, they are the same curve

If this above is correct, I would rather have NIP-61 NIP-44 encrypt the C part of the proofs a sender wants to zap, without p2pk lock the proofs at all, at least you can now zap anyone on Nostr without any previous configuration from the recipient

that would make them unverifiable by others, which is a major benefit over LN zaps.

You can still nutzap users that haven't done any setup, by zapping their nostr pubkey; but they will need to enter the nsec in the client to redeem it -- still completely reasonable, but would in real-world limit the number of clients a user can redeem from.

In my view, the main reason to have a user do the initial nutzap setup (a byproduct of which can be establishing this other pubkey for the p2pk lock), is the selection of the mints they are willing to work with. Making this explicit is, I believe, a good flow.

Of course, nothing prevents you from nutzapping someone that has not done their nutzap setup, but clients should not display those nutzaps as valid since perhaps even the receiver user is not even checking for them.

This is all open design space; different clients/users will make different decisions.

@ticruz38
Copy link

ticruz38 commented Sep 30, 2024

From my experience, p2pk lock a cashu token using my Nostr pubkey and then unlocking it with my Nostr private key just did not work on the mainstream mint, had an error where it said my key was not 32 or 64 Byte long. Probably an error on my end.

As long as using a Nostr pubkey to p2pk an ecash is feasible, I agree with this flow, where not only the recipient but a Nostr client can validate a zap. I just wasn't able to do so using minibit or lnserver mints.

Actually a matter of preference, which encryption do we use knowing they are pretty much the same?

Using Cashu encryption would discard all Nostr extension to use this NIP, at the benefit of having anyone validate the proofs.
Using Nostr encryption, then we onboard all kind of clients. At the cost of waiting for the recipient to validate zaps, which should happen pretty fast once this NIP becomes mainstream.

@pablof7z
Copy link
Member Author

From my experience, p2pk lock a cashu token using my Nostr pubkey and then unlocking it with my Nostr private key just did not work on the mainstream mint, had an error where it said my key was not 32 or 64 Byte long. Probably an error on my end.

You were missing the parity byte; nostr drops it, cashu keeps it. This is mentioned in NIP-60/61 😉

@fiatjaf
Copy link
Member

fiatjaf commented Oct 8, 2024

Is this ready to merge? I see there are like 5 clients using it already. Is it working?

@davidcaseria
Copy link

Is this ready to merge? I see there are like 5 clients using it already. Is it working?

I suggest waiting until a consensus is reached on using deleted events to manage proofs. I am firmly in favor of not using deleted events. Also, there needs to be a way to handle reserved proofs; otherwise, double-spend issues will be common.

pablof7z and others added 3 commits October 12, 2024 09:35
Co-authored-by: callebtc <[email protected]>
Co-authored-by: callebtc <[email protected]>
@fiatjaf fiatjaf merged commit ba46b23 into master Oct 25, 2024
@fiatjaf
Copy link
Member

fiatjaf commented Oct 25, 2024

There have been enough discussions and most people seem to be in consensus about this, and there are like 10 implementations out there, so it's time to merge. That doesn't mean we can't still change things.

RandyMcMillan pushed a commit to gnostr-org/gnostr-nips that referenced this pull request Apr 5, 2025
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.