Compare commits

..

4 Commits

Author SHA1 Message Date
fiatjaf
42a4f43c93 nip46: remove words, introduce distinction between bunker key and user key. 2024-10-29 11:32:53 -03:00
Asai Toshiya
e3afd7ac5b update related to NIP-71. 2024-10-23 13:34:49 -03:00
Vitor Pamplona
bef7fc1cf4 Merge pull request #1533 from greenart7c3/nip_55_result
[NIP-55] - Change return field from signature to result
2024-10-23 09:05:17 -04:00
greenart7c3
30f39d35d1 Change return field from signature to result 2024-10-11 07:29:54 -03:00
5 changed files with 75 additions and 227 deletions

146
37.md
View File

@@ -1,146 +0,0 @@
NIP-37
======
Event Publication Onion-routing
-----------------
`draft` `optional`
This NIP defines a way to do onion-based routing for event publishing, optionally compensated with cashu where pubkeys (not necessarily relays) provide routing services.
## Announcement
A pubkey announces itself as willing to route by publishing an ephemeral event `kind:20690` in their outbox relays. Announcers should republish a new ephemeral event every few minutes to indicate they are still willing to route and online. The refresh rate depends on the relays where they are publishing (usually ~5 minutes).
```jsonc
{
"kind": 20690,
"tags": [
[ "relay", "wss://example1.com" ],
[ "relay", "wss://example2.com" ],
[ "fee", "1", "sat" ]
],
"content": "",
"pubkey": <pubkey-of-router>
}
```
Tags:
`relay` -- relay(s) where the pubkey will be listening for requests.
`fee` -- an optional fee indicating how much money the pubkey demands to be paid.
## Routing request
A sender publishes a series of `kind:2444` or `kind:20444` where the `payload` is the event that should be published by the pubkey of the current hop in the relays specified by the envelope. An optional proof can be included, this cashu proof is for the hop processing this route.
```jsonc
{
"kind": 20444,
"content": nip44_encrypt("{
'relays': [ 'wss://example1.com' ],
'event': { 'id': ...., sig: ..., }, // event the current routing pubkey should publish
'proof': <optional-unencoded-cashu-proof>,
'mint': 'https://mint.com',
'unit': 'sat'
}")
"tags": [
[ "p", "pubkey-of-next-hop" ]
]
}
```
Event `kind:2444` has the exact same format as the `kind:20444`, the only difference being that it's not ephemeral, so it can be used to route events that are expected to take longer to route. `kind:2444` events SHOULD include an [[NIP-40]] `expiration` tag.
Routing pubkeys MUST look at the `created_at` of the event they need to publish and wait until at least that time before publishing. Using future `created_at`s allows the sender to increase their privacy by preventing timing analysis by mixing different time delays at each hop.
When a routing pubkey receives a routing request event it should decrypt the content, redeem the cashu and publish the event in the `event` field. If the cashu cannot be redeemed the routing pubkey MUST NOT publish the event. This is useful as a way to **cancel** a routing event where the sender uses a future `created_at` timestamps and chooses to cancel the publication.
## Constructing a route
The sender:
1. looks for `kind:20400` announcements and assembles a path
2. creates the event they want to publish and sign it with their normal pubkey
3. walks the path backwards (finish -> start), putting the event signed in the previous step in the `event` field of the `content` and the other fileds of the envelope
4. encrypts to the current hop and signs -- both operations with a new disposable key and `p`-tags the current hop.
3. repeat step #3 and #4 for the rest of the route until the first hop
4. sender publishes the resulting event to one of the relays the first hop indicated it's active on
## Pseudocode implementation
```ts
// event to be published
event = { "kind": 1, content: "This is an onion-routed event" }
sign_event(event, my_private_key)
outer_layer = assemble_onion_route(event, [ "pubkey-A", "pubkey-B", "pubkey-C" ])
publish_event(outer_layer)
function assemble_onion_route(final_event, path) {
current_event = final_event // Start with the event to be published by the final hop
// Walk the path backwards from "C" to "A"
for hop_pubkey in reverse(path): // path = ["A", "B", "C"], reversed to ["C", "B", "A"]
// Generate a new disposable key for this hop
disposable_key = generate_disposable_key()
// Create the payload for this hop
payload = {
"event": current_event // The event for the hop to publish
// ...
}
// Encrypt the payload for the current hop
encrypted_payload = nip44_encrypt(payload, hop_pubkey) // Encrypt with the hop's pubkey
// Create the routing event for this hop
routing_event = {
"kind": 20444, // Ephemeral routing request
"content": encrypted_payload, // The encrypted payload for this hop
"tags": [["p", hop_pubkey]] // Tag indicating the pubkey of the next hop
}
// Sign the event with the disposable key for this hop
sign_event(routing_event, disposable_key)
// Prepare for the next hop (wrap another layer)
current_event = routing_event
}
return current_event // Return the fully wrapped outermost event ready to be published
}
```
## Example anatomy of a sequence of routing events
```jsonc
{
kind: 20444,
content: nip44_encrypt({
relays: [...],
event: {
"id": ...,
"kind": 20444,
"pubkey": "ephemeral-key-1",
"sig": ...,
content: nip44_encrypt({
relays: [....],
event: {
"id": ....,
"kind": 20444,
"pubkey": "ephemeral-key-2",
"sig": ...,
"content": nip44_encrypt({
relays: [ "wss://target-relay.com" ],
event: {
id: ...,
"kind": 1,
content: "This is an onion-routed event",
"pubkey": "real-pubkey",
"sig": ...,
}
})
}
})
}
}
}
```

85
46.md
View File

@@ -4,6 +4,10 @@ NIP-46
Nostr Remote Signing
--------------------
## Changes
`remote-signer-key` is introduced, passed in bunker url, clients must differentiate between `remote-signer-pubkey` and `user-pubkey`, must call `get_public_key` after connect.
## Rationale
Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface.
@@ -12,51 +16,53 @@ This NIP describes a method for 2-way communication between a remote signer and
## Terminology
- **Local keypair**: A local public and private key-pair used to encrypt content and communicate with the remote signer. Usually created by the client application.
- **Remote user pubkey**: The public key that the user wants to sign as. The remote signer has control of the private key that matches this public key.
- **Remote signer pubkey**: This is the public key of the remote signer itself. This is needed in both `create_account` command because you don't yet have a remote user pubkey.
- **user**: A person that is trying to use Nostr.
- **client**: A user-facing application that _user_ is looking at and clicking buttons in. This application will send requests to _remote-signer_.
- **remote-signer**: A daemon or server running somewhere that will answer requests from _client_, also known as "bunker".
- **client-keypair/pubkey**: The keys generated by _client_. Used to encrypt content and communicate with _remote-signer_.
- **remote-signer-keypair/pubkey**: The keys used by _remote-signer_ to encrypt content and communicate with _client_. This keypair MAY be same as _user-keypair_, but not necessarily.
- **user-keypair/pubkey**: The actual keys representing _user_ (that will be used to sign events in response to `sign_event` requests, for example). The _remote-signer_ generally has control over these keys.
All pubkeys specified in this NIP are in hex format.
## Initiating a connection
To initiate a connection between a client and a remote signer there are a few different options.
There are two ways to initiate a connection:
### Direct connection initiated by remote signer
### Direct connection initiated by _remote-signer_
This is most common in a situation where you have your own nsecbunker or other type of remote signer and want to connect through a client that supports remote signing.
The remote signer would provide a connection token in the form:
_remote-signer_ provides connection token in the form:
```
bunker://<remote-user-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
bunker://<remote-signer-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
```
This token is pasted into the client by the user and the client then uses the details to connect to the remote signer via the specified relay(s). Optional secret can be used for single successfully established connection only, remote signer SHOULD ignore new attempts to establish connection with old optional secret.
_user_ pastes this token on _client_, which then uses the details to connect to _remote-signer_ via the specified relays. Optional secret can be used for single successfully established connection only, _remote-signer_ SHOULD ignore new attempts to establish connection with old optional secret.
### Direct connection initiated by the client
In this case, basically the opposite direction of the first case, the client provides a connection token (or encodes the token in a QR code) and the signer initiates a connection to the client via the specified relay(s).
In this case, basically the opposite direction of the first case, _client_ provides a connection token (or encodes the token in a QR code) and _remote-signer_ initiates a connection via the specified relays.
```
nostrconnect://<local-keypair-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata in the form: {"name":"...", "url": "...", "description": "..."}>
nostrconnect://<client-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata in the form: {"name":"...", "url": "...", "description": "..."}>
```
## The flow
1. Client creates a local keypair. This keypair doesn't need to be communicated to the user since it's largely disposable (i.e. the user doesn't need to see this pubkey). Clients might choose to store it locally and they should delete it when the user logs out.
2. Client gets the remote user pubkey (either via a `bunker://` connection string or a NIP-05 login-flow; shown below)
3. Clients use the local keypair to send requests to the remote signer by `p`-tagging and encrypting to the remote user pubkey.
4. The remote signer responds to the client by `p`-tagging and encrypting to the local keypair pubkey.
1. _client_ generates `client-keypair`. This keypair doesn't need to be communicated to _user_ since it's largely disposable. _client_ might choose to store it locally and they should delete it on logout;
2. _client_ gets `remote-signer-pubkey` (either via a `bunker://` connection string or a NIP-05 login-flow; shown below);
3. _client_ use `client-keypair` to send requests to _remote-signer_ by `p`-tagging and encrypting to `remote-signer-pubkey`;
4. _remote-signer_ responds to _client_ by `p`-tagging and encrypting to the `client-pubkey`.
### Example flow for signing an event
- Remote user pubkey (e.g. signing as) `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
- Local pubkey is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
- `remote-signer-pubkey` is `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
- `user-pubkey` is also `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
- `client-pubkey` is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
#### Signature request
```json
```js
{
"kind": 24133,
"pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
@@ -70,13 +76,13 @@ nostrconnect://<local-keypair-pubkey>?relay=<wss://relay-to-connect-on>&metadata
created_at: 1714078911
}>)]
}),
"tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote user pubkey
"tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote-signer-pubkey
}
```
#### Response event
```json
```js
{
"kind": 24133,
"pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
@@ -84,7 +90,7 @@ nostrconnect://<local-keypair-pubkey>?relay=<wss://relay-to-connect-on>&metadata
"id": <random_string>,
"result": json_stringified(<signed-event>)
}),
"tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the local keypair pubkey
"tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the client-pubkey
}
```
@@ -94,20 +100,18 @@ nostrconnect://<local-keypair-pubkey>?relay=<wss://relay-to-connect-on>&metadata
## Request Events `kind: 24133`
```jsonc
```js
{
"id": <id>,
"kind": 24133,
"pubkey": <local_keypair_pubkey>,
"content": <nip04(<request>)>,
"tags": [["p", <remote_user_pubkey>]], // NB: in the `create_account` event, the remote signer pubkey should be `p` tagged.
"created_at": <unix timestamp in seconds>
"tags": [["p", <remote-signer-pubkey>]],
}
```
The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted and has the following structure:
```json
```jsonc
{
"id": <random_string>,
"method": <method_name>,
@@ -125,15 +129,16 @@ Each of the following are methods that the client sends to the remote signer.
| Command | Params | Result |
| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- |
| `connect` | `[<remote_user_pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" |
| `connect` | `[<user_pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" |
| `sign_event` | `[<{kind, content, tags, created_at}>]` | `json_stringified(<signed_event>)` |
| `ping` | `[]` | "pong" |
| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` |
| `get_public_key` | `[]` | `<hex-pubkey>` |
| `get_public_key` | `[]` | `<user-pubkey>` |
| `nip04_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip04_ciphertext>` |
| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` |
| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` |
| `nip44_decrypt` | `[<third_party_pubkey>, <nip44_ciphertext_to_decrypt>]` | `<plaintext>` |
| `create_account` | `[<username>, <domain>, <optional_email>, <optional_requested_permissions>]` | `<newly_created_user_pubkey>` |
### Requested permissions
@@ -145,9 +150,9 @@ The `connect` method may be provided with `optional_requested_permissions` for u
{
"id": <id>,
"kind": 24133,
"pubkey": <remote_signer_pubkey>,
"pubkey": <remote-signer-pubkey>,
"content": <nip04(<response>)>,
"tags": [["p", <local_keypair_pubkey>]],
"tags": [["p", <client-pubkey>]],
"created_at": <unix timestamp in seconds>
}
```
@@ -184,18 +189,6 @@ Clients should display (in a popup or new tab) the URL from the `error` field an
![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png)
## Remote Signer Commands
Remote signers might support additional commands when communicating directly with it. These commands follow the same flow as noted above, the only difference is that when the client sends a request event, the `p`-tag is the pubkey of the remote signer itself and the `content` payload is encrypted to the same remote signer pubkey.
### Methods/Commands
Each of the following are methods that the client sends to the remote signer.
| Command | Params | Result |
| ---------------- | ------------------------------------------ | ------------------------------------ |
| `create_account` | `[<username>, <domain>, <optional_email>, <optional_requested_permissions>]` | `<newly_created_remote_user_pubkey>` |
## Appendix
### NIP-05 Login Flow
@@ -204,7 +197,7 @@ Clients might choose to present a more familiar login flow, so users can type a
When the user types a NIP-05 the client:
- Queries the `/.well-known/nostr.json` file from the domain for the NIP-05 address provided to get the user's pubkey (this is the **remote user pubkey**)
- Queries the `/.well-known/nostr.json` file from the domain for the NIP-05 address provided to get the user's pubkey (this is the `user-pubkey`)
- In the same `/.well-known/nostr.json` file, queries for the `nip46` key to get the relays that the remote signer will be listening on.
- Now the client has enough information to send commands to the remote signer on behalf of the user.
@@ -216,9 +209,9 @@ In this last case, most often used to facilitate an OAuth-like signin flow, the
First the client will query for `kind: 31990` events that have a `k` tag of `24133`.
These are generally shown to a user, and once the user selects which remote signer to use and provides the remote user pubkey they want to use (via npub, pubkey, or nip-05 value), the client can initiate a connection. Note that it's on the user to select the remote signer that is actually managing the remote key that they would like to use in this case. If the remote user pubkey is managed on another remote signer, the connection will fail.
These are generally shown to a user, and once the user selects which remote signer to use and provides the `user-pubkey` they want to use (via npub, pubkey, or nip-05 value), the client can initiate a connection. Note that it's on the user to select the _remote-signer_ that is actually managing the `user-keypair` that they would like to use in this case. If the `user-pubkey` is managed on another _remote-signer_ the connection will fail.
In addition, it's important that clients validate that the pubkey of the announced remote signer matches the pubkey of the `_` entry in the `/.well-known/nostr.json` file of the remote signer's announced domain.
In addition, it's important that clients validate that the pubkey of the announced _remote-signer_ matches the pubkey of the `_` entry in the `/.well-known/nostr.json` file of the remote signer's announced domain.
Clients that allow users to create new accounts should also consider validating the availability of a given username in the namespace of remote signer's domain by checking the `/.well-known/nostr.json` file for existing usernames. Clients can then show users feedback in the UI before sending a `create_account` event to the remote signer and receiving an error in return. Ideally, remote signers would also respond with understandable error messages if a client tries to create an account with an existing username.

68
55.md
View File

@@ -53,8 +53,8 @@ val launcher = rememberLauncherForActivityResult(
Toast.LENGTH_SHORT
).show()
} else {
val signature = activityResult.data?.getStringExtra("signature")
// Do something with signature ...
val result = activityResult.data?.getStringExtra("result")
// Do something with result ...
}
}
)
@@ -101,10 +101,10 @@ launcher.launch(intent)
context.startActivity(intent)
```
- result:
- If the user approved intent it will return the **pubkey** in the signature field
- If the user approved intent it will return the **pubkey** in the result field
```kotlin
val pubkey = intent.data?.getStringExtra("signature")
val pubkey = intent.data?.getStringExtra("result")
// The package name of the signer application
val packageName = intent.data?.getStringExtra("package")
```
@@ -124,10 +124,10 @@ launcher.launch(intent)
context.startActivity(intent)
```
- result:
- If the user approved intent it will return the **signature**, **id** and **event** fields
- If the user approved intent it will return the **result**, **id** and **event** fields
```kotlin
val signature = intent.data?.getStringExtra("signature")
val signature = intent.data?.getStringExtra("result")
// The id you sent
val id = intent.data?.getStringExtra("id")
val signedEventJson = intent.data?.getStringExtra("event")
@@ -150,10 +150,10 @@ launcher.launch(intent)
context.startActivity(intent)
```
- result:
- If the user approved intent it will return the **signature** and **id** fields
- If the user approved intent it will return the **result** and **id** fields
```kotlin
val encryptedText = intent.data?.getStringExtra("signature")
val encryptedText = intent.data?.getStringExtra("result")
// the id you sent
val id = intent.data?.getStringExtra("id")
```
@@ -200,10 +200,10 @@ launcher.launch(intent)
context.startActivity(intent)
```
- result:
- If the user approved intent it will return the **signature** and **id** fields
- If the user approved intent it will return the **result** and **id** fields
```kotlin
val plainText = intent.data?.getStringExtra("signature")
val plainText = intent.data?.getStringExtra("result")
// the id you sent
val id = intent.data?.getStringExtra("id")
```
@@ -225,10 +225,10 @@ launcher.launch(intent)
context.startActivity(intent)
```
- result:
- If the user approved intent it will return the **signature** and **id** fields
- If the user approved intent it will return the **result** and **id** fields
```kotlin
val plainText = intent.data?.getStringExtra("signature")
val plainText = intent.data?.getStringExtra("result")
// the id you sent
val id = intent.data?.getStringExtra("id")
```
@@ -248,10 +248,10 @@ launcher.launch(intent)
context.startActivity(intent)
```
- result:
- If the user approved intent it will return the **signature** and **id** fields
- If the user approved intent it will return the **result** and **id** fields
```kotlin
val relayJsonText = intent.data?.getStringExtra("signature")
val relayJsonText = intent.data?.getStringExtra("result")
// the id you sent
val id = intent.data?.getStringExtra("id")
```
@@ -270,10 +270,10 @@ launcher.launch(intent)
context.startActivity(intent)
```
- result:
- If the user approved intent it will return the **signature** and **id** fields
- If the user approved intent it will return the **result** and **id** fields
```kotlin
val eventJson = intent.data?.getStringExtra("signature")
val eventJson = intent.data?.getStringExtra("result")
// the id you sent
val id = intent.data?.getStringExtra("id")
```
@@ -284,9 +284,9 @@ To get the result back from Signer Application you should use contentResolver.qu
If the user did not check the "remember my choice" option, the pubkey is not in Signer Application or the signer type is not recognized the `contentResolver` will return null
For the SIGN_EVENT type Signer Application returns two columns "signature" and "event". The column event is the signed event json
For the SIGN_EVENT type Signer Application returns two columns "result" and "event". The column event is the signed event json
For the other types Signer Application returns the column "signature"
For the other types Signer Application returns the column "result"
If the user chose to always reject the event, signer application will return the column "rejected" and you should not open signer application
@@ -305,13 +305,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **pubkey** in the signature column
- Will return the **pubkey** in the result column
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
if (index < 0) return
val pubkey = it.getString(index)
}
@@ -330,13 +330,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **signature** and the **event** columns
- Will return the **result** and the **event** columns
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
val indexJson = it.getColumnIndex("event")
val signature = it.getString(index)
val eventJson = it.getString(indexJson)
@@ -356,13 +356,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **signature** column
- Will return the **result** column
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
val encryptedText = it.getString(index)
}
```
@@ -380,13 +380,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **signature** column
- Will return the **result** column
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
val encryptedText = it.getString(index)
}
```
@@ -404,13 +404,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **signature** column
- Will return the **result** column
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
val encryptedText = it.getString(index)
}
```
@@ -428,13 +428,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **signature** column
- Will return the **result** column
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
val encryptedText = it.getString(index)
}
```
@@ -452,13 +452,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **signature** column
- Will return the **result** column
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
val relayJsonText = it.getString(index)
}
```
@@ -476,13 +476,13 @@ If the user chose to always reject the event, signer application will return the
)
```
- result:
- Will return the **signature** column
- Will return the **result** column
```kotlin
if (result == null) return
if (result.moveToFirst()) {
val index = it.getColumnIndex("signature")
val index = it.getColumnIndex("result")
val eventJson = it.getString(index)
}
```

View File

@@ -5,6 +5,8 @@ reverse chronological order.
| Date | Commit | NIP | Change |
| ----------- | --------- | -------- | ------ |
| 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [NIP-71](71.md) | some tags were replaced with `imeta` tag |
| 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [NIP-71](71.md) | `kind: 34237` was dropped |
| 2024-10-07 | [7bb8997b](https://github.com/nostr-protocol/nips/commit/7bb8997b) | [NIP-55](55.md) | some fields and passing data were changed |
| 2024-08-18 | [3aff37bd](https://github.com/nostr-protocol/nips/commit/3aff37bd) | [NIP-54](54.md) | content should be Asciidoc |
| 2024-07-31 | [3ea2f1a4](https://github.com/nostr-protocol/nips/commit/3ea2f1a4) | [NIP-45](45.md) | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) was reverted |

View File

@@ -208,7 +208,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `31990` | Handler information | [89](89.md) |
| `34235` | Video Event | [71](71.md) |
| `34236` | Short-form Portrait Video Event | [71](71.md) |
| `34237` | Video View Event | [71](71.md) |
| `34550` | Community Definition | [72](72.md) |
| `39000-9` | Group metadata events | [29](29.md) |