Compare commits

..

2 Commits

Author SHA1 Message Date
fiatjaf
87ba38e73b a more construtive proposal for NIP-90 2025-05-02 12:43:38 -03:00
fiatjaf
8b9f440233 reset the DVM spec. 2025-05-01 13:24:47 -03:00
7 changed files with 148 additions and 396 deletions

4
01.md
View File

@@ -14,7 +14,7 @@ Each user has a keypair. Signatures, public key, and encodings are done accordin
The only object type that exists is the `event`, which has the following format on the wire:
```yaml
```jsonc
{
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
@@ -120,7 +120,7 @@ Clients can send 3 types of messages, which must be JSON arrays, according to th
`<filtersX>` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
```yaml
```json
{
"ids": <a list of event ids>,
"authors": <a list of lowercase pubkeys, the pubkey of an event must be one of these>,

79
17.md
View File

@@ -15,17 +15,17 @@ Kind `14` is a chat message. `p` tags identify one or more receivers of the mess
```jsonc
{
"id": "<usual hash>",
"pubkey": "<sender-pubkey>",
  "pubkey": "<sender-pubkey>",
"created_at": "<current-time>",
"kind": 14,
"tags": [
["p", "<receiver-1-pubkey>", "<relay-url>"],
["p", "<receiver-2-pubkey>", "<relay-url>"],
["e", "<kind-14-id>", "<relay-url>"] // if this is a reply
  "kind": 14,
  "tags": [
    ["p", "<receiver-1-pubkey>", "<relay-url>"],
    ["p", "<receiver-2-pubkey>", "<relay-url>"],
    ["e", "<kind-14-id>", "<relay-url>"] // if this is a reply
["subject", "<conversation-title>"],
// rest of tags...
],
"content": "<message-in-plain-text>",
    // rest of tags...
  ],
  "content": "<message-in-plain-text>",
}
```
@@ -65,22 +65,21 @@ Kind `14`s MUST never be signed. If it is signed, the message might leak to rela
}
```
Kind `15` is used for sending encrypted file event messages:
Kind 15 is used for sending encrypted file event messages:
- `file-type`: Specifies the MIME type of the attached file (e.g., `image/jpeg`, `audio/mpeg`, etc.) before encryption.
- `encryption-algorithm`: Indicates the encryption algorithm used for encrypting the file. Supported algorithms: `aes-gcm`.
- `file-type`: Specifies the MIME type of the attached file (e.g., `image/jpeg`, `audio/mpeg`, etc.).
- `encryption-algorithm`: Indicates the encryption algorithm used for encrypting the file. Supported algorithms may include `aes-gcm`, `chacha20-poly1305`,`aes-cbc` etc.
- `decryption-key`: The decryption key that will be used by the recipient to decrypt the file.
- `decryption-nonce`: The decryption nonce that will be used by the recipient to decrypt the file.
- `content`: The URL of the file (`<file-url>`).
- `x` containing the SHA-256 hexencoded string of the encrypted file.
- `ox` containing the SHA-256 hexencoded string of the file before encryption.
- `size` (optional) size of the encrypted file in bytes
- `dim` (optional) size in pixels in the form `<width>x<height>`
- `x` containing the SHA-256 hexencoded string of the file.
- `size` (optional) size of file in bytes
- `dim` (optional) size of the file in pixels in the form `<width>x<height>`
- `blurhash`(optional) the [blurhash](https://github.com/woltapp/blurhash) to show while the client is loading the file
- `thumb` (optional) URL of thumbnail with same aspect ratio (encrypted with the same key, nonce)
- `fallback` (optional) zero or more fallback file sources in case `url` fails (encrypted with the same key, nonce)
- `fallback` (optional) zero or more fallback file sources in case `url` fails
Just like kind `14`, kind `15`s MUST never be signed.
Just like kind 14, kind `15`s MUST never be signed.
## Chat Rooms
@@ -88,34 +87,34 @@ The set of `pubkey` + `p` tags defines a chat room. If a new `p` tag is added or
Clients SHOULD render messages of the same room in a continuous thread.
An optional `subject` tag defines the current name/topic of the conversation. Any member can change the topic by simply submitting a new `subject` to an existing `pubkey` + `p` tags room. There is no need to send `subject` in every message. The newest `subject` in the chat room is the subject of the conversation.
An optional `subject` tag defines the current name/topic of the conversation. Any member can change the topic by simply submitting a new `subject` to an existing `pubkey` + `p`-tags room. There is no need to send `subject` in every message. The newest `subject` in the thread is the subject of the conversation.
## Encrypting
Following [NIP-59](59.md), the **unsigned** `kind:14` & `kind:15` chat messages must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.
```js
```jsonc
{
"id": "<usual hash>",
"pubkey": randomPublicKey,
"created_at": randomTimeUpTo2DaysInThePast(),
  "pubkey": randomPublicKey,
  "created_at": randomTimeUpTo2DaysInThePast(),
"kind": 1059, // gift wrap
"tags": [
["p", receiverPublicKey, "<relay-url>"] // receiver
],
"content": nip44Encrypt(
{
  "tags": [
    ["p", receiverPublicKey, "<relay-url>"] // receiver
  ],
  "content": nip44Encrypt(
    {
"id": "<usual hash>",
"pubkey": senderPublicKey,
"created_at": randomTimeUpTo2DaysInThePast(),
"kind": 13, // seal
"tags": [], // no tags
"content": nip44Encrypt(unsignedKind14, senderPrivateKey, receiverPublicKey),
"sig": "<signed by senderPrivateKey>"
},
randomPrivateKey, receiverPublicKey
),
"sig": "<signed by randomPrivateKey>"
      "pubkey": senderPublicKey,
      "created_at": randomTimeUpTo2DaysInThePast(),
      "kind": 13, // seal
      "tags": [], // no tags
      "content": nip44Encrypt(unsignedKind14, senderPrivateKey, receiverPublicKey),
      "sig": "<signed by senderPrivateKey>"
    },
    randomPrivateKey, receiverPublicKey
  ),
  "sig": "<signed by randomPrivateKey>"
}
```
@@ -125,7 +124,7 @@ Clients MUST verify if pubkey of the `kind:13` is the same pubkey on the `kind:1
Clients SHOULD randomize `created_at` in up to two days in the past in both the seal and the gift wrap to make sure grouping by `created_at` doesn't reveal any metadata.
The gift wrap's `p` tag can be the receiver's main pubkey or an alias key created to receive DMs without exposing the receiver's identity.
The gift wrap's `p`-tag can be the receiver's main pubkey or an alias key created to receive DMs without exposing the receiver's identity.
Clients CAN offer disappearing messages by setting an `expiration` tag in the gift wrap of each receiver or by not generating a gift wrap to the sender's public key
@@ -189,7 +188,7 @@ The two final GiftWraps, one to the receiver and the other to the sender, respec
"created_at":1703128320,
"kind":1059,
"tags":[
["p", "918e2da906df4ccd12c8ac672d8335add131a4cf9d27ce42b3bb3625755f0788"]
[ "p", "918e2da906df4ccd12c8ac672d8335add131a4cf9d27ce42b3bb3625755f0788"]
],
"content":"AsqzdlMsG304G8h08bE67dhAR1gFTzTckUUyuvndZ8LrGCvwI4pgC3d6hyAK0Wo9gtkLqSr2rT2RyHlE5wRqbCOlQ8WvJEKwqwIJwT5PO3l2RxvGCHDbd1b1o40ZgIVwwLCfOWJ86I5upXe8K5AgpxYTOM1BD+SbgI5jOMA8tgpRoitJedVSvBZsmwAxXM7o7sbOON4MXHzOqOZpALpS2zgBDXSAaYAsTdEM4qqFeik+zTk3+L6NYuftGidqVluicwSGS2viYWr5OiJ1zrj1ERhYSGLpQnPKrqDaDi7R1KrHGFGyLgkJveY/45y0rv9aVIw9IWF11u53cf2CP7akACel2WvZdl1htEwFu/v9cFXD06fNVZjfx3OssKM/uHPE9XvZttQboAvP5UoK6lv9o3d+0GM4/3zP+yO3C0NExz1ZgFmbGFz703YJzM+zpKCOXaZyzPjADXp8qBBeVc5lmJqiCL4solZpxA1865yPigPAZcc9acSUlg23J1dptFK4n3Tl5HfSHP+oZ/QS/SHWbVFCtq7ZMQSRxLgEitfglTNz9P1CnpMwmW/Y4Gm5zdkv0JrdUVrn2UO9ARdHlPsW5ARgDmzaxnJypkfoHXNfxGGXWRk0sKLbz/ipnaQP/eFJv/ibNuSfqL6E4BnN/tHJSHYEaTQ/PdrA2i9laG3vJti3kAl5Ih87ct0w/tzYfp4SRPhEF1zzue9G/16eJEMzwmhQ5Ec7jJVcVGa4RltqnuF8unUu3iSRTQ+/MNNUkK6Mk+YuaJJs6Fjw6tRHuWi57SdKKv7GGkr0zlBUU2Dyo1MwpAqzsCcCTeQSv+8qt4wLf4uhU9Br7F/L0ZY9bFgh6iLDCdB+4iABXyZwT7Ufn762195hrSHcU4Okt0Zns9EeiBOFxnmpXEslYkYBpXw70GmymQfJlFOfoEp93QKCMS2DAEVeI51dJV1e+6t3pCSsQN69Vg6jUCsm1TMxSs2VX4BRbq562+VffchvW2BB4gMjsvHVUSRl8i5/ZSDlfzSPXcSGALLHBRzy+gn0oXXJ/447VHYZJDL3Ig8+QW5oFMgnWYhuwI5QSLEyflUrfSz+Pdwn/5eyjybXKJftePBD9Q+8NQ8zulU5sqvsMeIx/bBUx0fmOXsS3vjqCXW5IjkmSUV7q54GewZqTQBlcx+90xh/LSUxXex7UwZwRnifvyCbZ+zwNTHNb12chYeNjMV7kAIr3cGQv8vlOMM8ajyaZ5KVy7HpSXQjz4PGT2/nXbL5jKt8Lx0erGXsSsazkdoYDG3U",
"sig":"a3c6ce632b145c0869423c1afaff4a6d764a9b64dedaf15f170b944ead67227518a72e455567ca1c2a0d187832cecbde7ed478395ec4c95dd3e71749ed66c480"
@@ -203,7 +202,7 @@ The two final GiftWraps, one to the receiver and the other to the sender, respec
"created_at":1702711587,
"kind":1059,
"tags":[
["p", "44900586091b284416a0c001f677f9c49f7639a55c3f1e2ec130a8e1a7998e1b"]
[ "p", "44900586091b284416a0c001f677f9c49f7639a55c3f1e2ec130a8e1a7998e1b"]
],
"content":"AsTClTzr0gzXXji7uye5UB6LYrx3HDjWGdkNaBS6BAX9CpHa+Vvtt5oI2xJrmWLen+Fo2NBOFazvl285Gb3HSM82gVycrzx1HUAaQDUG6HI7XBEGqBhQMUNwNMiN2dnilBMFC3Yc8ehCJT/gkbiNKOpwd2rFibMFRMDKai2mq2lBtPJF18oszKOjA+XlOJV8JRbmcAanTbEK5nA/GnG3eGUiUzhiYBoHomj3vztYYxc0QYHOx0WxiHY8dsC6jPsXC7f6k4P+Hv5ZiyTfzvjkSJOckel1lZuE5SfeZ0nduqTlxREGeBJ8amOykgEIKdH2VZBZB+qtOMc7ez9dz4wffGwBDA7912NFS2dPBr6txHNxBUkDZKFbuD5wijvonZDvfWq43tZspO4NutSokZB99uEiRH8NAUdGTiNb25m9JcDhVfdmABqTg5fIwwTwlem5aXIy8b66lmqqz2LBzJtnJDu36bDwkILph3kmvaKPD8qJXmPQ4yGpxIbYSTCohgt2/I0TKJNmqNvSN+IVoUuC7ZOfUV9lOV8Ri0AMfSr2YsdZ9ofV5o82ClZWlWiSWZwy6ypa7CuT1PEGHzywB4CZ5ucpO60Z7hnBQxHLiAQIO/QhiBp1rmrdQZFN6PUEjFDloykoeHe345Yqy9Ke95HIKUCS9yJurD+nZjjgOxZjoFCsB1hQAwINTIS3FbYOibZnQwv8PXvcSOqVZxC9U0+WuagK7IwxzhGZY3vLRrX01oujiRrevB4xbW7Oxi/Agp7CQGlJXCgmRE8Rhm+Vj2s+wc/4VLNZRHDcwtfejogjrjdi8p6nfUyqoQRRPARzRGUnnCbh+LqhigT6gQf3sVilnydMRScEc0/YYNLWnaw9nbyBa7wFBAiGbJwO40k39wj+xT6HTSbSUgFZzopxroO3f/o4+ubx2+IL3fkev22mEN38+dFmYF3zE+hpE7jVxrJpC3EP9PLoFgFPKCuctMnjXmeHoiGs756N5r1Mm1ffZu4H19MSuALJlxQR7VXE/LzxRXDuaB2u9days/6muP6gbGX1ASxbJd/ou8+viHmSC/ioHzNjItVCPaJjDyc6bv+gs1NPCt0qZ69G+JmgHW/PsMMeL4n5bh74g0fJSHqiI9ewEmOG/8bedSREv2XXtKV39STxPweceIOh0k23s3N6+wvuSUAJE7u1LkDo14cobtZ/MCw/QhimYPd1u5HnEJvRhPxz0nVPz0QqL/YQeOkAYk7uzgeb2yPzJ6DBtnTnGDkglekhVzQBFRJdk740LEj6swkJ",
"sig":"c94e74533b482aa8eeeb54ae72a5303e0b21f62909ca43c8ef06b0357412d6f8a92f96e1a205102753777fd25321a58fba3fb384eee114bd53ce6c06a1c22bab"

12
51.md
View File

@@ -22,7 +22,6 @@ For example, _mute list_ can contain the public keys of spammers and bad actors
| name | kind | description | expected tag items |
| --- | --- | --- | --- |
| Follow list | 3 | microblogging basic follow list, see [NIP-02](02.md) | `"p"` (pubkeys -- with optional relay hint and petname) |
| Mute list | 10000 | things the user doesn't want to see in their feeds | `"p"` (pubkeys), `"t"` (hashtags), `"word"` (lowercase string), `"e"` (threads) |
| Pinned notes | 10001 | events the user intends to showcase in their profile page | `"e"` (kind:1 notes) |
| Read/write relays | 10002 | where a user publishes to and where they expect mentions | see [NIP-65](65.md) |
@@ -32,9 +31,7 @@ For example, _mute list_ can contain the public keys of spammers and bad actors
| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) |
| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) |
| Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group id + relay URL + optional group name), `"r"` for each relay in use |
| Favorite relays | 10012 | user favorite relays and pointers to relay sets | `"relay"` (relay URLs) and `"a"` (kind:30002 relay set) |
| Interests | 10015 | topics a user may be interested in and pointers | `"t"` (hashtags) and `"a"` (kind:30015 interest set) |
| Media follows | 10020 | multimedia (photos, short video) follow list | `"p"` (pubkeys -- with optional relay hint and petname) |
| Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) |
| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) |
| Good wiki authors | 10101 | [NIP-54](54.md) user recommended wiki authors | `"p"` (pubkeys) |
@@ -60,9 +57,6 @@ Aside from their main identifier, the `"d"` tag, sets can optionally have a `"ti
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
| Release artifact sets | 30063 | group of artifacts of a software release | `"e"` (kind:1063 [file metadata](94.md) events), `"a"` (software application event) |
| App curation sets | 30267 | references to multiple software applications | `"a"` (software application event) |
| Calendar | 31924 | a set of events categorized in any way | `"a"` (calendar event event) |
| Starter packs | 39089 | a named set of profiles to be shared around with the goal of being followed together | `"p"` (pubkeys) |
| Media starter packs | 39092 | same as above, but specific to multimedia (photos, short video) clients | `"p"` (pubkeys) |
### Deprecated standard lists
@@ -104,9 +98,9 @@ Some clients have used these lists in the past, but they should work on transiti
"kind": 30004,
"tags": [
["d", "jvdy9i4"],
["title", "Yaks"],
["image", "https://cdn.britannica.com/40/188540-050-9AC748DE/Yak-Himalayas-Nepal.jpg"],
["description", "The domestic yak, also known as the Tartary ox, grunting ox, or hairy cattle, is a species of long-haired domesticated cattle found throughout the Himalayan region of the Indian subcontinent, the Tibetan Plateau, Gilgit-Baltistan, Tajikistan and as far north as Mongolia and Siberia."],
["name", "Yaks"],
["picture", "https://cdn.britannica.com/40/188540-050-9AC748DE/Yak-Himalayas-Nepal.jpg"],
["about", "The domestic yak, also known as the Tartary ox, grunting ox, or hairy cattle, is a species of long-haired domesticated cattle found throughout the Himalayan region of the Indian subcontinent, the Tibetan Plateau, Gilgit-Baltistan, Tajikistan and as far north as Mongolia and Siberia."],
["a", "30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:95ODQzw3ajNoZ8SyMDOzQ"],
["a", "30023:54af95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:1-MYP8dAhramH9J5gJWKx"],
["a", "30023:f8fe95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:D2Tbd38bGrFvU0bIbvSMt"],

129
52.md
View File

@@ -12,14 +12,23 @@ Unlike the term `calendar event` specific to this NIP, the term `event` is used
## Calendar Events
There are two types of calendar events represented by different kinds: _date-based_ and _time-based_ calendar events.
There are two types of calendar events represented by different kinds: date-based and time-based calendar events. Calendar events are not required to be part of a [calendar](#calendar).
These tags are common to both types of calendar events:
### Date-Based Calendar Event
* `d` (required) a short unique string identifier. Generated by the client creating the calendar event.
This kind of calendar event starts on a date and ends before a different date in the future. Its use is appropriate for all-day or multi-day events where time and time zone hold no significance. e.g., anniversary, public holidays, vacation days.
#### Format
The format uses an _addressable event_ of `kind:31922`.
The `.content` of these events should be a detailed description of the calendar event. It is required but can be an empty string.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `title` (required) title of the calendar event
* `summary` (optional) brief description of the calendar event
* `image` (optional) url of an image to use for the event
* `start` (required) inclusive start date in ISO 8601 format (YYYY-MM-DD). Must be less than `end`, if it exists.
* `end` (optional) exclusive end date in ISO 8601 format (YYYY-MM-DD). If omitted, the calendar event ends on the same date as `start`.
* `location` (optional, repeated) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
@@ -27,49 +36,39 @@ These tags are common to both types of calendar events:
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
The following tags are deprecated:
* `name` name of the calendar event. Use only if `title` is not available.
Calendar events are _not_ required to be part of a [calendar](#calendar).
### Date-Based Calendar Event
This kind of calendar event starts on a date and ends before a different date in the future. Its use is appropriate for all-day or multi-day events where time and time zone hold no significance. e.g., anniversary, public holidays, vacation days.
It's an _addressable event_ of `kind:31922`.
The `.content` of these events SHOULD be a description of the calendar event.
Aside from the common tags, this also takes the following tags:
* `start` (required) inclusive start date in ISO 8601 format (YYYY-MM-DD). Must be less than `end`, if it exists.
* `end` (optional) exclusive end date in ISO 8601 format (YYYY-MM-DD). If omitted, the calendar event ends on the same date as `start`.
Example:
```yaml
```jsonc
{
"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>,
"created_at": <Unix timestamp in seconds>,
"kind": 31922,
"content": "<description of calendar event>",
"tags": [
["d", "<random-identifier>"],
["d", "<UUID>"],
["title", "<title of calendar event>"],
// dates
// Dates
["start", "<YYYY-MM-DD>"],
["end", "<YYYY-MM-DD>"],
// location
// Location
["location", "<location>"],
["g", "<geohash>"],
// participants
// Participants
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
// Hashtags
["t", "<tag>"],
["t", "<tag>"],
// Reference links
["r", "<url>"],
["r", "<url>"]
]
}
```
@@ -78,68 +77,97 @@ Example:
This kind of calendar event spans between a start time and end time.
It's an _addressable event_ of `kind:31923`.
#### Format
The `.content` of these events should be a description of the calendar event. It is required but can be an empty string.
The format uses an _addressable event_ kind `31923`.
Aside from the common tags, this also takes the following tags:
The `.content` of these events should be a detailed description of the calendar event. It is required but can be an empty string.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `title` (required) title of the calendar event
* `start` (required) inclusive start Unix timestamp in seconds. Must be less than `end`, if it exists.
* `end` (optional) exclusive end Unix timestamp in seconds. If omitted, the calendar event ends instantaneously.
* `start_tzid` (optional) time zone of the start timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`
* `end_tzid` (optional) time zone of the end timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`. If omitted and `start_tzid` is provided, the time zone of the end timestamp is the same as the start timestamp.
* `summary` (optional) brief description of the calendar event
* `image` (optional) url of an image to use for the event
* `location` (optional, repeated) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
* `l` (optional, repeated) label to categorize calendar event. e.g. `audiospace` to denote a scheduled event from a live audio space implementation such as cornychat.com
* `t` (optional, repeated) hashtag to categorize calendar event
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
```yaml
The following tags are deprecated:
* `name` name of the calendar event. Use only if `title` is not available.
```jsonc
{
"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>,
"created_at": <Unix timestamp in seconds>,
"kind": 31923,
"content": "<description of calendar event>",
"tags": [
["d", "<random-identifier>"],
["d", "<UUID>"],
["title", "<title of calendar event>"],
["summary", "<brief description of the calendar event>"],
["image", "<string with image URI>"],
// timestamps
["start", "<unix timestamp in seconds>"],
["end", "<unix timestamp in seconds>"],
// Timestamps
["start", "<Unix timestamp in seconds>"],
["end", "<Unix timestamp in seconds>"],
["start_tzid", "<IANA Time Zone Database identifier>"],
["end_tzid", "<IANA Time Zone Database identifier>"],
// location
// Location
["location", "<location>"],
["g", "<geohash>"],
// participants
// Participants
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
// Labels (example using com.cornychat namespace denoting the event as an audiospace)
["L", "com.cornychat"],
["l", "audiospace", "com.cornychat"],
// Hashtags
["t", "<tag>"],
["t", "<tag>"],
// Reference links
["r", "<url>"],
["r", "<url>"]
]
}
```
## Calendar
A calendar is a collection of calendar events, represented as a custom _addressable list_ event using kind `31924`. A user can have multiple calendars. One may create a calendar to segment calendar events for specific purposes. e.g., personal, work, travel, meetups, and conferences.
A calendar is a collection of calendar events, represented as a custom replaceable list event using kind `31924`. A user can have multiple calendars. One may create a calendar to segment calendar events for specific purposes. e.g., personal, work, travel, meetups, and conferences.
### Format
The `.content` of these events should be a detailed description of the calendar. It is required but can be an empty string.
The format uses a custom replaceable list of kind `31924` with a list of tags as described below:
* `d` (required) universally unique identifier. Generated by the client creating the calendar.
* `title` (required) calendar title
* `a` (repeated) reference tag to kind `31922` or `31923` calendar event being responded to
```yaml
```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>,
"created_at": <Unix timestamp in seconds>,
"kind": 31924,
"content": "<description of calendar>",
"tags": [
["d", "<random-identifier>"],
["d", "<UUID>"],
["title", "<calendar title>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"]
@@ -163,12 +191,13 @@ The RSVP MUST have an `a` tag of the event coordinates to the calendar event, an
The RSVP MAY tag the author of the calendar event it is in response to using a `p` tag so that clients can easily query all RSVPs that pertain to the author.
The RSVP is an _addressable event_ of `kind:31925`.
### Format
The format uses an _addressable event_ kind `31925`.
The `.content` of these events is optional and should be a free-form note that adds more context to this calendar event response.
The list of tags is as follows:
The list of tags are as follows:
* `a` (required) coordinates to a kind `31922` or `31923` calendar event being responded to.
* `e` (optional) event id of a kind `31922` or `31923` calendar event being responded to.
* `d` (required) universally unique identifier. Generated by the client creating the calendar event RSVP.
@@ -176,17 +205,17 @@ The list of tags is as follows:
* `fb` (optional) `free` or `busy`. Determines if the user would be free or busy for the duration of the calendar event. This tag must be omitted or ignored if the `status` label is set to `declined`.
* `p` (optional) pubkey of the author of the calendar event being responded to.
```yaml
```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>,
"created_at": <Unix timestamp in seconds>,
"kind": 31925,
"content": "<note>",
"tags": [
["e", "<kind 31922 or 31923 event id", "<optional recommended relay URL>"]
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional recommended relay URL>"],
["d", "<random-identifier>"],
["d", "<UUID>"],
["status", "<accepted/declined/tentative>"],
["fb", "<free/busy>"],
["p", "<hex pubkey of kind 31922 or 31923 event>", "<optional recommended relay URL>"]

73
53.md
View File

@@ -84,79 +84,6 @@ Event `kind:1311` is live chat's channel message. Clients MUST include the `a` t
Hosts may choose to pin one or more live chat messages by updating the `pinned` tags in the live event kind `30311`.
### Stream Raids
Event `kind:1312` is a live stream raid (redirect viewers to another stream).
Clients MUST include two `a` tags, one marked as `from` and the other marked as `to` which redirects viewers to a new live stream.
The `content` MAY contain a raid message.
Clients SHOULD automatically redirect viewers from one stream to another.
This is a similar function used on Twitch also called raids.
```jsonc
{
"kind": 1312,
"tags": [
["a", "30311:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>", "from"],
["a", "30311:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>", "to"],
],
"content": "Zap this epic streamer!",
// other fields...
}
```
### Stream Clips
Event `kind:1313` is a live stream clip.
A clip is a small video clip taken from the live stream used to highlight a specific memorable or entertaining part of the stream.
Clients MUST include one `a` tag which points to the live stream event which the clip was taken from.
Clients MUST include one `r` tag which is a URL pointing to the video clip file, preferably in MP4 format for maximum compatibility.
Clients MAY also include a message about the clip in the `title` tag.
```jsonc
{
"kind": 1313,
"tags": [
["a", "30311:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>", "root"],
["r", "https://example.com/my-clip.mp4"],
["title", "Streamer gets rekt by bot!"]
],
"content": "",
// other fields...
}
```
### Stream Timeout
Event `kind:1314` is a live chat moderation event that temporarily mutes a user from chatting.
Clients MUST include one or more `p` tags of the timed out users.
Clients MUST include a [NIP-40](./40.md) expiration timestamp which signals when the timeout will end.
Clients SHOULD enfore timeouts by disabling chat functions in their apps and not displaying any chat messages from the user during their timeout.
Clients SHOULD listen for timeout events published by the stream host.
Clients MAY include a reason for the timeout in the `content` field.
```jsonc
{
"kind": 1314,
"tags": [
["p", "<pubkey-of-muted-user>"],
["expiration", "1747658333"]
],
"content": "Rude",
// other fields...
}
```
## Use Cases
Common use cases include meeting rooms/workshops, watch-together activities, or event spaces, such as [zap.stream](https://zap.stream).

234
90.md
View File

@@ -1,230 +1,44 @@
NIP-90
======
Data Vending Machine
--------------------
Data Vending Machines
---------------------
`draft` `optional`
This NIP defines the interaction between customers and Service Providers for performing on-demand computation.
This NIP is a documentation effort and a hub for incentivizing the proliferation of DVMs on Nostr.
Money in, data out.
A DVM is anything that can be interacted with programmatically, either to benefit Nostr clients in general or making use of the Nostr ecosystem.
## Kinds
This NIP reserves the range `5000-7000` for data vending machine use.
There are 3 types of DVM-related interaction:
| Kind | Description |
| ---- | ----------- |
| 5000-5999 | Job request kinds |
| 6000-6999 | Job result |
| 7000 | Job feedback |
- A "bot", i.e. a Nostr profile, optionally with a `kind:0` metadata, that may or may not publish `kind:1`s with human-readable notes, that can be controlled by a human or by a computer program.
- A "service", i.e., a server exposing a HTTP/JSON API (although other formats aren't out of scope either).
- A "relay", i.e. a server exposing a [NIP-01](01.md) WebSocket endpoint.
Job results always use a kind number that is `1000` higher than the job request kind. (e.g. request: `kind:5001` gets a result: `kind:6001`).
To qualify as a DVM entity a thing must:
Job request types are defined [separately](https://github.com/nostr-protocol/data-vending-machines/tree/master/kinds).
- Accept inputs in some way, such as: by reading some specific event kind from one or more relays; by accept HTTP requests to its own HTTP API; or by receiving `EVENT` or `REQ` messages in its own WebSocket endpoint.
- Produce outputs in some way, such as: by publishing some kind of event to one or more relays; by returning an HTTP response; or by returning `EVENT`s in its WebSocket connection.
## Rationale
Nostr can act as a marketplace for data processing, where users request jobs to be processed in certain ways (e.g., "speech-to-text", "summarization", etc.), but they don't necessarily care about "who" processes the data.
It's important to notice that such definition of DVM encompasses basically everything in the world -- including Nostr relays, Nostr clients and Nostr users (yes, we are all DVMs), but this NIP limits itself to describing standards that emerge naturally for interaction flows that:
This NIP is not to be confused with a 1:1 marketplace; instead, it describes a flow where a user announces a desired output, willingness to pay, and service providers compete to fulfill the job requirement in the best way possible.
- Will benefit from standardization such that more than one DVM can exist fulfilling the same role and more than one client or consumer can exist that interacts with such DVM directly;
- Aren't defined anywhere else and that would be difficult to define in other NIPs.
### Actors
There are two actors in the workflow described in this NIP:
* Customers (npubs who request a job)
* Service providers (npubs who fulfill jobs)
### DVMs
## Job request (`kind:5000-5999`)
A request to process data, published by a customer. This event signals that a customer is interested in receiving the result of some kind of compute.
#### Translation
```jsonc
{
"kind": 5xxx, // kind in 5000-5999 range
"content": "",
"tags": [
[ "i", "<data>", "<input-type>", "<relay>", "<marker>" ],
[ "output", "<mime-type>" ],
[ "relays", "wss://..." ],
[ "bid", "<msat-amount>" ],
[ "t", "bitcoin" ]
],
// other fields...
}
```
- TODO: nostr.wine HTTP translation
- TODO: event-based translation?
All tags are optional.
#### Trending feeds
* `i` tag: Input data for the job (zero or more inputs)
* `<data>`: The argument for the input
* `<input-type>`: The way this argument should be interpreted. MUST be one of:
* `url`: A URL to be fetched of the data that should be processed.
* `event`: A Nostr event ID.
* `job`: The output of a previous job with the specified event ID. The dermination of which output to build upon is up to the service provider to decide (e.g. waiting for a signaling from the customer, waiting for a payment, etc.)
* `text`: `<data>` is the value of the input, no resolution is needed
* `<relay>`: If `event` or `job` input-type, the relay where the event/job was published, otherwise optional or empty string
* `<marker>`: An optional field indicating how this input should be used within the context of the job
* `output`: Expected output format. Different job request `kind` defines this more precisely.
* `param`: Optional parameters for the job as key (first argument)/value (second argument). Different job request `kind` defines this more precisely. (e.g. `[ "param", "lang", "es" ]`)
* `bid`: Customer MAY specify a maximum amount (in millisats) they are willing to pay
* `relays`: List of relays where Service Providers SHOULD publish responses to
* `p`: Service Providers the customer is interested in. Other SPs MIGHT still choose to process the job
- TODO: primal HTTP trending feeds
- TODO: nostr.band HTTP trending feeds
- TODO: DVM-based trending feeds
## Encrypted Params
#### Whatever
If the user wants to keep the input parameters a secret, they can encrypt the `i` and `param` tags with the service provider's 'p' tag and add it to the content field. Add a tag `encrypted` as tags. Encryption for private tags will use [NIP-04 - Encrypted Direct Message encryption](04.md), using the user's private and service provider's public key for the shared secret
```json
[
["i", "what is the capital of France? ", "text"],
["param", "model", "LLaMA-2"],
["param", "max_tokens", "512"],
["param", "temperature", "0.5"],
["param", "top-k", "50"],
["param", "top-p", "0.7"],
["param", "frequency_penalty", "1"]
]
```
This param data will be encrypted and added to the `content` field and `p` tag should be present
```jsonc
{
"content": "BE2Y4xvS6HIY7TozIgbEl3sAHkdZoXyLRRkZv4fLPh3R7LtviLKAJM5qpkC7D6VtMbgIt4iNcMpLtpo...",
"tags": [
["p", "04f74530a6ede6b24731b976b8e78fb449ea61f40ff10e3d869a3030c4edc91f"],
["encrypted"]
],
// other fields...
}
```
## Job result (`kind:6000-6999`)
Service providers publish job results, providing the output of the job result. They should tag the original job request event id as well as the customer's pubkey.
```jsonc
{
"pubkey": "<service-provider pubkey>",
"content": "<payload>",
"kind": 6xxx,
"tags": [
["request", "<job-request>"],
["e", "<job-request-id>", "<relay-hint>"],
["i", "<input-data>"],
["p", "<customer's-pubkey>"],
["amount", "requested-payment-amount", "<optional-bolt11>"]
],
// other fields...
}
```
* `request`: The job request event stringified-JSON.
* `amount`: millisats that the Service Provider is requesting to be paid. An optional third value can be a bolt11 invoice.
* `i`: The original input(s) specified in the request.
## Encrypted Output
If the request has encrypted params, then output should be encrypted and placed in `content` field. If the output is encrypted, then avoid including `i` tag with input-data as clear text.
Add a tag encrypted to mark the output content as `encrypted`
```jsonc
{
"pubkey": "<service-provider pubkey>",
"content": "<encrypted payload>",
"kind": 6xxx,
"tags": [
["request", "<job-request>"],
["e", "<job-request-id>", "<relay-hint>"],
["p", "<customer's-pubkey>"],
["amount", "requested-payment-amount", "<optional-bolt11>"],
["encrypted"]
],
// other fields...
}
```
## Job feedback
Service providers can give feedback about a job back to the customer.
```jsonc
{
"kind": 7000,
"content": "<empty-or-payload>",
"tags": [
["status", "<status>", "<extra-info>"],
["amount", "requested-payment-amount", "<bolt11>"],
["e", "<job-request-id>", "<relay-hint>"],
["p", "<customer's-pubkey>"],
],
// other fields...
}
```
* `content`: Either empty or a job-result (e.g. for partial-result samples)
* `amount` tag: as defined in the [Job Result](#job-result-kind6000-6999) section.
* `status` tag: Service Providers SHOULD indicate what this feedback status refers to. [Job Feedback Status](#job-feedback-status) defines status. Extra human-readable information can be added as an extra argument.
* NOTE: If the input params requires input to be encrypted, then `content` field will have encrypted payload with `p` tag as key.
### Job feedback status
| status | description |
| -------- | ------------- |
| `payment-required` | Service Provider requires payment before continuing. |
| `processing` | Service Provider is processing the job. |
| `error` | Service Provider was unable to process the job. |
| `success` | Service Provider successfully processed the job. |
| `partial` | Service Provider partially processed the job. The `.content` might include a sample of the partial results. |
Any job feedback event MIGHT include results in the `.content` field, as described in the [Job Result](#job-result-kind6000-6999) section. This is useful for service providers to provide a sample of the results that have been processed so far.
# Protocol Flow
* Customer publishes a job request (e.g. `kind:5000` speech-to-text).
* Service Providers MAY submit `kind:7000` job-feedback events (e.g. `payment-required`, `processing`, `error`, etc.).
* Upon completion, the service provider publishes the result of the job with a `kind:6000` job-result event.
* At any point, if there is an `amount` pending to be paid as instructed by the service provider, the user can pay the included `bolt11` or zap the job result event the service provider has sent to the user.
Job feedback (`kind:7000`) and Job Results (`kind:6000-6999`) events MAY include an `amount` tag, this can be interpreted as a suggestion to pay. Service Providers MUST use the `payment-required` feedback event to signal that a payment is required and no further actions will be performed until the payment is sent.
Customers can always either pay the included `bolt11` invoice or zap the event requesting the payment and service providers should monitor for both if they choose to include a bolt11 invoice.
## Notes about the protocol flow
The flow is deliberately ambiguous, allowing vast flexibility for the interaction between customers and service providers so that service providers can model their behavior based on their own decisions/perceptions of risk.
Some service providers might choose to submit a `payment-required` as the first reaction before sending a `processing` or before delivering results, some might choose to serve partial results for the job (e.g. a sample), send a `payment-required` to deliver the rest of the results, and some service providers might choose to assess likelihood of payment based on an npub's past behavior and thus serve the job results before requesting payment for the best possible UX.
It's not up to this NIP to define how individual vending machines should choose to run their business.
# Cancellation
A job request might be canceled by publishing a `kind:5` delete request event tagging the job request event.
# Appendix 1: Job chaining
A Customer MAY request multiple jobs to be processed as a chain, where the output of a job is the input of another job. (e.g. podcast transcription -> summarization of the transcription). This is done by specifying as input an event id of a different job with the `job` type.
Service Providers MAY begin processing a subsequent job the moment they see the prior job's result, but they will likely wait for a zap to be published first. This introduces a risk that Service Provider of job #1 might delay publishing the zap event in order to have an advantage. This risk is up to Service Providers to mitigate or to decide whether the service provider of job #1 tends to have good-enough results so as to not wait for an explicit zap to assume the job was accepted.
This gives a higher level of flexibility to service providers (which sophisticated service providers would take anyway).
# Appendix 2: Service provider discoverability
Service Providers MAY use NIP-89 announcements to advertise their support for job kinds:
```jsonc
{
"kind": 31990,
"pubkey": "<pubkey>",
"content": "{
\"name\": \"Translating DVM\",
\"about\": \"I'm a DVM specialized in translating Bitcoin content.\"
}",
"tags": [
["k", "5005"], // e.g. translation
["t", "bitcoin"] // e.g. optionally advertises it specializes in bitcoin audio transcription that won't confuse "Drivechains" with "Ridechains"
],
// other fields...
}
```
Customers can use NIP-89 to see what service providers their follows use.
- TODO:

View File

@@ -150,10 +150,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `1063` | File Metadata | [94](94.md) |
| `1068` | Poll | [88](88.md) |
| `1111` | Comment | [22](22.md) |
| `1311` | Live Stream Chat Message | [53](53.md) |
| `1312` | Live Stream Raid | [53](53.md) |
| `1313` | Live Stream Clip | [53](53.md) |
| `1314` | Live Stream Timeout | [53](53.md) |
| `1311` | Live Chat Message | [53](53.md) |
| `1337` | Code Snippet | [C0](C0.md) |
| `1617` | Patches | [34](34.md) |
| `1621` | Issues | [34](34.md) |
@@ -168,9 +165,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `2004` | Torrent Comment | [35](35.md) |
| `2022` | Coinjoin Pool | [joinstr][joinstr] |
| `4550` | Community Post Approval | [72](72.md) |
| `5000`-`5999` | Job Request | [90](90.md) |
| `6000`-`6999` | Job Result | [90](90.md) |
| `7000` | Job Feedback | [90](90.md) |
| `7374` | Reserved Cashu Wallet Tokens | [60](60.md) |
| `7375` | Cashu Wallet Tokens | [60](60.md) |
| `7376` | Cashu Wallet History | [60](60.md) |
@@ -190,11 +184,9 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `10006` | Blocked relays list | [51](51.md) |
| `10007` | Search relays list | [51](51.md) |
| `10009` | User groups | [51](51.md), [29](29.md) |
| `10012` | Favorite relays list | [51](51.md) |
| `10013` | Private event relay list | [37](37.md) |
| `10015` | Interests list | [51](51.md) |
| `10019` | Nutzap Mint Recommendation | [61](61.md) |
| `10020` | Media follows | [51](51.md) |
| `10030` | User emoji list | [51](51.md) |
| `10050` | Relay list to receive DMs | [51](51.md), [17](17.md) |
| `10063` | User server list | [Blossom][blossom] |
@@ -254,8 +246,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `34550` | Community Definition | [72](72.md) |
| `38383` | Peer-to-peer Order events | [69](69.md) |
| `39000-9` | Group metadata events | [29](29.md) |
| `39089` | Starter packs | [51](51.md) |
| `39092` | Media starter packs | [51](51.md) |
| `39701` | Web bookmarks | [B0](B0.md) |
[NUD: Custom Feeds]: https://wikifreedia.xyz/cip-01/
@@ -336,7 +326,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `dep` | Required dependency | -- | [C0](C0.md) |
| `description` | description | -- | [34](34.md), [57](57.md), [58](58.md), [C0](C0.md) |
| `emoji` | shortcode, image URL | -- | [30](30.md) |
| `encrypted` | -- | -- | [90](90.md) |
| `extension` | File extension | -- | [C0](C0.md) |
| `expiration` | unix timestamp (string) | -- | [40](40.md) |
| `file` | full path (string) | -- | [35](35.md) |