[NWC] Add an encryption tag to negotiate upgrading to NIP44.

This is an alternative proposal to https://github.com/nostr-protocol/nips/pull/1531 to upgrade to nip44 without introducing a versioning system.
This commit is contained in:
Jeremy Klein 2025-02-12 13:50:30 -08:00
parent 6e7a618e7f
commit 9c9b0b422f

141
47.md
View File

@ -28,15 +28,16 @@ Fundamentally NWC is communication between a **client** and **wallet service** b
4. Once the payment is complete the **wallet service** will send an encrypted `response` (kind 23195) to the **user** over the relay(s) in the URI. 4. Once the payment is complete the **wallet service** will send an encrypted `response` (kind 23195) to the **user** over the relay(s) in the URI.
5. The **wallet service** may send encrypted notifications (kind 23196) of wallet events (such as a received payment) to the **client**. 5. The **wallet service** may send encrypted notifications (kind 23197 or 23196) of wallet events (such as a received payment) to the **client**.
## Events ## Events
There are four event kinds: There are four event kinds:
- `NIP-47 info event`: 13194 - `NIP-47 info event`: 13194
- `NIP-47 request`: 23194 - `NIP-47 request`: 23194
- `NIP-47 response`: 23195 - `NIP-47 response`: 23195
- `NIP-47 notification event`: 23196 - `NIP-47 notification event`: 23197 (23196 for backwards compatibility with NIP-04)
### Info Event ### Info Event
@ -46,34 +47,72 @@ The content should be a plaintext string with the supported capabilities space-s
If the **wallet service** supports notifications, the info event SHOULD contain a `notifications` tag with the supported notification types space-separated, eg. `payment_received payment_sent`. If the **wallet service** supports notifications, the info event SHOULD contain a `notifications` tag with the supported notification types space-separated, eg. `payment_received payment_sent`.
It should also contain supported encryption modes as described in the [Encryption](#encryption) section. For example:
```jsonc
{
"kind": 13194,
"tags": [
["encryption", "nip44 nip04"], // List of supported encryption schemes as described in the Encryption section.
["notifications", "payment_received payment_sent"]
// ...
],
"content": "pay_invoice get_balance make_invoice lookup_invoice list_transactions get_info notifications",
// ...
}
```
### Request and Response Events ### Request and Response Events
Both the request and response events SHOULD contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **client** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to. Both the request and response events SHOULD contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **client** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to.
Optionally, a request can have an `expiration` tag that has a unix timestamp in seconds. If the request is received after this timestamp, it should be ignored. Optionally, a request can have an `expiration` tag that has a unix timestamp in seconds. If the request is received after this timestamp, it should be ignored.
The content of requests and responses is encrypted with [NIP04](04.md), and is a JSON-RPCish object with a semi-fixed structure: The content of requests and responses is encrypted with [NIP44](44.md), and is a JSON-RPCish object with a semi-fixed structure.
Request: **Important note for backwards-compatibility:** The initial version of the protocol used [NIP04](04.md). If a **wallet service** or client app does not include the `encryption` tag in the
```jsonc `info` or request events, it should be assumed that the connection is using NIP04 for encryption. See the [Encryption](#encryption) section for more information.
Example request:
```js
{ {
"method": "pay_invoice", // method, string "kind" 23194,
"params": { // params, object "tags": [
"invoice": "lnbc50n1..." // command-related data ["encryption", "nip44"],
} ["p", "03..." ] // public key of the wallet service.
// ...
],
"content": nip44_encrypt({
"method": "pay_invoice", // method, string
"params": { // params, object
"invoice": "lnbc50n1..." // command-related data
}
}),
} }
``` ```
Response: Example response:
```jsonc
```js
{ {
"result_type": "pay_invoice", //indicates the structure of the result field "kind" 23195,
"error": { //object, non-null in case of error "tags": [
"code": "UNAUTHORIZED", //string error code, see below ["encryption", "nip44"],
"message": "human readable error message" ["p", "03..." ] // public key of the requesting client app
}, ["e", "1234"] // id of the request event this is responding to
"result": { // result, object. null in case of error. // ...
"preimage": "0123456789abcdef..." // command-related data ],
} "content": nip44_encrypt({
"result_type": "pay_invoice", //indicates the structure of the result field
"error": { //object, non-null in case of error
"code": "UNAUTHORIZED", //string error code, see below
"message": "human readable error message"
},
"result": { // result, object. null in case of error.
"preimage": "0123456789abcdef..." // command-related data
}
})
// ...
} }
``` ```
@ -83,9 +122,9 @@ If the command was successful, the `error` field must be null.
### Notification Events ### Notification Events
The notification event SHOULD contain one `p` tag, the public key of the **client**. The notification event is a kind 23197 event (23196 if using nip04) SHOULD contain one `p` tag, the public key of the **user**.
The content of notifications is encrypted with [NIP04](04.md), and is a JSON-RPCish object with a semi-fixed structure: The content of notifications is encrypted with [NIP44](44.md) (or NIP-04 for legacy client apps), and is a JSON-RPCish object with a semi-fixed structure:
```jsonc ```jsonc
{ {
@ -96,6 +135,7 @@ The content of notifications is encrypted with [NIP04](04.md), and is a JSON-RPC
} }
``` ```
_Note on backwards-compatibility:_ If a **wallet service** supports both nip44 and nip04 for legacy client apps, it should publish both notification events for each notification - kind 23196 encrypted with NIP-04, and kind 23197 encrypted with NIP-44. It is up to the **client** to decide which event to listen to based on its supported encryption and declared supported encryption schemes of the **wallet service** in the `info` event.
### Error codes ### Error codes
- `RATE_LIMITED`: The client is sending commands too fast. It should retry in a few seconds. - `RATE_LIMITED`: The client is sending commands too fast. It should retry in a few seconds.
@ -105,6 +145,7 @@ The content of notifications is encrypted with [NIP04](04.md), and is a JSON-RPC
- `RESTRICTED`: This public key is not allowed to do this operation. - `RESTRICTED`: This public key is not allowed to do this operation.
- `UNAUTHORIZED`: This public key has no wallet connected. - `UNAUTHORIZED`: This public key has no wallet connected.
- `INTERNAL`: An internal error. - `INTERNAL`: An internal error.
- `UNSUPPORTED_ENCRYPTION`: The encryption type of the request is not supported by the wallet service.
- `OTHER`: Other error. - `OTHER`: Other error.
## Nostr Wallet Connect URI ## Nostr Wallet Connect URI
@ -499,6 +540,63 @@ Notification:
2. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment. 2. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment.
3. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage. 3. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage.
## Encryption
The initial version of NWC used [NIP-04](04.md) for encryption which has been deprecated and replaced by [NIP-44](44.md). NIP-44 should always be preferred for encryption, but there may be legacy cases
where wither the **wallet service** or **client** has not yet migrated to NIP-44. The **wallet service** and **client** should negotiate the encryption method to use based on the `encryption` tags in the `info` event.
The negotiation works as follows.
1. The **wallet service** includes an `encryption` tag in the `info` event. This tag contains a space-separated list of encryption schemes that the **wallet service** supports (eg. `nip44 nip04`)
2. The **client application** includes an `encryption` tag in each request event. This tag contains the encryption scheme which should be used for the request. The **client application** should always prefer nip44 if supported by the **wallet service**.
### Info event
First, the **wallet service** adds an `encryption` tag to its `info` event containing a space-separated list of encryption schemes it supports. For example,
if a wallet service supports nip44, but also allows backwards-compatibility to nip04 client applications, its `encryption` tag in the `info` event might look something like:
```jsonc
{
"kind": 13194,
"tags": [
["encryption", "nip44 nip04"],
// ...
],
"content": "pay_invoice get_balance make_invoice lookup_invoice list_transactions get_info",
// ...
}
```
When a **client application** establishes a connection, it should read the info event and look for the `encryption` tag.
**Absence of this tag implies that the wallet only supports nip04.**
If the `encryption` tag is present, the **client application** will choose optimal encryption supported by both itself, and the **wallet service**, which should always be nip44 if possible.
### Request events
When a **client application** sends a request event, it should include a `encryption` tag with the encryption scheme it is using. The scheme MUST be supported by the **wallet service** as indicated by the info event.
For example, if the client application supports nip44, the request event might look like:
```jsonc
{
"kind": 23194,
"tags": [
["encryption", "nip44"],
// ...
],
// ...
}
```
If the **wallet service** does not support the specified encryption scheme, it will return an `UNSUPPORTED_ENCRYPTION` error. Absence of the `encryption` tag indicates use of nip04 for encryption.
### Notification events
As described above in the [Notifications](#notifications) section, if a **wallet service** supports both nip04 and nip44, it should publish two notification events for each notification - kind 23196 encrypted with NIP-04, and kind 23197 encrypted with NIP-44. If the **wallet service** only supports nip44, it should only publish kind 23197 events.
The **client** should check the `encryption` tag in the `info` event to determine which encryption schemes the **wallet service** supports, and listen to the appropriate notification event. Clients that support both nip04 and nip44 can choose to only listen to 23197 events for simplicity.
## Using a dedicated relay ## Using a dedicated relay
This NIP does not specify any requirements on the type of relays used. However, if the user is using a custodial service it might make sense to use a relay that is hosted by the custodial service. The relay may then enforce authentication to prevent metadata leaks. Not depending on a 3rd party relay would also improve reliability in this case. This NIP does not specify any requirements on the type of relays used. However, if the user is using a custodial service it might make sense to use a relay that is hosted by the custodial service. The relay may then enforce authentication to prevent metadata leaks. Not depending on a 3rd party relay would also improve reliability in this case.
@ -513,6 +611,7 @@ This NIP does not specify any requirements on the type of relays used. However,
"created_at": 1713883677, "created_at": 1713883677,
"kind": 13194, "kind": 13194,
"tags": [ "tags": [
[ "encryption", "nip44 nip04" ],
[ [
"notifications", "notifications",
"payment_received payment_sent" "payment_received payment_sent"