mirror of
https://github.com/nostr-protocol/nips.git
synced 2025-12-09 08:38:50 +00:00
Compare commits
27 Commits
cashu-wall
...
onion-rout
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fc031151c | ||
|
|
086335713c | ||
|
|
5e2ad4b3bb | ||
|
|
b5774a1338 | ||
|
|
07de7ea7e5 | ||
|
|
743e925ca5 | ||
|
|
1cda2dcc59 | ||
|
|
1e2f19863c | ||
|
|
e381b577c9 | ||
|
|
79cc2ef215 | ||
|
|
22c11cb243 | ||
|
|
38af1efe77 | ||
|
|
23ef4aa05b | ||
|
|
10c112defe | ||
|
|
4769b1658a | ||
|
|
db13d12eb6 | ||
|
|
7bb8997be5 | ||
|
|
7df7deebf5 | ||
|
|
2053aee0c2 | ||
|
|
f5a6fb258f | ||
|
|
78b6615c21 | ||
|
|
02e934acb7 | ||
|
|
7f67ce53fb | ||
|
|
344b0b9a72 | ||
|
|
e830a73cbd | ||
|
|
ce2234e0ba | ||
|
|
53afaaece6 |
2
01.md
2
01.md
@@ -81,7 +81,7 @@ This NIP defines 3 standard tags that can be used across all event kinds with th
|
||||
- for an addressable event: `["a", <kind integer>:<32-bytes lowercase hex of a pubkey>:<d tag value>, <recommended relay URL, optional>]`
|
||||
- for a normal replaceable event: `["a", <kind integer>:<32-bytes lowercase hex of a pubkey>:, <recommended relay URL, optional>]`
|
||||
|
||||
As a convention, all single-letter (only english alphabet letters: a-z, A-Z) key tags are expected to be indexed by relays, such that it is possible, for example, to query or subscribe to events that reference the event `"5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"` by using the `{"#e": ["5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"]}` filter.
|
||||
As a convention, all single-letter (only english alphabet letters: a-z, A-Z) key tags are expected to be indexed by relays, such that it is possible, for example, to query or subscribe to events that reference the event `"5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"` by using the `{"#e": ["5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"]}` filter. Only the first value in any given tag is indexed.
|
||||
|
||||
### Kinds
|
||||
|
||||
|
||||
8
18.md
8
18.md
@@ -25,6 +25,14 @@ quote reposted. The `q` tag ensures quote reposts are not pulled and included
|
||||
as replies in threads. It also allows you to easily pull and count all of the
|
||||
quotes for a post.
|
||||
|
||||
`q` tags should follow the same conventions as NIP 10 `e` tags, with the exception
|
||||
of the `mark` argument.
|
||||
|
||||
`["q", <event-id>, <relay-url>, <pubkey>]`
|
||||
|
||||
Quote reposts MUST include the [NIP-21](21.md) `nevent`, `note`, or `naddr` of the
|
||||
event in the content.
|
||||
|
||||
## Generic Reposts
|
||||
|
||||
Since `kind 6` reposts are reserved for `kind 1` contents, we use `kind 16`
|
||||
|
||||
2
23.md
2
23.md
@@ -20,7 +20,7 @@ The `.content` of these events should be a string text in Markdown syntax. To ma
|
||||
|
||||
### Metadata
|
||||
|
||||
For the date of the last update the `.created_at` field should be used, for "tags"/"hashtags" (i.e. topics about which the event might be of relevance) the `t` tag should be used, as per NIP-12.
|
||||
For the date of the last update the `.created_at` field should be used, for "tags"/"hashtags" (i.e. topics about which the event might be of relevance) the `t` tag should be used.
|
||||
|
||||
Other metadata fields can be added as tags to the event as necessary. Here we standardize 4 that may be useful, although they remain strictly optional:
|
||||
|
||||
|
||||
146
37.md
Normal file
146
37.md
Normal file
@@ -0,0 +1,146 @@
|
||||
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": ...,
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
124
55.md
124
55.md
@@ -101,10 +101,10 @@ launcher.launch(intent)
|
||||
context.startActivity(intent)
|
||||
```
|
||||
- result:
|
||||
- If the user approved intent it will return the **npub** in the signature field
|
||||
- If the user approved intent it will return the **pubkey** in the signature field
|
||||
|
||||
```kotlin
|
||||
val npub = intent.data?.getStringExtra("signature")
|
||||
val pubkey = intent.data?.getStringExtra("signature")
|
||||
// The package name of the signer application
|
||||
val packageName = intent.data?.getStringExtra("package")
|
||||
```
|
||||
@@ -118,8 +118,8 @@ launcher.launch(intent)
|
||||
intent.putExtra("type", "sign_event")
|
||||
// To handle results when not waiting between intents
|
||||
intent.putExtra("id", event.id)
|
||||
// Send the current logged in user npub
|
||||
intent.putExtra("current_user", npub)
|
||||
// Send the current logged in user pubkey
|
||||
intent.putExtra("current_user", pubkey)
|
||||
|
||||
context.startActivity(intent)
|
||||
```
|
||||
@@ -142,10 +142,10 @@ launcher.launch(intent)
|
||||
intent.putExtra("type", "nip04_encrypt")
|
||||
// to control the result in your application in case you are not waiting the result before sending another intent
|
||||
intent.putExtra("id", "some_id")
|
||||
// Send the current logged in user npub
|
||||
intent.putExtra("current_user", account.keyPair.pubKey.toNpub())
|
||||
// Send the hex pubKey that will be used for encrypting the data
|
||||
intent.putExtra("pubKey", pubKey)
|
||||
// Send the current logged in user pubkey
|
||||
intent.putExtra("current_user", account.keyPair.pubkey)
|
||||
// Send the hex pubkey that will be used for encrypting the data
|
||||
intent.putExtra("pubkey", pubkey)
|
||||
|
||||
context.startActivity(intent)
|
||||
```
|
||||
@@ -167,10 +167,10 @@ launcher.launch(intent)
|
||||
intent.putExtra("type", "nip44_encrypt")
|
||||
// to control the result in your application in case you are not waiting the result before sending another intent
|
||||
intent.putExtra("id", "some_id")
|
||||
// Send the current logged in user npub
|
||||
intent.putExtra("current_user", account.keyPair.pubKey.toNpub())
|
||||
// Send the hex pubKey that will be used for encrypting the data
|
||||
intent.putExtra("pubKey", pubKey)
|
||||
// Send the current logged in user pubkey
|
||||
intent.putExtra("current_user", account.keyPair.pubkey)
|
||||
// Send the hex pubkey that will be used for encrypting the data
|
||||
intent.putExtra("pubkey", pubkey)
|
||||
|
||||
context.startActivity(intent)
|
||||
```
|
||||
@@ -192,10 +192,10 @@ launcher.launch(intent)
|
||||
intent.putExtra("type", "nip04_decrypt")
|
||||
// to control the result in your application in case you are not waiting the result before sending another intent
|
||||
intent.putExtra("id", "some_id")
|
||||
// Send the current logged in user npub
|
||||
intent.putExtra("current_user", account.keyPair.pubKey.toNpub())
|
||||
// Send the hex pubKey that will be used for decrypting the data
|
||||
intent.putExtra("pubKey", pubKey)
|
||||
// Send the current logged in user pubkey
|
||||
intent.putExtra("current_user", account.keyPair.pubkey)
|
||||
// Send the hex pubkey that will be used for decrypting the data
|
||||
intent.putExtra("pubkey", pubkey)
|
||||
|
||||
context.startActivity(intent)
|
||||
```
|
||||
@@ -217,10 +217,10 @@ launcher.launch(intent)
|
||||
intent.putExtra("type", "nip04_decrypt")
|
||||
// to control the result in your application in case you are not waiting the result before sending another intent
|
||||
intent.putExtra("id", "some_id")
|
||||
// Send the current logged in user npub
|
||||
intent.putExtra("current_user", account.keyPair.pubKey.toNpub())
|
||||
// Send the hex pubKey that will be used for decrypting the data
|
||||
intent.putExtra("pubKey", pubKey)
|
||||
// Send the current logged in user pubkey
|
||||
intent.putExtra("current_user", account.keyPair.pubkey)
|
||||
// Send the hex pubkey that will be used for decrypting the data
|
||||
intent.putExtra("pubkey", pubkey)
|
||||
|
||||
context.startActivity(intent)
|
||||
```
|
||||
@@ -233,6 +233,29 @@ launcher.launch(intent)
|
||||
val id = intent.data?.getStringExtra("id")
|
||||
```
|
||||
|
||||
- **get_relays**
|
||||
- params:
|
||||
|
||||
```kotlin
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:"))
|
||||
intent.`package` = "com.example.signer"
|
||||
intent.putExtra("type", "get_relays")
|
||||
// to control the result in your application in case you are not waiting the result before sending another intent
|
||||
intent.putExtra("id", "some_id")
|
||||
// Send the current logged in user pubkey
|
||||
intent.putExtra("current_user", account.keyPair.pubkey)
|
||||
|
||||
context.startActivity(intent)
|
||||
```
|
||||
- result:
|
||||
- If the user approved intent it will return the **signature** and **id** fields
|
||||
|
||||
```kotlin
|
||||
val relayJsonText = intent.data?.getStringExtra("signature")
|
||||
// the id you sent
|
||||
val id = intent.data?.getStringExtra("id")
|
||||
```
|
||||
|
||||
- **decrypt_zap_event**
|
||||
- params:
|
||||
|
||||
@@ -242,8 +265,8 @@ launcher.launch(intent)
|
||||
intent.putExtra("type", "decrypt_zap_event")
|
||||
// to control the result in your application in case you are not waiting the result before sending another intent
|
||||
intent.putExtra("id", "some_id")
|
||||
// Send the current logged in user npub
|
||||
intent.putExtra("current_user", account.keyPair.pubKey.toNpub())
|
||||
// Send the current logged in user pubkey
|
||||
intent.putExtra("current_user", account.keyPair.pubkey)
|
||||
context.startActivity(intent)
|
||||
```
|
||||
- result:
|
||||
@@ -259,7 +282,7 @@ launcher.launch(intent)
|
||||
|
||||
To get the result back from Signer Application you should use contentResolver.query in Kotlin. If you are using another framework check the documentation of your framework or a third party library to get the result.
|
||||
|
||||
If the user did not check the "remember my choice" option, the npub is not in Signer Application or the signer type is not recognized the `contentResolver` will return null
|
||||
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
|
||||
|
||||
@@ -282,7 +305,7 @@ If the user chose to always reject the event, signer application will return the
|
||||
)
|
||||
```
|
||||
- result:
|
||||
- Will return the **npub** in the signature column
|
||||
- Will return the **pubkey** in the signature column
|
||||
|
||||
```kotlin
|
||||
if (result == null) return
|
||||
@@ -290,7 +313,7 @@ If the user chose to always reject the event, signer application will return the
|
||||
if (result.moveToFirst()) {
|
||||
val index = it.getColumnIndex("signature")
|
||||
if (index < 0) return
|
||||
val npub = it.getString(index)
|
||||
val pubkey = it.getString(index)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -300,7 +323,7 @@ If the user chose to always reject the event, signer application will return the
|
||||
```kotlin
|
||||
val result = context.contentResolver.query(
|
||||
Uri.parse("content://com.example.signer.SIGN_EVENT"),
|
||||
listOf("$eventJson", "", "${logged_in_user_npub}"),
|
||||
listOf("$eventJson", "", "${logged_in_user_pubkey}"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
@@ -326,7 +349,7 @@ If the user chose to always reject the event, signer application will return the
|
||||
```kotlin
|
||||
val result = context.contentResolver.query(
|
||||
Uri.parse("content://com.example.signer.NIP04_ENCRYPT"),
|
||||
listOf("$plainText", "${hex_pub_key}", "${logged_in_user_npub}"),
|
||||
listOf("$plainText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
@@ -350,7 +373,7 @@ If the user chose to always reject the event, signer application will return the
|
||||
```kotlin
|
||||
val result = context.contentResolver.query(
|
||||
Uri.parse("content://com.example.signer.NIP44_ENCRYPT"),
|
||||
listOf("$plainText", "${hex_pub_key}", "${logged_in_user_npub}"),
|
||||
listOf("$plainText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
@@ -374,7 +397,7 @@ If the user chose to always reject the event, signer application will return the
|
||||
```kotlin
|
||||
val result = context.contentResolver.query(
|
||||
Uri.parse("content://com.example.signer.NIP04_DECRYPT"),
|
||||
listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_npub}"),
|
||||
listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
@@ -398,7 +421,7 @@ If the user chose to always reject the event, signer application will return the
|
||||
```kotlin
|
||||
val result = context.contentResolver.query(
|
||||
Uri.parse("content://com.example.signer.NIP44_DECRYPT"),
|
||||
listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_npub}"),
|
||||
listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
@@ -416,13 +439,37 @@ If the user chose to always reject the event, signer application will return the
|
||||
}
|
||||
```
|
||||
|
||||
- **get_relays**
|
||||
- params:
|
||||
|
||||
```kotlin
|
||||
val result = context.contentResolver.query(
|
||||
Uri.parse("content://com.example.signer.GET_RELAYS"),
|
||||
listOf("${logged_in_user_pubkey}"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
```
|
||||
- result:
|
||||
- Will return the **signature** column
|
||||
|
||||
```kotlin
|
||||
if (result == null) return
|
||||
|
||||
if (result.moveToFirst()) {
|
||||
val index = it.getColumnIndex("signature")
|
||||
val relayJsonText = it.getString(index)
|
||||
}
|
||||
```
|
||||
|
||||
- **decrypt_zap_event**
|
||||
- params:
|
||||
|
||||
```kotlin
|
||||
val result = context.contentResolver.query(
|
||||
Uri.parse("content://com.example.signer.DECRYPT_ZAP_EVENT"),
|
||||
listOf("$eventJson", "", "${logged_in_user_npub}"),
|
||||
listOf("$eventJson", "", "${logged_in_user_pubkey}"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
@@ -472,28 +519,35 @@ Android intents and browser urls have limitations, so if you are using the `retu
|
||||
- params:
|
||||
|
||||
```js
|
||||
window.href = `nostrsigner:${plainText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_encrypt&callbackUrl=https://example.com/?event=`;
|
||||
window.href = `nostrsigner:${plainText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_encrypt&callbackUrl=https://example.com/?event=`;
|
||||
```
|
||||
|
||||
- **nip44_encrypt**
|
||||
- params:
|
||||
|
||||
```js
|
||||
window.href = `nostrsigner:${plainText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_encrypt&callbackUrl=https://example.com/?event=`;
|
||||
window.href = `nostrsigner:${plainText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_encrypt&callbackUrl=https://example.com/?event=`;
|
||||
```
|
||||
|
||||
- **nip04_decrypt**
|
||||
- params:
|
||||
|
||||
```js
|
||||
window.href = `nostrsigner:${encryptedText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_decrypt&callbackUrl=https://example.com/?event=`;
|
||||
window.href = `nostrsigner:${encryptedText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_decrypt&callbackUrl=https://example.com/?event=`;
|
||||
```
|
||||
|
||||
- **nip44_decrypt**
|
||||
- params:
|
||||
|
||||
```js
|
||||
window.href = `nostrsigner:${encryptedText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_decrypt&callbackUrl=https://example.com/?event=`;
|
||||
window.href = `nostrsigner:${encryptedText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_decrypt&callbackUrl=https://example.com/?event=`;
|
||||
```
|
||||
|
||||
- **get_relays**
|
||||
- params:
|
||||
|
||||
```js
|
||||
window.href = `nostrsigner:?compressionType=none&returnType=signature&type=get_relays&callbackUrl=https://example.com/?event=`;
|
||||
```
|
||||
|
||||
- **decrypt_zap_event**
|
||||
|
||||
117
71.md
117
71.md
@@ -20,21 +20,60 @@ The format uses an _addressable event_ kind `34235` for horizontal videos and `3
|
||||
|
||||
The `.content` of these events is a summary or description on the video content.
|
||||
|
||||
The list of tags are as follows:
|
||||
* `d` (required) universally unique identifier (UUID). Generated by the client creating the video event.
|
||||
* `url` (required) the url to the video file
|
||||
* `m` a string indicating the data type of the file. The [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) format must be used, and they should be lowercase.
|
||||
The primary source of video information is the `imeta` tags which is defined in [NIP-92](92.md)
|
||||
|
||||
Each `imeta` tag can be used to specify a variant of the video by the `dim` & `m` properties.
|
||||
|
||||
Example:
|
||||
```json
|
||||
[
|
||||
["imeta",
|
||||
"dim 1920x1080",
|
||||
"url https://myvideo.com/1080/12345.mp4",
|
||||
"x 3093509d1e0bc604ff60cb9286f4cd7c781553bc8991937befaacfdc28ec5cdc",
|
||||
"m video/mp4",
|
||||
"image https://myvideo.com/1080/12345.jpg",
|
||||
"image https://myotherserver.com/1080/12345.jpg",
|
||||
"fallback https://myotherserver.com/1080/12345.mp4",
|
||||
"fallback https://andanotherserver.com/1080/12345.mp4",
|
||||
"service nip96",
|
||||
],
|
||||
["imeta",
|
||||
"dim 1280x720",
|
||||
"url https://myvideo.com/720/12345.mp4",
|
||||
"x e1d4f808dae475ed32fb23ce52ef8ac82e3cc760702fca10d62d382d2da3697d",
|
||||
"m video/mp4",
|
||||
"image https://myvideo.com/720/12345.jpg",
|
||||
"image https://myotherserver.com/720/12345.jpg",
|
||||
"fallback https://myotherserver.com/720/12345.mp4",
|
||||
"fallback https://andanotherserver.com/720/12345.mp4",
|
||||
"service nip96",
|
||||
],
|
||||
["imeta",
|
||||
"dim 1280x720",
|
||||
"url https://myvideo.com/720/12345.m3u8",
|
||||
"x 704e720af2697f5d6a198ad377789d462054b6e8d790f8a3903afbc1e044014f",
|
||||
"m application/x-mpegURL",
|
||||
"image https://myvideo.com/720/12345.jpg",
|
||||
"image https://myotherserver.com/720/12345.jpg",
|
||||
"fallback https://myotherserver.com/720/12345.m3u8",
|
||||
"fallback https://andanotherserver.com/720/12345.m3u8",
|
||||
"service nip96",
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
Where `url` is the primary server url and `fallback` are other servers hosting the same file, both `url` and `fallback` should be weighted equally and clients are recommended to use any of the provided video urls.
|
||||
|
||||
The `image` tag contains a preview image (at the same resolution). Multiple `image` tags may be used to specify fallback copies in the same way `fallback` is used for `url`.
|
||||
|
||||
Additionally `service nip96` may be included to allow clients to search the authors NIP-96 server list to find the file using the hash.
|
||||
|
||||
### Other tags:
|
||||
* `title` (required) title of the video
|
||||
* `"published_at"`, for the timestamp in unix seconds (stringified) of the first time the video was published
|
||||
* `x` containing the SHA-256 hexencoded string of the file.
|
||||
* `size` (optional) size of file in bytes
|
||||
* `dim` (optional) size of file in pixels in the form `<width>x<height>`
|
||||
* `published_at`, for the timestamp in unix seconds (stringified) of the first time the video was published
|
||||
* `duration` (optional) video duration in seconds
|
||||
* `magnet` (optional) URI to magnet file
|
||||
* `i` (optional) torrent infohash
|
||||
* `text-track` (optional, repeated) link to WebVTT file for video, type of supplementary information (captions/subtitles/chapters/metadata), optional language code
|
||||
* `thumb` (optional) url of thumbnail with same aspect ratio
|
||||
* `image` (optional) url of preview image with same dimensions
|
||||
* `content-warning` (optional) warning about content of NSFW video
|
||||
* `alt` (optional) description for accessibility
|
||||
* `segment` (optional, repeated) start timestamp in format `HH:MM:SS.sss`, end timestamp in format `HH:MM:SS.sss`, chapter/segment title, chapter thumbnail-url
|
||||
@@ -53,19 +92,23 @@ The list of tags are as follows:
|
||||
["d", "<UUID>"],
|
||||
|
||||
["title", "<title of video>"],
|
||||
["thumb", "<thumbnail image for video>"],
|
||||
["published_at", "<unix timestamp>"],
|
||||
["alt", <description>],
|
||||
|
||||
// Video Data
|
||||
["url",<string with URI of file>],
|
||||
["m", <MIME type>],
|
||||
["x",<Hash SHA-256>],
|
||||
["size", <size of file in bytes>],
|
||||
["imeta",
|
||||
"dim 1920x1080",
|
||||
"url https://myvideo.com/1080/12345.mp4",
|
||||
"x 3093509d1e0bc604ff60cb9286f4cd7c781553bc8991937befaacfdc28ec5cdc",
|
||||
"m video/mp4",
|
||||
"image https://myvideo.com/1080/12345.jpg",
|
||||
"image https://myotherserver.com/1080/12345.jpg",
|
||||
"fallback https://myotherserver.com/1080/12345.mp4",
|
||||
"fallback https://andanotherserver.com/1080/12345.mp4",
|
||||
"service nip96",
|
||||
],
|
||||
|
||||
["duration", <duration of video in seconds>],
|
||||
["dim", <size of file in pixels>],
|
||||
["magnet",<magnet URI> ],
|
||||
["i",<torrent infohash>],
|
||||
["text-track", "<encoded `kind 6000` event>", "<recommended relay urls>"],
|
||||
["content-warning", "<reason>"],
|
||||
["segment", <start>, <end>, "<title>", "<thumbnail URL>"],
|
||||
@@ -83,36 +126,4 @@ The list of tags are as follows:
|
||||
["r", "<url>"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Video View
|
||||
|
||||
A video event view is a response to a video event to track a user's view or progress viewing the video.
|
||||
|
||||
### Format
|
||||
|
||||
The format uses an _addressable event_ kind `34237`.
|
||||
|
||||
The `.content` of these events is optional and could be a free-form note that acts like a bookmark for the user.
|
||||
|
||||
The list of tags are as follows:
|
||||
* `a` (required) reference tag to kind `34235` or `34236` video event being viewed
|
||||
* `d` (required) same as `a` reference tag value
|
||||
* `viewed` (optional, repeated) timestamp of the user's start time in seconds, timestamp of the user's end time in seconds
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
|
||||
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
|
||||
"created_at": <Unix timestamp in seconds>,
|
||||
"kind": 34237,
|
||||
"content": "<note>",
|
||||
"tags": [
|
||||
["a", "<34235 | 34236>:<video event author pubkey>:<d-identifier of video event>", "<optional relay url>"],
|
||||
["e", "<event-id", "<relay-url>"]
|
||||
["d", "<34235 | 34236>:<video event author pubkey>:<d-identifier of video event>"],
|
||||
["viewed", <start>, <end>],
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
13
94.md
13
94.md
@@ -26,6 +26,7 @@ This NIP specifies the use of the `1063` event type, having in `content` a descr
|
||||
* `summary` (optional) text excerpt
|
||||
* `alt` (optional) description for accessibility
|
||||
* `fallback` (optional) zero or more fallback file sources in case `url` fails
|
||||
* `service` (optional) service type which is serving the file (eg. [NIP-96](96.md))
|
||||
|
||||
```jsonc
|
||||
{
|
||||
@@ -33,15 +34,15 @@ This NIP specifies the use of the `1063` event type, having in `content` a descr
|
||||
"tags": [
|
||||
["url",<string with URI of file>],
|
||||
["m", <MIME type>],
|
||||
["x",<Hash SHA-256>],
|
||||
["ox",<Hash SHA-256>],
|
||||
["x", <Hash SHA-256>],
|
||||
["ox", <Hash SHA-256>],
|
||||
["size", <size of file in bytes>],
|
||||
["dim", <size of file in pixels>],
|
||||
["magnet",<magnet URI> ],
|
||||
["i",<torrent infohash>],
|
||||
["magnet", <magnet URI> ],
|
||||
["i", <torrent infohash>],
|
||||
["blurhash", <value>],
|
||||
["thumb", <string with thumbnail URI>],
|
||||
["image", <string with preview URI>],
|
||||
["thumb", <string with thumbnail URI>, <Hash SHA-256>],
|
||||
["image", <string with preview URI>, <Hash SHA-256>],
|
||||
["summary", <excerpt>],
|
||||
["alt", <description>]
|
||||
],
|
||||
|
||||
2
99.md
2
99.md
@@ -26,7 +26,7 @@ The `.pubkey` field of these events are treated as the party creating the listin
|
||||
|
||||
### Metadata
|
||||
|
||||
- For "tags"/"hashtags" (i.e. categories or keywords of relevance for the listing) the `"t"` event tag should be used, as per [NIP-12](12.md).
|
||||
- For "tags"/"hashtags" (i.e. categories or keywords of relevance for the listing) the `"t"` event tag should be used.
|
||||
- For images, whether included in the markdown content or not, clients SHOULD use `image` tags as described in [NIP-58](58.md). This allows clients to display images in carousel format more easily.
|
||||
|
||||
The following tags, used for structured metadata, are standardized and SHOULD be included. Other tags may be added as necessary.
|
||||
|
||||
@@ -5,6 +5,7 @@ reverse chronological order.
|
||||
|
||||
| Date | Commit | NIP | Change |
|
||||
| ----------- | --------- | -------- | ------ |
|
||||
| 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 |
|
||||
| 2024-07-30 | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) | [NIP-45](45.md) | NIP-45 was deprecated |
|
||||
|
||||
104
README.md
104
README.md
@@ -191,12 +191,14 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
| `30078` | Application-specific Data | [78](78.md) |
|
||||
| `30311` | Live Event | [53](53.md) |
|
||||
| `30315` | User Statuses | [38](38.md) |
|
||||
| `30388` | Slide Set | [Corny Chat][cornychat-slideset] |
|
||||
| `30402` | Classified Listing | [99](99.md) |
|
||||
| `30403` | Draft Classified Listing | [99](99.md) |
|
||||
| `30617` | Repository announcements | [34](34.md) |
|
||||
| `30618` | Repository state announcements | [34](34.md) |
|
||||
| `30818` | Wiki article | [54](54.md) |
|
||||
| `30819` | Redirects | [54](54.md) |
|
||||
| `31388` | Link Set | [Corny Chat][cornychat-linkset] |
|
||||
| `31890` | Feed | [NUD: Custom Feeds][NUD: Custom Feeds] |
|
||||
| `31922` | Date-Based Calendar Event | [52](52.md) |
|
||||
| `31923` | Time-Based Calendar Event | [52](52.md) |
|
||||
@@ -213,6 +215,8 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
[NUD: Custom Feeds]: https://wikifreedia.xyz/cip-01/
|
||||
[nostrocket]: https://github.com/nostrocket/NIPS/blob/main/Problems.md
|
||||
[lnpub]: https://github.com/shocknet/Lightning.Pub/blob/master/proto/autogenerated/client.md
|
||||
[cornychat-slideset]: https://cornychat.com/datatypes#kind30388slideset
|
||||
[cornychat-linkset]: https://cornychat.com/datatypes#kind31388linkset
|
||||
[joinstr]: https://gitlab.com/1440000bytes/joinstr/-/blob/main/NIP.md
|
||||
[NKBIP-01]: https://wikistr.com/nkbip-01
|
||||
[NKBIP-02]: https://wikistr.com/nkbip-02
|
||||
@@ -245,56 +249,56 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
|
||||
## Standardized Tags
|
||||
|
||||
| name | value | other parameters | NIP |
|
||||
| ----------------- | ------------------------------------ | ------------------------------- | ------------------------------------- |
|
||||
| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) |
|
||||
| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) |
|
||||
| `a` | coordinates to an event | relay URL | [01](01.md) |
|
||||
| `d` | identifier | -- | [01](01.md) |
|
||||
| `-` | -- | -- | [70](70.md) |
|
||||
| `g` | geohash | -- | [52](52.md) |
|
||||
| `h` | group id | -- | [29](29.md) |
|
||||
| `i` | external identity | proof, url hint | [39](39.md), [73](73.md) |
|
||||
| `k` | kind number (string) | -- | [18](18.md), [25](25.md), [72](72.md) |
|
||||
| `l` | label, label namespace | -- | [32](32.md) |
|
||||
| `L` | label namespace | -- | [32](32.md) |
|
||||
| `m` | MIME type | -- | [94](94.md) |
|
||||
| `q` | event id (hex) | relay URL | [18](18.md) |
|
||||
| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) |
|
||||
| `r` | relay url | marker | [65](65.md) |
|
||||
| `t` | hashtag | -- | [24](24.md), [34](34.md) |
|
||||
| `alt` | summary | -- | [31](31.md) |
|
||||
| `amount` | millisatoshis, stringified | -- | [57](57.md) |
|
||||
| `bolt11` | `bolt11` invoice | -- | [57](57.md) |
|
||||
| `challenge` | challenge string | -- | [42](42.md) |
|
||||
| `client` | name, address | relay URL | [89](89.md) |
|
||||
| `clone` | git clone URL | -- | [34](34.md) |
|
||||
| `content-warning` | reason | -- | [36](36.md) |
|
||||
| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) |
|
||||
| `description` | description | -- | [34](34.md), [57](57.md), [58](58.md) |
|
||||
| `emoji` | shortcode, image URL | -- | [30](30.md) |
|
||||
| `encrypted` | -- | -- | [90](90.md) |
|
||||
| `expiration` | unix timestamp (string) | -- | [40](40.md) |
|
||||
| `goal` | event id (hex) | relay URL | [75](75.md) |
|
||||
| `image` | image URL | dimensions in pixels | [23](23.md), [52](52.md), [58](58.md) |
|
||||
| `imeta` | inline metadata | -- | [92](92.md) |
|
||||
| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) |
|
||||
| `location` | location string | -- | [52](52.md), [99](99.md) |
|
||||
| `name` | name | -- | [34](34.md), [58](58.md), [72](72.md) |
|
||||
| `nonce` | random | difficulty | [13](13.md) |
|
||||
| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) |
|
||||
| `price` | price | currency, frequency | [99](99.md) |
|
||||
| `proxy` | external ID | protocol | [48](48.md) |
|
||||
| `published_at` | unix timestamp (string) | -- | [23](23.md) |
|
||||
| `relay` | relay url | -- | [42](42.md), [17](17.md) |
|
||||
| `relays` | relay list | -- | [57](57.md) |
|
||||
| `server` | file storage server url | -- | [96](96.md) |
|
||||
| `subject` | subject | -- | [14](14.md), [17](17.md), [34](34.md) |
|
||||
| `summary` | summary | -- | [23](23.md), [52](52.md) |
|
||||
| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) |
|
||||
| `title` | article title | -- | [23](23.md) |
|
||||
| `web` | webpage URL | -- | [34](34.md) |
|
||||
| `zap` | pubkey (hex), relay URL | weight | [57](57.md) |
|
||||
| name | value | other parameters | NIP |
|
||||
| ----------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- |
|
||||
| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) |
|
||||
| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) |
|
||||
| `a` | coordinates to an event | relay URL | [01](01.md) |
|
||||
| `d` | identifier | -- | [01](01.md) |
|
||||
| `-` | -- | -- | [70](70.md) |
|
||||
| `g` | geohash | -- | [52](52.md) |
|
||||
| `h` | group id | -- | [29](29.md) |
|
||||
| `i` | external identity | proof, url hint | [39](39.md), [73](73.md) |
|
||||
| `k` | kind | -- | [18](18.md), [25](25.md), [72](72.md), [73](73.md) |
|
||||
| `l` | label, label namespace | -- | [32](32.md) |
|
||||
| `L` | label namespace | -- | [32](32.md) |
|
||||
| `m` | MIME type | -- | [94](94.md) |
|
||||
| `q` | event id (hex) | relay URL, pubkey (hex) | [18](18.md) |
|
||||
| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) |
|
||||
| `r` | relay url | marker | [65](65.md) |
|
||||
| `t` | hashtag | -- | [24](24.md), [34](34.md) |
|
||||
| `alt` | summary | -- | [31](31.md) |
|
||||
| `amount` | millisatoshis, stringified | -- | [57](57.md) |
|
||||
| `bolt11` | `bolt11` invoice | -- | [57](57.md) |
|
||||
| `challenge` | challenge string | -- | [42](42.md) |
|
||||
| `client` | name, address | relay URL | [89](89.md) |
|
||||
| `clone` | git clone URL | -- | [34](34.md) |
|
||||
| `content-warning` | reason | -- | [36](36.md) |
|
||||
| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) |
|
||||
| `description` | description | -- | [34](34.md), [57](57.md), [58](58.md) |
|
||||
| `emoji` | shortcode, image URL | -- | [30](30.md) |
|
||||
| `encrypted` | -- | -- | [90](90.md) |
|
||||
| `expiration` | unix timestamp (string) | -- | [40](40.md) |
|
||||
| `goal` | event id (hex) | relay URL | [75](75.md) |
|
||||
| `image` | image URL | dimensions in pixels | [23](23.md), [52](52.md), [58](58.md) |
|
||||
| `imeta` | inline metadata | -- | [92](92.md) |
|
||||
| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) |
|
||||
| `location` | location string | -- | [52](52.md), [99](99.md) |
|
||||
| `name` | name | -- | [34](34.md), [58](58.md), [72](72.md) |
|
||||
| `nonce` | random | difficulty | [13](13.md) |
|
||||
| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) |
|
||||
| `price` | price | currency, frequency | [99](99.md) |
|
||||
| `proxy` | external ID | protocol | [48](48.md) |
|
||||
| `published_at` | unix timestamp (string) | -- | [23](23.md) |
|
||||
| `relay` | relay url | -- | [42](42.md), [17](17.md) |
|
||||
| `relays` | relay list | -- | [57](57.md) |
|
||||
| `server` | file storage server url | -- | [96](96.md) |
|
||||
| `subject` | subject | -- | [14](14.md), [17](17.md), [34](34.md) |
|
||||
| `summary` | summary | -- | [23](23.md), [52](52.md) |
|
||||
| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) |
|
||||
| `title` | article title | -- | [23](23.md) |
|
||||
| `web` | webpage URL | -- | [34](34.md) |
|
||||
| `zap` | pubkey (hex), relay URL | weight | [57](57.md) |
|
||||
|
||||
Please update these lists when proposing new NIPs.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user